save the work about getting rid of pointers.
This commit is contained in:
parent
5186fec028
commit
3f2f16a79e
14 changed files with 348 additions and 413 deletions
13
action.go
13
action.go
|
@ -1,13 +0,0 @@
|
||||||
package tg
|
|
||||||
|
|
||||||
import (
|
|
||||||
//"reflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
// The argument for handling in channenl behaviours.
|
|
||||||
type ChannelContext struct {
|
|
||||||
}
|
|
||||||
type CC = ChannelContext
|
|
||||||
type ChannelAction struct {
|
|
||||||
Act (*ChannelContext)
|
|
||||||
}
|
|
65
bot.go
65
bot.go
|
@ -13,16 +13,15 @@ type User = tgbotapi.User
|
||||||
// The wrapper around Telegram API.
|
// The wrapper around Telegram API.
|
||||||
type Bot struct {
|
type Bot struct {
|
||||||
// Custom data value.
|
// Custom data value.
|
||||||
Data any
|
data any
|
||||||
Api *tgbotapi.BotAPI
|
api *tgbotapi.BotAPI
|
||||||
Me *User
|
me User
|
||||||
// Private bot behaviour.
|
// Private bot behaviour.
|
||||||
behaviour *Behaviour
|
behaviour *Behaviour
|
||||||
// Group bot behaviour.
|
// Group bot behaviour.
|
||||||
//groupBehaviour *GroupBehaviour
|
//groupBehaviour *GroupBehaviour
|
||||||
// Bot behaviour in channels.
|
// Bot behaviour in channels.
|
||||||
//channelBehaviour *ChannelBehaviour
|
//channelBehaviour *ChannelBehaviour
|
||||||
contexts map[SessionId] *context
|
|
||||||
sessions SessionMap
|
sessions SessionMap
|
||||||
//groupSessions GroupSessionMap
|
//groupSessions GroupSessionMap
|
||||||
}
|
}
|
||||||
|
@ -35,40 +34,49 @@ func NewBot(token string) (*Bot, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Bot{
|
return &Bot{
|
||||||
Api: bot,
|
api: bot,
|
||||||
contexts: make(map[SessionId] *context),
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bot *Bot) Debug(debug bool) *Bot {
|
func (bot *Bot) Debug(debug bool) *Bot {
|
||||||
bot.Api.Debug = debug
|
bot.api.Debug = debug
|
||||||
return bot
|
return bot
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (bot *Bot) Api() *tgbotapi.BotAPI {
|
||||||
|
return bot.api
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bot *Bot) Me() User {
|
||||||
|
return bot.me
|
||||||
|
}
|
||||||
|
|
||||||
// Send the Renderable to the specified session client side.
|
// Send the Renderable to the specified session client side.
|
||||||
// Can be used for both group and private sessions because
|
// 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(
|
func (bot *Bot) Send(
|
||||||
sid SessionId, v Sendable,
|
sid SessionId, v Sendable,
|
||||||
) (*Message, error) {
|
) (Message, error) {
|
||||||
config := v.SendConfig(sid, bot)
|
config := v.SendConfig(sid, bot)
|
||||||
if config.Error != nil {
|
if config.Error != nil {
|
||||||
return nil, config.Error
|
return Message{}, config.Error
|
||||||
}
|
}
|
||||||
|
|
||||||
msg, err := bot.Api.Send(config.ToApi())
|
msg, err := bot.api.Send(config.ToApi())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Message{}, err
|
||||||
}
|
}
|
||||||
return &msg, nil
|
v.SetMessage(msg)
|
||||||
|
return msg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bot *Bot) Sendf(
|
func (bot *Bot) Sendf(
|
||||||
sid SessionId, format string, v ...any,
|
sid SessionId, format string, v ...any,
|
||||||
) (*Message, error){
|
) (Message, error){
|
||||||
|
msg := Messagef(format, v...)
|
||||||
return bot.Send(
|
return bot.Send(
|
||||||
sid,
|
sid,
|
||||||
NewMessage(format, v...),
|
&msg,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +84,7 @@ func (bot *Bot) Sendf(
|
||||||
func (bot *Bot) SendRaw(
|
func (bot *Bot) SendRaw(
|
||||||
sid SessionId, v tgbotapi.Chattable,
|
sid SessionId, v tgbotapi.Chattable,
|
||||||
) (*Message, error) {
|
) (*Message, error) {
|
||||||
msg, err := bot.Api.Send(v)
|
msg, err := bot.api.Send(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -117,7 +125,7 @@ func (b *Bot) WithGroupSessions(sessions GroupSessionMap) *Bot {
|
||||||
func (bot *Bot) DeleteCommands() {
|
func (bot *Bot) DeleteCommands() {
|
||||||
//tgbotapi.NewBotCommandScopeAllPrivateChats(),
|
//tgbotapi.NewBotCommandScopeAllPrivateChats(),
|
||||||
cfg := tgbotapi.NewDeleteMyCommands()
|
cfg := tgbotapi.NewDeleteMyCommands()
|
||||||
bot.Api.Request(cfg)
|
bot.api.Request(cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setting the command on the user side.
|
// Setting the command on the user side.
|
||||||
|
@ -151,7 +159,7 @@ func (bot *Bot) SetCommands(
|
||||||
botCmds...,
|
botCmds...,
|
||||||
)
|
)
|
||||||
|
|
||||||
_, err := bot.Api.Request(cfg)
|
_, err := bot.api.Request(cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -170,11 +178,11 @@ func (bot *Bot) Run() error {
|
||||||
|
|
||||||
uc := tgbotapi.NewUpdate(0)
|
uc := tgbotapi.NewUpdate(0)
|
||||||
uc.Timeout = 10
|
uc.Timeout = 10
|
||||||
updates := bot.Api.GetUpdatesChan(uc)
|
updates := bot.api.GetUpdatesChan(uc)
|
||||||
handles := make(map[string] chan *Update)
|
handles := make(map[string] chan Update)
|
||||||
|
|
||||||
if bot.behaviour != nil {
|
if bot.behaviour != nil {
|
||||||
chn := make(chan *Update)
|
chn := make(chan Update)
|
||||||
handles["private"] = chn
|
handles["private"] = chn
|
||||||
go bot.handlePrivate(chn)
|
go bot.handlePrivate(chn)
|
||||||
}
|
}
|
||||||
|
@ -195,10 +203,10 @@ func (bot *Bot) Run() error {
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
me, _ := bot.Api.GetMe()
|
me, _ := bot.Api.GetMe()
|
||||||
bot.Me = &me
|
bot.me = me
|
||||||
for up := range updates {
|
for up := range updates {
|
||||||
u := &Update{
|
u := Update{
|
||||||
Update: &up,
|
Update: up,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sometimes returns nil.
|
// Sometimes returns nil.
|
||||||
|
@ -220,7 +228,7 @@ func (bot *Bot) Run() error {
|
||||||
|
|
||||||
// The function handles updates supposed for the private
|
// The function handles updates supposed for the private
|
||||||
// chat with the bot.
|
// chat with the bot.
|
||||||
func (bot *Bot) handlePrivate(updates chan *Update) {
|
func (bot *Bot) handlePrivate(updates chan Update) {
|
||||||
var sid SessionId
|
var sid SessionId
|
||||||
for u := range updates {
|
for u := range updates {
|
||||||
sid = SessionId(u.FromChat().ID)
|
sid = SessionId(u.FromChat().ID)
|
||||||
|
@ -245,12 +253,13 @@ func (bot *Bot) handlePrivate(updates chan *Update) {
|
||||||
bot.contexts[sid] = ctx
|
bot.contexts[sid] = ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
go (&Context{
|
go Context{
|
||||||
context: ctx,
|
session: session,
|
||||||
|
bot: bot,
|
||||||
Update: u,
|
Update: u,
|
||||||
input: ctx.updates,
|
input: ctx.updates,
|
||||||
}).serve()
|
}.serve()
|
||||||
ctx.updates.Send(u)
|
ctx.session.updates.Send(u)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
26
button.go
26
button.go
|
@ -16,7 +16,7 @@ type Button struct {
|
||||||
Action Action
|
Action Action
|
||||||
}
|
}
|
||||||
|
|
||||||
type ButtonMap map[string]*Button
|
type ButtonMap map[string]Button
|
||||||
|
|
||||||
// Returns the only location button in the map.
|
// Returns the only location button in the map.
|
||||||
func (btnMap ButtonMap) LocationButton() *Button {
|
func (btnMap ButtonMap) LocationButton() *Button {
|
||||||
|
@ -32,14 +32,14 @@ func (btnMap ButtonMap) LocationButton() *Button {
|
||||||
type ButtonRow []*Button
|
type ButtonRow []*Button
|
||||||
|
|
||||||
// Returns new button with the specified text and no action.
|
// Returns new button with the specified text and no action.
|
||||||
func NewButton(format string, v ...any) *Button {
|
func Buttonf(format string, v ...any) Button {
|
||||||
return &Button{
|
return &Button{
|
||||||
Text: fmt.Sprintf(format, v...),
|
Text: fmt.Sprintf(format, v...),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Randomize buttons data to make the key unique.
|
// Randomize buttons data to make the key unique.
|
||||||
func (btn *Button) Rand() *Button {
|
func (btn Button) Rand() Button {
|
||||||
rData := make([]byte, 8)
|
rData := make([]byte, 8)
|
||||||
rand.Read(rData)
|
rand.Read(rData)
|
||||||
data := make([]byte, base64.StdEncoding.EncodedLen(len(rData)))
|
data := make([]byte, base64.StdEncoding.EncodedLen(len(rData)))
|
||||||
|
@ -49,41 +49,37 @@ func (btn *Button) Rand() *Button {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the URL for the button. Only for inline buttons.
|
// Set the URL for the button. Only for inline buttons.
|
||||||
func (btn *Button) WithUrl(format string, v ...any) *Button {
|
func (btn Button) WithUrl(format string, v ...any) Button {
|
||||||
btn.Url = fmt.Sprintf(format, v...)
|
btn.Url = fmt.Sprintf(format, v...)
|
||||||
return btn
|
return btn
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the action when pressing the button.
|
// Set the action when pressing the button.
|
||||||
// By default is nil and does nothing.
|
// By default is nil and does nothing.
|
||||||
func (btn *Button) WithAction(a Action) *Button {
|
func (btn Button) WithAction(a Action) Button {
|
||||||
btn.Action = a
|
btn.Action = a
|
||||||
return btn
|
return btn
|
||||||
}
|
}
|
||||||
|
|
||||||
func (btn *Button) WithData(dat string) *Button {
|
func (btn Button) WithData(dat string) Button {
|
||||||
btn.Data = dat
|
btn.Data = dat
|
||||||
return btn
|
return btn
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets whether the button must send owner's location.
|
// Sets whether the button must send owner's location.
|
||||||
func (btn *Button) WithSendLocation(ok bool) *Button {
|
func (btn Button) WithSendLocation(ok bool) Button {
|
||||||
btn.SendLocation = ok
|
btn.SendLocation = ok
|
||||||
return btn
|
return btn
|
||||||
}
|
}
|
||||||
|
|
||||||
func (btn *Button) ActionFunc(fn ActionFunc) *Button {
|
func (btn Button) Go(pth Path, args ...any) Button {
|
||||||
return btn.WithAction(fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (btn *Button) Go(pth Path, args ...any) *Button {
|
|
||||||
return btn.WithAction(ScreenGo{
|
return btn.WithAction(ScreenGo{
|
||||||
Path: pth,
|
Path: pth,
|
||||||
Args: args,
|
Args: args,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (btn *Button) ToTelegram() apix.KeyboardButton {
|
func (btn Button) ToTelegram() apix.KeyboardButton {
|
||||||
ret := apix.NewKeyboardButton(btn.Text)
|
ret := apix.NewKeyboardButton(btn.Text)
|
||||||
if btn.SendLocation {
|
if btn.SendLocation {
|
||||||
ret.RequestLocation = true
|
ret.RequestLocation = true
|
||||||
|
@ -91,7 +87,7 @@ func (btn *Button) ToTelegram() apix.KeyboardButton {
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func (btn *Button) ToTelegramInline() apix.InlineKeyboardButton {
|
func (btn Button) ToTelegramInline() apix.InlineKeyboardButton {
|
||||||
if btn.Data != "" {
|
if btn.Data != "" {
|
||||||
return apix.NewInlineKeyboardButtonData(btn.Text, btn.Data)
|
return apix.NewInlineKeyboardButtonData(btn.Text, btn.Data)
|
||||||
}
|
}
|
||||||
|
@ -105,7 +101,7 @@ func (btn *Button) ToTelegramInline() apix.InlineKeyboardButton {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the key of the button to identify it by messages and callbacks.
|
// Return the key of the button to identify it by messages and callbacks.
|
||||||
func (btn *Button) Key() string {
|
func (btn Button) Key() string {
|
||||||
if btn == nil {
|
if btn == nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
44
command.go
44
command.go
|
@ -1,8 +1,6 @@
|
||||||
package tg
|
package tg
|
||||||
|
|
||||||
import (
|
import (
|
||||||
//"flag"
|
|
||||||
|
|
||||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -14,17 +12,17 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type CommandName string
|
type CommandName string
|
||||||
|
|
||||||
type Command struct {
|
type Command struct {
|
||||||
Name CommandName
|
Name CommandName
|
||||||
Type CommandType
|
Type CommandType
|
||||||
Description string
|
Description string
|
||||||
Action Action
|
Action Action
|
||||||
Widget Widget
|
Widget Widget
|
||||||
}
|
}
|
||||||
|
|
||||||
type CommandMap map[CommandName]*Command
|
type CommandMap map[CommandName]*Command
|
||||||
|
|
||||||
func NewCommand(name CommandName, desc string) *Command {
|
func NewCommand(name CommandName, desc string) Command {
|
||||||
if name == "" || desc == "" {
|
if name == "" || desc == "" {
|
||||||
panic("name and description cannot be an empty string")
|
panic("name and description cannot be an empty string")
|
||||||
}
|
}
|
||||||
|
@ -39,27 +37,23 @@ func (c *Command) WithAction(a Action) *Command {
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Command) ActionFunc(af ActionFunc) *Command {
|
func (c Command) WithWidget(w Widget) Command {
|
||||||
return c.WithAction(af)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Command) WithWidget(w Widget) *Command {
|
|
||||||
c.Widget = w
|
c.Widget = w
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Command) WidgetFunc(fn Func) *Command {
|
func (c Command) WidgetFunc(fn Func) Command {
|
||||||
return c.WithWidget(fn)
|
return c.WithWidget(fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Command) ToApi() tgbotapi.BotCommand {
|
func (c Command) ToApi() tgbotapi.BotCommand {
|
||||||
ret := tgbotapi.BotCommand{}
|
ret := tgbotapi.BotCommand{}
|
||||||
ret.Command = string(c.Name)
|
ret.Command = string(c.Name)
|
||||||
ret.Description = c.Description
|
ret.Description = c.Description
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Command) Go(pth Path, args ...any) *Command {
|
func (c Command) Go(pth Path, args ...any) Command {
|
||||||
return c.WithAction(ScreenGo{
|
return c.WithAction(ScreenGo{
|
||||||
Path: pth,
|
Path: pth,
|
||||||
Args: args,
|
Args: args,
|
||||||
|
@ -76,13 +70,12 @@ type CommandCompo struct {
|
||||||
|
|
||||||
// Returns new empty CommandCompo.
|
// Returns new empty CommandCompo.
|
||||||
func NewCommandCompo(cmds ...*Command) *CommandCompo {
|
func NewCommandCompo(cmds ...*Command) *CommandCompo {
|
||||||
ret := (&CommandCompo{}).WithCommands(cmds...)
|
ret := CommandCompo{}.WithCommands(cmds...)
|
||||||
//ret.Commands = make(CommandMap)
|
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the commands to handle.
|
// Set the commands to handle.
|
||||||
func (w *CommandCompo) WithCommands(cmds ...*Command) *CommandCompo {
|
func (w CommandCompo) WithCommands(cmds ...*Command) *CommandCompo {
|
||||||
if w.Commands == nil {
|
if w.Commands == nil {
|
||||||
w.Commands = make(CommandMap)
|
w.Commands = make(CommandMap)
|
||||||
}
|
}
|
||||||
|
@ -105,23 +98,18 @@ func (w *CommandCompo) WithPreStart(a Action) *CommandCompo {
|
||||||
return w
|
return w
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the prestart action with function.
|
|
||||||
func (w *CommandCompo) WithPreStartFunc(fn ActionFunc) *CommandCompo {
|
|
||||||
return w.WithPreStart(fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the usage action.
|
// Set the usage action.
|
||||||
func (w *CommandCompo) WithUsage(a Action) *CommandCompo {
|
func (w CommandCompo) WithUsage(a Action) *CommandCompo {
|
||||||
w.Usage = a
|
w.Usage = a
|
||||||
return w
|
return w
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the usage action with function.
|
// Set the usage action with function.
|
||||||
func (w *CommandCompo) WithUsageFunc(fn ActionFunc) *CommandCompo {
|
func (w CommandCompo) WithUsageFunc(fn ActionFunc) *CommandCompo {
|
||||||
return w.WithUsage(fn)
|
return w.WithUsage(fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (widget *CommandCompo) Filter(
|
func (widget CommandCompo) Filter(
|
||||||
u *Update,
|
u *Update,
|
||||||
) bool {
|
) bool {
|
||||||
if u.Message == nil || !u.Message.IsCommand() {
|
if u.Message == nil || !u.Message.IsCommand() {
|
||||||
|
@ -132,13 +120,13 @@ func (widget *CommandCompo) Filter(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implementing server.
|
// Implementing server.
|
||||||
func (compo *CommandCompo) Serve(c *Context) {
|
func (compo CommandCompo) Serve(c Context) {
|
||||||
/*commanders := make(map[CommandName] BotCommander)
|
/*commanders := make(map[CommandName] BotCommander)
|
||||||
for k, v := range compo.Commands {
|
for k, v := range compo.Commands {
|
||||||
commanders[k] = v
|
commanders[k] = v
|
||||||
}*/
|
}*/
|
||||||
c.Bot.DeleteCommands()
|
c.bot.DeleteCommands()
|
||||||
err := c.Bot.SetCommands(
|
err := c.bot.SetCommands(
|
||||||
tgbotapi.NewBotCommandScopeChat(c.Session.Id.ToApi()),
|
tgbotapi.NewBotCommandScopeChat(c.Session.Id.ToApi()),
|
||||||
compo.Commands,
|
compo.Commands,
|
||||||
)
|
)
|
||||||
|
|
266
context.go
266
context.go
|
@ -8,57 +8,17 @@ import (
|
||||||
//"path"
|
//"path"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Go(pth Path) UI {
|
|
||||||
return UI{
|
|
||||||
GoWidget(pth),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type GoWidget string
|
|
||||||
// Implementing the Server interface.
|
|
||||||
func (widget GoWidget) Serve(c *Context) {
|
|
||||||
c.input.Close()
|
|
||||||
c.Go(Path(widget))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (widget GoWidget) Render(c *Context) UI {
|
|
||||||
return UI{widget}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (widget GoWidget) Filter(u *Update) bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// General context for a specific user.
|
|
||||||
// Is always the same and is not reached
|
|
||||||
// inside end function-handlers.
|
|
||||||
type context struct {
|
|
||||||
Session *Session
|
|
||||||
// To reach the bot abilities inside callbacks.
|
|
||||||
Bot *Bot
|
|
||||||
// Costum status for currently running context.
|
|
||||||
Status any
|
|
||||||
Type ContextType
|
|
||||||
updates *UpdateChan
|
|
||||||
skippedUpdates *UpdateChan
|
|
||||||
// Current screen ID.
|
|
||||||
pathHistory []Path
|
|
||||||
//path, prevPath Path
|
|
||||||
}
|
|
||||||
|
|
||||||
type Contexter interface {
|
|
||||||
GetContext() *Context
|
|
||||||
}
|
|
||||||
|
|
||||||
// Interface to interact with the user.
|
// Interface to interact with the user.
|
||||||
type Context struct {
|
type Context struct {
|
||||||
*context
|
session *Session
|
||||||
// The update that called the Context usage.
|
// The update that called the Context usage.
|
||||||
*Update
|
update Update
|
||||||
// Used as way to provide outer values redirection
|
// Used as way to provide outer values redirection
|
||||||
// into widgets and actions. It is like arguments
|
// into widgets and actions. It is like arguments
|
||||||
// for REST API request etc.
|
// for REST API request etc.
|
||||||
arg any
|
arg any
|
||||||
|
typ ContextType
|
||||||
// Instead of updates as argument.
|
// Instead of updates as argument.
|
||||||
input *UpdateChan
|
input *UpdateChan
|
||||||
}
|
}
|
||||||
|
@ -68,7 +28,7 @@ type Context struct {
|
||||||
// maybe you will find another usage for this.
|
// maybe you will find another usage for this.
|
||||||
// Returns users context by specified session ID
|
// Returns users context by specified session ID
|
||||||
// or nil if the user is not logged in.
|
// or nil if the user is not logged in.
|
||||||
func (c *Context) As(sid SessionId) *Context {
|
func (c Context) As(sid SessionId) Context {
|
||||||
n, ok := c.Bot.contexts[sid]
|
n, ok := c.Bot.contexts[sid]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
|
@ -78,17 +38,13 @@ func (c *Context) As(sid SessionId) *Context {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) GetContext() *Context {
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// General type function to define actions, single component widgets
|
// General type function to define actions, single component widgets
|
||||||
// and components themselves.
|
// and components themselves.
|
||||||
type Func func(*Context)
|
type Func func(Context)
|
||||||
func (f Func) Act(c *Context) {
|
func (f Func) Act(c Context) {
|
||||||
f(c)
|
f(c)
|
||||||
}
|
}
|
||||||
func (f Func) Serve(c *Context) {
|
func (f Func) Serve(c Context) {
|
||||||
f(c)
|
f(c)
|
||||||
}
|
}
|
||||||
func(f Func) Filter(_ *Update) bool {
|
func(f Func) Filter(_ *Update) bool {
|
||||||
|
@ -108,13 +64,13 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Goroutie function to handle each user.
|
// Goroutie function to handle each user.
|
||||||
func (c *Context) serve() {
|
func (c Context) serve() {
|
||||||
beh := c.Bot.behaviour
|
beh := c.Bot.behaviour
|
||||||
c.Run(beh.Init)
|
c.Run(beh.Init)
|
||||||
beh.Root.Serve(c)
|
beh.Root.Serve(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) Path() Path {
|
func (c Context) Path() Path {
|
||||||
ln := len(c.pathHistory)
|
ln := len(c.pathHistory)
|
||||||
if ln == 0 {
|
if ln == 0 {
|
||||||
return ""
|
return ""
|
||||||
|
@ -122,11 +78,11 @@ func (c *Context) Path() Path {
|
||||||
return c.pathHistory[ln-1]
|
return c.pathHistory[ln-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) Arg() any {
|
func (c Context) Arg() any {
|
||||||
return c.arg
|
return c.arg
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) Run(a Action) {
|
func (c Context) Run(a Action) {
|
||||||
if a != nil {
|
if a != nil {
|
||||||
a.Act(c)
|
a.Act(c)
|
||||||
}
|
}
|
||||||
|
@ -135,12 +91,12 @@ func (c *Context) Run(a Action) {
|
||||||
// Only for the root widget usage.
|
// Only for the root widget usage.
|
||||||
// Skip the update sending it down to
|
// Skip the update sending it down to
|
||||||
// the underlying widget.
|
// the underlying widget.
|
||||||
func (c *Context) Skip(u *Update) {
|
func (c Context) Skip(u Update) {
|
||||||
c.skippedUpdates.Send(u)
|
c.skippedUpdates.Send(u)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sends to the Sendable object.
|
// Sends to the Sendable object.
|
||||||
func (c *Context) Send(v Sendable) (*Message, error) {
|
func (c Context) Send(v Sendable) (Message, error) {
|
||||||
config := v.SendConfig(c.Session.Id, c.Bot)
|
config := v.SendConfig(c.Session.Id, c.Bot)
|
||||||
if config.Error != nil {
|
if config.Error != nil {
|
||||||
return nil, config.Error
|
return nil, config.Error
|
||||||
|
@ -155,219 +111,79 @@ func (c *Context) Send(v Sendable) (*Message, error) {
|
||||||
|
|
||||||
// Sends the formatted with fmt.Sprintf message to the user
|
// Sends the formatted with fmt.Sprintf message to the user
|
||||||
// using default Markdown parsing format.
|
// using default Markdown parsing format.
|
||||||
func (c *Context) Sendf(format string, v ...any) (*Message, error) {
|
func (c Context) Sendf(format string, v ...any) (Message, error) {
|
||||||
return c.Send(NewMessage(format, v...))
|
return c.Send(NewMessage(format, v...))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Same as Sendf but uses Markdown 2 format for parsing.
|
// Same as Sendf but uses Markdown 2 format for parsing.
|
||||||
func (c *Context) Sendf2(format string, v ...any) (*Message, error) {
|
func (c Context) Sendf2(format string, v ...any) (Message, error) {
|
||||||
return c.Send(NewMessage(fmt.Sprintf(format, v...)).MD2())
|
return c.Send(NewMessage(fmt.Sprintf(format, v...)).MD2())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Same as Sendf but uses HTML format for parsing.
|
// Same as Sendf but uses HTML format for parsing.
|
||||||
func (c *Context) SendfHTML(format string, v ...any) (*Message, error) {
|
func (c Context) SendfHTML(format string, v ...any) (Message, error) {
|
||||||
return c.Send(NewMessage(fmt.Sprintf(format, v...)).HTML())
|
return c.Send(NewMessage(fmt.Sprintf(format, v...)).HTML())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) SendfR(format string, v ...any) (*Message, error) {
|
// Send the message in raw format escaping all the special characters.
|
||||||
|
func (c Context) SendfR(format string, v ...any) (Message, error) {
|
||||||
return c.Send(NewMessage(Escape2(fmt.Sprintf(format, v...))).MD2())
|
return c.Send(NewMessage(Escape2(fmt.Sprintf(format, v...))).MD2())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the input for current widget.
|
// Get the input for current widget.
|
||||||
// Should be used inside handlers (aka "Serve").
|
// Should be used inside handlers (aka "Serve").
|
||||||
func (c *Context) Input() chan *Update {
|
func (c Context) Input() chan Update {
|
||||||
return c.input.Chan()
|
return c.input.Chan()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns copy of current context so
|
func (c Context) WithArg(v any) Context {
|
||||||
// it will not affect the current one.
|
|
||||||
// But be careful because
|
|
||||||
// most of the insides uses pointers
|
|
||||||
// which are not deeply copied.
|
|
||||||
func (c *Context) Copy() *Context {
|
|
||||||
ret := *c
|
|
||||||
return &ret
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Context) WithArg(v any) *Context {
|
|
||||||
c = c.Copy()
|
|
||||||
c.arg = v
|
c.arg = v
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) WithUpdate(u *Update) *Context {
|
func (c Context) WithUpdate(u *Update) Context {
|
||||||
c = c.Copy()
|
|
||||||
c.Update = u
|
c.Update = u
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) WithInput(input *UpdateChan) *Context {
|
func (c Context) WithInput(input *UpdateChan) Context {
|
||||||
c = c.Copy()
|
|
||||||
c.input = input
|
c.input = input
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c Context) Go(pth Path) error {
|
||||||
|
return c.session.go_(pth, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Context) GoWithArg(pth Path, arg any) error {
|
||||||
|
return c.session.go_(pth, arg)
|
||||||
|
}
|
||||||
|
|
||||||
// Customized actions for the bot.
|
// Customized actions for the bot.
|
||||||
type Action interface {
|
type Action interface {
|
||||||
Act(*Context)
|
Act(Context)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ActionFunc func(*Context)
|
type ActionFunc func(Context)
|
||||||
|
|
||||||
func (af ActionFunc) Act(c *Context) {
|
func (af ActionFunc) Act(c *Context) {
|
||||||
af(c)
|
af(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) History() []Path {
|
func (c Context) History() []Path {
|
||||||
return c.pathHistory
|
return c.session.pathHistory
|
||||||
}
|
}
|
||||||
|
|
||||||
// Changes screen of user to the Id one.
|
func (c Context) PathExist(pth Path) bool {
|
||||||
func (c *Context) Go(pth Path, args ...any) error {
|
return c.bot.behaviour.PathExist(pth)
|
||||||
var err error
|
|
||||||
if pth == "" {
|
|
||||||
c.pathHistory = []Path{}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
var back bool
|
|
||||||
if pth == "-" {
|
|
||||||
if len(c.pathHistory) < 2 {
|
|
||||||
return c.Go("")
|
|
||||||
}
|
|
||||||
pth = c.pathHistory[len(c.pathHistory)-2]
|
|
||||||
c.pathHistory = c.pathHistory[:len(c.pathHistory)-1]
|
|
||||||
}
|
|
||||||
// Getting the screen and changing to
|
|
||||||
// then executing its widget.
|
|
||||||
if !pth.IsAbs() {
|
|
||||||
pth = (c.Path() + "/" + pth).Clean()
|
|
||||||
}
|
|
||||||
|
|
||||||
if !c.PathExist(pth) {
|
|
||||||
return ScreenNotExistErr
|
|
||||||
}
|
|
||||||
|
|
||||||
if !back && c.Path() != pth {
|
|
||||||
c.pathHistory = append(c.pathHistory, pth)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stopping the current widget.
|
|
||||||
screen := c.Bot.behaviour.Screens[pth]
|
|
||||||
c.skippedUpdates.Close()
|
|
||||||
if screen.Widget != nil {
|
|
||||||
c.skippedUpdates, err = c.RunWidget(screen.Widget, args...)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return NoWidgetForScreenErr
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) PathExist(pth Path) bool {
|
// Simple way to read strings for widgets with
|
||||||
return c.Bot.behaviour.PathExist(pth)
|
// the specified prompt.
|
||||||
}
|
func (c Context) ReadString(promptf string, args ...any) string {
|
||||||
|
|
||||||
func (c *Context) makeArg(args []any) any {
|
|
||||||
var arg any
|
|
||||||
if len(args) == 1 {
|
|
||||||
arg = args[0]
|
|
||||||
} else if len(args) > 1 {
|
|
||||||
arg = args
|
|
||||||
}
|
|
||||||
return arg
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Context) RunCompo(compo Component, args ...any) (*UpdateChan, error) {
|
|
||||||
if compo == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
s, ok := compo.(Sendable)
|
|
||||||
if ok {
|
|
||||||
msg, err := c.Send(s)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
s.SetMessage(msg)
|
|
||||||
}
|
|
||||||
updates := NewUpdateChan()
|
|
||||||
go func() {
|
|
||||||
compo.Serve(
|
|
||||||
c.WithInput(updates).
|
|
||||||
WithArg(c.makeArg(args)),
|
|
||||||
)
|
|
||||||
// To let widgets finish themselves before
|
|
||||||
// the channel is closed and close it by themselves.
|
|
||||||
updates.Close()
|
|
||||||
}()
|
|
||||||
return updates, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run widget in background returning the new input channel for it.
|
|
||||||
func (c *Context) RunWidget(widget Widget, args ...any) (*UpdateChan, error) {
|
|
||||||
var err error
|
|
||||||
if widget == nil {
|
|
||||||
return nil, EmptyWidgetErr
|
|
||||||
}
|
|
||||||
|
|
||||||
pth := c.Path()
|
|
||||||
compos := widget.Render(c.WithArg(c.makeArg(args)))
|
|
||||||
// Leave if changed path or components are empty.
|
|
||||||
if compos == nil || pth != c.Path() {
|
|
||||||
return nil, EmptyCompoErr
|
|
||||||
}
|
|
||||||
chns := make([]*UpdateChan, len(compos))
|
|
||||||
for i, compo := range compos {
|
|
||||||
chns[i], err = c.RunCompo(compo, args...)
|
|
||||||
if err != nil {
|
|
||||||
for _, chn := range chns {
|
|
||||||
chn.Close()
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ret := NewUpdateChan()
|
|
||||||
go func() {
|
|
||||||
ln := len(compos)
|
|
||||||
UPDATE:
|
|
||||||
for u := range ret.Chan() {
|
|
||||||
if u == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
cnt := 0
|
|
||||||
for i, compo := range compos {
|
|
||||||
chn := chns[i]
|
|
||||||
if chn.Closed() {
|
|
||||||
cnt++
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !compo.Filter(u) {
|
|
||||||
chn.Send(u)
|
|
||||||
continue UPDATE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if cnt == ln {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ret.Close()
|
|
||||||
for _, chn := range chns {
|
|
||||||
chn.Close()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simple way to read strings for widgets.
|
|
||||||
func (c *Context) ReadString(pref string, args ...any) string {
|
|
||||||
var text string
|
var text string
|
||||||
if pref != "" {
|
if pref != "" {
|
||||||
c.Sendf(pref, args...)
|
c.Sendf(promptf, args...)
|
||||||
}
|
}
|
||||||
for u := range c.Input() {
|
for u := range c.Input() {
|
||||||
if u == nil {
|
if u == nil {
|
||||||
|
@ -382,6 +198,10 @@ func (c *Context) ReadString(pref string, args ...any) string {
|
||||||
return text
|
return text
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c Context) Update() Update {
|
||||||
|
return c.update
|
||||||
|
}
|
||||||
|
|
||||||
// Returns the reader for specified file ID and path.
|
// Returns the reader for specified file ID and path.
|
||||||
func (c *Context) GetFile(fileId FileId) (io.ReadCloser, string, error) {
|
func (c *Context) GetFile(fileId FileId) (io.ReadCloser, string, error) {
|
||||||
file, err := c.Bot.Api.GetFile(tgbotapi.FileConfig{FileID:string(fileId)})
|
file, err := c.Bot.Api.GetFile(tgbotapi.FileConfig{FileID:string(fileId)})
|
||||||
|
|
6
file.go
6
file.go
|
@ -27,7 +27,7 @@ var (
|
||||||
// The type implements the structure to easily send
|
// The type implements the structure to easily send
|
||||||
// files to the client.
|
// files to the client.
|
||||||
type File struct {
|
type File struct {
|
||||||
*MessageCompo
|
MessageCompo
|
||||||
name string
|
name string
|
||||||
reader io.Reader
|
reader io.Reader
|
||||||
upload bool
|
upload bool
|
||||||
|
@ -108,7 +108,7 @@ func (f *File) SendData() string {
|
||||||
|
|
||||||
func (f *File) SendConfig(
|
func (f *File) SendConfig(
|
||||||
sid SessionId, bot *Bot,
|
sid SessionId, bot *Bot,
|
||||||
) (*SendConfig) {
|
) (SendConfig) {
|
||||||
var config SendConfig
|
var config SendConfig
|
||||||
cid := sid.ToApi()
|
cid := sid.ToApi()
|
||||||
|
|
||||||
|
@ -127,5 +127,5 @@ func (f *File) SendConfig(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return &config
|
return config
|
||||||
}
|
}
|
||||||
|
|
22
go.go
Normal file
22
go.go
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
package tg
|
||||||
|
|
||||||
|
func Go(pth Path) UI {
|
||||||
|
return UI{
|
||||||
|
GoWidget(pth),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type GoWidget string
|
||||||
|
// Implementing the Server interface.
|
||||||
|
func (widget GoWidget) Serve(c Context) {
|
||||||
|
c.input.Close()
|
||||||
|
c.Go(Path(widget))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (widget GoWidget) Render(c Context) UI {
|
||||||
|
return UI{widget}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (widget GoWidget) Filter(u Update) bool {
|
||||||
|
return true
|
||||||
|
}
|
23
inline.go
23
inline.go
|
@ -4,13 +4,14 @@ import (
|
||||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||||
)
|
)
|
||||||
|
|
||||||
// The type represents keyboard to be emdedded into the messages (inline in Telegram terms).
|
// The type represents keyboard to be emdedded
|
||||||
|
// into the messages (inline in Telegram terms).
|
||||||
type Inline struct {
|
type Inline struct {
|
||||||
*Keyboard
|
Keyboard
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert the inline keyboard to markup for the tgbotapi.
|
// Convert the inline keyboard to markup for the tgbotapi.
|
||||||
func (kbd *Inline) ToApi() tgbotapi.InlineKeyboardMarkup {
|
func (kbd Inline) ToApi() tgbotapi.InlineKeyboardMarkup {
|
||||||
rows := [][]tgbotapi.InlineKeyboardButton{}
|
rows := [][]tgbotapi.InlineKeyboardButton{}
|
||||||
for _, row := range kbd.Rows {
|
for _, row := range kbd.Rows {
|
||||||
if row == nil {
|
if row == nil {
|
||||||
|
@ -31,14 +32,14 @@ func (kbd *Inline) ToApi() tgbotapi.InlineKeyboardMarkup {
|
||||||
|
|
||||||
// The type implements message with an inline keyboard.
|
// The type implements message with an inline keyboard.
|
||||||
type InlineCompo struct {
|
type InlineCompo struct {
|
||||||
*MessageCompo
|
MessageCompo
|
||||||
*Inline
|
Inline
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implementing the Sendable interface.
|
// Implementing the Sendable interface.
|
||||||
func (compo *InlineCompo) SendConfig(
|
func (compo *InlineCompo) SendConfig(
|
||||||
sid SessionId, bot *Bot,
|
sid SessionId, bot *Bot,
|
||||||
) (*SendConfig) {
|
) (SendConfig) {
|
||||||
sendConfig := compo.MessageCompo.SendConfig(sid, bot)
|
sendConfig := compo.MessageCompo.SendConfig(sid, bot)
|
||||||
if len(compo.Inline.Rows) > 0 {
|
if len(compo.Inline.Rows) > 0 {
|
||||||
sendConfig.Message.ReplyMarkup = compo.Inline.ToApi()
|
sendConfig.Message.ReplyMarkup = compo.Inline.ToApi()
|
||||||
|
@ -48,7 +49,9 @@ func (compo *InlineCompo) SendConfig(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the component on the client side.
|
// Update the component on the client side.
|
||||||
func (compo *InlineCompo) Update(c *Context) {
|
// Requires exactly the pointer but not the value
|
||||||
|
// cause it changes insides of the structure.
|
||||||
|
func (compo *InlineCompo) Update(c Context) {
|
||||||
if compo.Message != nil {
|
if compo.Message != nil {
|
||||||
var edit tgbotapi.Chattable
|
var edit tgbotapi.Chattable
|
||||||
markup := compo.Inline.ToApi()
|
markup := compo.Inline.ToApi()
|
||||||
|
@ -74,7 +77,7 @@ func (compo *InlineCompo) Update(c *Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implementing the Filterer interface.
|
// Implementing the Filterer interface.
|
||||||
func (compo *InlineCompo) Filter(u *Update) bool {
|
func (compo InlineCompo) Filter(u Update) bool {
|
||||||
if compo == nil || u.CallbackQuery == nil {
|
if compo == nil || u.CallbackQuery == nil {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -88,13 +91,13 @@ func (compo *InlineCompo) Filter(u *Update) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implementing the Server interface.
|
// Implementing the Server interface.
|
||||||
func (compo *InlineCompo) Serve(c *Context) {
|
func (compo InlineCompo) Serve(c Context) {
|
||||||
for u := range c.Input() {
|
for u := range c.Input() {
|
||||||
compo.OnOneUpdate(c, u)
|
compo.OnOneUpdate(c, u)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (compo *InlineCompo) OnOneUpdate(c *Context, u *Update) {
|
func (compo *InlineCompo) OnOneUpdate(c Context, u Update) {
|
||||||
var act Action
|
var act Action
|
||||||
btns := compo.ButtonMap()
|
btns := compo.ButtonMap()
|
||||||
cb := tgbotapi.NewCallback(
|
cb := tgbotapi.NewCallback(
|
||||||
|
|
25
keyboard.go
25
keyboard.go
|
@ -14,7 +14,7 @@ type Keyboard struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the new keyboard with specified rows.
|
// Returns the new keyboard with specified rows.
|
||||||
func NewKeyboard(rows ...ButtonRow) *Keyboard {
|
func NewKeyboard(rows ...ButtonRow) Keyboard {
|
||||||
ret := &Keyboard{}
|
ret := &Keyboard{}
|
||||||
for _, row := range rows {
|
for _, row := range rows {
|
||||||
if row != nil && len(row) > 0 {
|
if row != nil && len(row) > 0 {
|
||||||
|
@ -24,7 +24,7 @@ func NewKeyboard(rows ...ButtonRow) *Keyboard {
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func (kbd *Keyboard) RowNum() int {
|
func (kbd Keyboard) RowNum() int {
|
||||||
return len(kbd.Rows)
|
return len(kbd.Rows)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ func (kbd *Keyboard) RemoveRow(i int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds a new button row to the current keyboard.
|
// Adds a new button row to the current keyboard.
|
||||||
func (kbd *Keyboard) Row(btns ...*Button) *Keyboard {
|
func (kbd Keyboard) Row(btns ...Button) Keyboard {
|
||||||
// For empty row. We do not need that.
|
// For empty row. We do not need that.
|
||||||
if len(btns) < 1 {
|
if len(btns) < 1 {
|
||||||
return kbd
|
return kbd
|
||||||
|
@ -56,7 +56,7 @@ func (kbd *Keyboard) Row(btns ...*Button) *Keyboard {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds buttons as one column list.
|
// Adds buttons as one column list.
|
||||||
func (kbd *Keyboard) List(btns ...*Button) *Keyboard {
|
func (kbd Keyboard) List(btns ...Button) Keyboard {
|
||||||
for _, btn := range btns {
|
for _, btn := range btns {
|
||||||
if btn == nil {
|
if btn == nil {
|
||||||
continue
|
continue
|
||||||
|
@ -68,18 +68,13 @@ func (kbd *Keyboard) List(btns ...*Button) *Keyboard {
|
||||||
|
|
||||||
// Set the default action when no button provides
|
// Set the default action when no button provides
|
||||||
// key to the data we got.
|
// key to the data we got.
|
||||||
func (kbd *Keyboard) WithAction(a Action) *Keyboard {
|
func (kbd Keyboard) WithAction(a Action) Keyboard {
|
||||||
kbd.Action = a
|
kbd.Action = a
|
||||||
return kbd
|
return kbd
|
||||||
}
|
}
|
||||||
|
|
||||||
// Alias to WithAction but better typing when setting
|
// Returns the map of buttons. Where the key
|
||||||
// a specific function
|
// is button data and the value is Action.
|
||||||
func (kbd *Keyboard) ActionFunc(fn ActionFunc) *Keyboard {
|
|
||||||
return kbd.WithAction(fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the map of buttons.
|
|
||||||
func (kbd Keyboard) ButtonMap() ButtonMap {
|
func (kbd Keyboard) ButtonMap() ButtonMap {
|
||||||
if kbd.buttonMap == nil {
|
if kbd.buttonMap == nil {
|
||||||
kbd.buttonMap = kbd.MakeButtonMap()
|
kbd.buttonMap = kbd.MakeButtonMap()
|
||||||
|
@ -101,14 +96,14 @@ func (kbd Keyboard) MakeButtonMap() ButtonMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert the keyboard to the more specific inline one.
|
// Convert the keyboard to the more specific inline one.
|
||||||
func (kbd *Keyboard) Inline() *Inline {
|
func (kbd Keyboard) Inline() Inline {
|
||||||
ret := &Inline{}
|
ret := Inline{}
|
||||||
ret.Keyboard = kbd
|
ret.Keyboard = kbd
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert the keyboard to the more specific reply one.
|
// Convert the keyboard to the more specific reply one.
|
||||||
func (kbd *Keyboard) Reply() *Reply {
|
func (kbd Keyboard) Reply() Reply {
|
||||||
ret := &Reply{}
|
ret := &Reply{}
|
||||||
ret.Keyboard = kbd
|
ret.Keyboard = kbd
|
||||||
// it is used more often than not once.
|
// it is used more often than not once.
|
||||||
|
|
55
message.go
55
message.go
|
@ -10,7 +10,7 @@ type Message = tgbotapi.Message
|
||||||
|
|
||||||
// Simple text message component type.
|
// Simple text message component type.
|
||||||
type MessageCompo struct {
|
type MessageCompo struct {
|
||||||
Message *Message
|
Message Message
|
||||||
ParseMode string
|
ParseMode string
|
||||||
Text string
|
Text string
|
||||||
}
|
}
|
||||||
|
@ -26,14 +26,19 @@ func Escape2(str string) string {
|
||||||
return string(escapeRe.ReplaceAll([]byte(str), []byte("\\$1")))
|
return string(escapeRe.ReplaceAll([]byte(str), []byte("\\$1")))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (compo *MessageCompo) Update(c *Context) {
|
// Call the function after the message was sent.
|
||||||
|
func (compo *MessageCompo) Update(c Context) error {
|
||||||
edit := tgbotapi.NewEditMessageText(
|
edit := tgbotapi.NewEditMessageText(
|
||||||
c.Session.Id.ToApi(),
|
c.Session.Id.ToApi(),
|
||||||
compo.Message.MessageID,
|
compo.Message.MessageID,
|
||||||
compo.Text,
|
compo.Text,
|
||||||
)
|
)
|
||||||
msg, _ := c.Bot.Api.Send(edit)
|
msg, err := c.bot.api.Send(edit)
|
||||||
compo.Message = &msg
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
compo.Message = msg
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (compo *MessageCompo) Delete(c *Context) {
|
func (compo *MessageCompo) Delete(c *Context) {
|
||||||
|
@ -44,59 +49,59 @@ func (compo *MessageCompo) Delete(c *Context) {
|
||||||
|
|
||||||
// Is only implemented to make it sendable and so we can put it
|
// Is only implemented to make it sendable and so we can put it
|
||||||
// return of rendering functions.
|
// return of rendering functions.
|
||||||
func (compo *MessageCompo) SetMessage(msg *Message) {
|
func (compo *MessageCompo) SetMessage(msg Message) {
|
||||||
compo.Message = msg
|
compo.Message = msg
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return new message with the specified text.
|
// Return new message with the specified text.
|
||||||
func NewMessage(format string, v ...any) *MessageCompo {
|
func Messagef(format string, v ...any) MessageCompo {
|
||||||
ret := &MessageCompo{}
|
ret := MessageCompo{}
|
||||||
ret.Text = fmt.Sprintf(format, v...)
|
ret.Text = fmt.Sprintf(format, v...)
|
||||||
ret.ParseMode = tgbotapi.ModeMarkdown
|
ret.ParseMode = tgbotapi.ModeMarkdown
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return message with the specified parse mode.
|
// Return message with the specified parse mode.
|
||||||
func (msg *MessageCompo) withParseMode(mode string) *MessageCompo {
|
func (msg MessageCompo) withParseMode(mode string) MessageCompo {
|
||||||
msg.ParseMode = mode
|
msg.ParseMode = mode
|
||||||
return msg
|
return msg
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the default Markdown parsing mode.
|
// Set the default Markdown parsing mode.
|
||||||
func (msg *MessageCompo) MD() *MessageCompo {
|
func (msg MessageCompo) MD() MessageCompo {
|
||||||
return msg.withParseMode(tgbotapi.ModeMarkdown)
|
return msg.withParseMode(tgbotapi.ModeMarkdown)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the Markdown 2 parsing mode.
|
// Set the Markdown 2 parsing mode.
|
||||||
func (msg *MessageCompo) MD2() *MessageCompo {
|
func (msg MessageCompo) MD2() MessageCompo {
|
||||||
return msg.withParseMode(tgbotapi.ModeMarkdownV2)
|
return msg.withParseMode(tgbotapi.ModeMarkdownV2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the HTML parsing mode.
|
// Set the HTML parsing mode.
|
||||||
func (msg *MessageCompo) HTML() *MessageCompo {
|
func (msg MessageCompo) HTML() MessageCompo {
|
||||||
return msg.withParseMode(tgbotapi.ModeHTML)
|
return msg.withParseMode(tgbotapi.ModeHTML)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transform the message component into one with reply keyboard.
|
// Transform the message component into one with reply keyboard.
|
||||||
func (msg *MessageCompo) Inline(inline *Inline) *InlineCompo {
|
func (msg MessageCompo) Inline(inline Inline) InlineCompo {
|
||||||
return &InlineCompo{
|
return InlineCompo{
|
||||||
Inline: inline,
|
Inline: inline,
|
||||||
MessageCompo: msg,
|
MessageCompo: msg,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transform the message component into one with reply keyboard.
|
// Transform the message component into one with reply keyboard.
|
||||||
func (msg *MessageCompo) Reply(reply *Reply) *ReplyCompo {
|
func (msg MessageCompo) Reply(reply Reply) ReplyCompo {
|
||||||
return &ReplyCompo{
|
return ReplyCompo{
|
||||||
Reply: reply,
|
Reply: reply,
|
||||||
MessageCompo: msg,
|
MessageCompo: msg,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transform the message component into the location one.
|
// Transform the message component into the location one.
|
||||||
func (msg *MessageCompo) Location(
|
func (msg MessageCompo) Location(
|
||||||
lat, long float64,
|
lat, long float64,
|
||||||
) *LocationCompo {
|
) LocationCompo {
|
||||||
ret := &LocationCompo{
|
ret := &LocationCompo{
|
||||||
MessageCompo: msg,
|
MessageCompo: msg,
|
||||||
Location: Location{
|
Location: Location{
|
||||||
|
@ -108,9 +113,9 @@ func (msg *MessageCompo) Location(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implementing the Sendable interface.
|
// Implementing the Sendable interface.
|
||||||
func (config *MessageCompo) SendConfig(
|
func (config MessageCompo) SendConfig(
|
||||||
sid SessionId, bot *Bot,
|
sid SessionId, bot *Bot,
|
||||||
) (*SendConfig) {
|
) (SendConfig) {
|
||||||
var (
|
var (
|
||||||
ret SendConfig
|
ret SendConfig
|
||||||
text string
|
text string
|
||||||
|
@ -122,20 +127,18 @@ func (config *MessageCompo) SendConfig(
|
||||||
text = config.Text
|
text = config.Text
|
||||||
}
|
}
|
||||||
|
|
||||||
//text = strings.ReplaceAll(text, "-", "\\-")
|
|
||||||
|
|
||||||
msg := tgbotapi.NewMessage(sid.ToApi(), text)
|
msg := tgbotapi.NewMessage(sid.ToApi(), text)
|
||||||
ret.Message = &msg
|
msg.ParseMode = config.ParseMode
|
||||||
ret.Message.ParseMode = config.ParseMode
|
ret.Chattable = msg
|
||||||
|
|
||||||
return &ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
// Empty serving to use messages in rendering.
|
// Empty serving to use messages in rendering.
|
||||||
func (compo *MessageCompo) Serve(c *Context) {}
|
func (compo *MessageCompo) Serve(c Context) {}
|
||||||
|
|
||||||
// Filter that skips everything. Messages cannot do anything with updates.
|
// Filter that skips everything. Messages cannot do anything with updates.
|
||||||
func (compo *MessageCompo) Filter(_ *Update) bool {
|
func (compo *MessageCompo) Filter(_ Update) bool {
|
||||||
// Skip everything
|
// Skip everything
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
21
reply.go
21
reply.go
|
@ -6,7 +6,7 @@ import (
|
||||||
|
|
||||||
// The type represents reply keyboards.
|
// The type represents reply keyboards.
|
||||||
type Reply struct {
|
type Reply struct {
|
||||||
*Keyboard
|
Keyboard
|
||||||
// If true will be removed after one press.
|
// If true will be removed after one press.
|
||||||
OneTime bool
|
OneTime bool
|
||||||
// If true will remove the keyboard on send.
|
// If true will remove the keyboard on send.
|
||||||
|
@ -15,20 +15,20 @@ type Reply struct {
|
||||||
|
|
||||||
// Set if we should remove current keyboard on the user side
|
// Set if we should remove current keyboard on the user side
|
||||||
// when sending the keyboard.
|
// when sending the keyboard.
|
||||||
func (kbd *Reply) WithRemove(remove bool) *Reply {
|
func (kbd Reply) WithRemove(remove bool) Reply {
|
||||||
kbd.Remove = remove
|
kbd.Remove = remove
|
||||||
return kbd
|
return kbd
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set if the keyboard should be hidden after
|
// Set if the keyboard should be hidden after
|
||||||
// one of buttons is pressede.
|
// one of buttons is pressede.
|
||||||
func (kbd *Reply) WithOneTime(oneTime bool) *Reply{
|
func (kbd Reply) WithOneTime(oneTime bool) Reply{
|
||||||
kbd.OneTime = oneTime
|
kbd.OneTime = oneTime
|
||||||
return kbd
|
return kbd
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert the Keyboard to the Telegram API type of reply keyboard.
|
// Convert the Keyboard to the Telegram API type of reply keyboard.
|
||||||
func (kbd *Reply) ToApi() any {
|
func (kbd Reply) ToApi() any {
|
||||||
// Shades everything.
|
// Shades everything.
|
||||||
if kbd.Remove {
|
if kbd.Remove {
|
||||||
return tgbotapi.NewRemoveKeyboard(true)
|
return tgbotapi.NewRemoveKeyboard(true)
|
||||||
|
@ -58,20 +58,21 @@ func (kbd *Reply) ToApi() any {
|
||||||
|
|
||||||
// The type implements reply keyboard widget.
|
// The type implements reply keyboard widget.
|
||||||
type ReplyCompo struct {
|
type ReplyCompo struct {
|
||||||
*MessageCompo
|
MessageCompo
|
||||||
*Reply
|
Reply
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implementing the sendable interface.
|
// Implementing the sendable interface.
|
||||||
func (compo *ReplyCompo) SendConfig(
|
func (compo ReplyCompo) SendConfig(
|
||||||
sid SessionId, bot *Bot,
|
sid SessionId, bot *Bot,
|
||||||
) (*SendConfig) {
|
) (SendConfig) {
|
||||||
sendConfig := compo.MessageCompo.SendConfig(sid, bot)
|
sendConfig := compo.MessageCompo.SendConfig(sid, bot)
|
||||||
sendConfig.Message.ReplyMarkup = compo.Reply.ToApi()
|
sendConfig.Message.ReplyMarkup = compo.Reply.ToApi()
|
||||||
return sendConfig
|
return sendConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
func (compo *ReplyCompo) Filter(
|
// Implementing the Server interface.
|
||||||
|
func (compo ReplyCompo) Filter(
|
||||||
u *Update,
|
u *Update,
|
||||||
) bool {
|
) bool {
|
||||||
if compo == nil || u.Message == nil {
|
if compo == nil || u.Message == nil {
|
||||||
|
@ -93,7 +94,7 @@ func (compo *ReplyCompo) Filter(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implementing the UI interface.
|
// Implementing the UI interface.
|
||||||
func (compo *ReplyCompo) Serve(c *Context) {
|
func (compo ReplyCompo) Serve(c *Context) {
|
||||||
for u := range c.Input() {
|
for u := range c.Input() {
|
||||||
var btn *Button
|
var btn *Button
|
||||||
text := u.Message.Text
|
text := u.Message.Text
|
||||||
|
|
30
send.go
30
send.go
|
@ -10,24 +10,14 @@ type MessageId int64
|
||||||
// way to define what message will be
|
// way to define what message will be
|
||||||
// sent to the side of a user.
|
// sent to the side of a user.
|
||||||
type Sendable interface {
|
type Sendable interface {
|
||||||
SendConfig(SessionId, *Bot) (*SendConfig)
|
SendConfig(SessionId, *Bot) (SendConfig)
|
||||||
SetMessage(*Message)
|
SetMessage(Message)
|
||||||
}
|
|
||||||
|
|
||||||
type Errorer interface {
|
|
||||||
Err() error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The type is used as an endpoint to send messages
|
// The type is used as an endpoint to send messages
|
||||||
// via bot.Send .
|
// via bot.Send .
|
||||||
type SendConfig struct {
|
type SendConfig struct {
|
||||||
// Message with text and keyboard.
|
Chattable tgbotapi.Chattable
|
||||||
Message *tgbotapi.MessageConfig
|
|
||||||
|
|
||||||
// The image to be sent.
|
|
||||||
Photo *tgbotapi.PhotoConfig
|
|
||||||
Document *tgbotapi.DocumentConfig
|
|
||||||
Location *tgbotapi.LocationConfig
|
|
||||||
Error error
|
Error error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,17 +25,7 @@ type SendConfig struct {
|
||||||
type MessageMap map[string] *Message
|
type MessageMap map[string] *Message
|
||||||
|
|
||||||
// Convert to the bot.Api.Send format.
|
// Convert to the bot.Api.Send format.
|
||||||
func (config *SendConfig) ToApi() tgbotapi.Chattable {
|
func (config SendConfig) ToApi() tgbotapi.Chattable {
|
||||||
switch {
|
return config.Chattable
|
||||||
case config.Message != nil :
|
|
||||||
return *(config.Message)
|
|
||||||
case config.Photo != nil :
|
|
||||||
return *(config.Photo)
|
|
||||||
case config.Location != nil :
|
|
||||||
return *(config.Location)
|
|
||||||
case config.Document != nil :
|
|
||||||
return *(config.Document)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
147
session.go
147
session.go
|
@ -1,5 +1,16 @@
|
||||||
package tg
|
package tg
|
||||||
|
|
||||||
|
// The type represents map of sessions using
|
||||||
|
// as key.
|
||||||
|
type SessionMap map[SessionId]*Session
|
||||||
|
|
||||||
|
// Add new empty session by it's ID.
|
||||||
|
func (sm SessionMap) Add(sid SessionId, scope SessionScope) *Session {
|
||||||
|
ret := NewSession(sid, scope)
|
||||||
|
sm[sid] = ret
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
// The way to determine where the context is
|
// The way to determine where the context is
|
||||||
// related to.
|
// related to.
|
||||||
type SessionScope uint8
|
type SessionScope uint8
|
||||||
|
@ -27,6 +38,11 @@ type Session struct {
|
||||||
Scope SessionScope
|
Scope SessionScope
|
||||||
// Custom value for each user.
|
// Custom value for each user.
|
||||||
Data any
|
Data any
|
||||||
|
|
||||||
|
bot *Bot
|
||||||
|
pathHistory []Path
|
||||||
|
skippedUpdates *UpdateChan
|
||||||
|
updates *UpdateChan
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return new empty session with specified user ID.
|
// Return new empty session with specified user ID.
|
||||||
|
@ -37,14 +53,129 @@ func NewSession(id SessionId, scope SessionScope) *Session {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The type represents map of sessions using
|
// Changes screen of user to the Id one.
|
||||||
// as key.
|
func (s *Session) go_(pth Path, arg any) error {
|
||||||
type SessionMap map[SessionId]*Session
|
var err error
|
||||||
|
if pth == "" {
|
||||||
|
s.pathHistory = []Path{}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var back bool
|
||||||
|
if pth == "-" {
|
||||||
|
if len(s.pathHistory) < 2 {
|
||||||
|
return s.Go("")
|
||||||
|
}
|
||||||
|
pth = s.pathHistory[len(s.pathHistory)-2]
|
||||||
|
s.pathHistory = s.pathHistory[:len(s.pathHistory)-1]
|
||||||
|
}
|
||||||
|
// Getting the screen and changing to
|
||||||
|
// then executing its widget.
|
||||||
|
if !pth.IsAbs() {
|
||||||
|
pth = (s.Path() + "/" + pth).Clean()
|
||||||
|
}
|
||||||
|
|
||||||
// Add new empty session by it's ID.
|
if !s.PathExist(pth) {
|
||||||
func (sm SessionMap) Add(sid SessionId, scope SessionScope) *Session {
|
return ScreenNotExistErr
|
||||||
ret := NewSession(sid, scope)
|
}
|
||||||
sm[sid] = ret
|
|
||||||
return ret
|
if !back && s.Path() != pth {
|
||||||
|
s.pathHistory = append(s.pathHistory, pth)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stopping the current widget.
|
||||||
|
screen := s.bot.behaviour.Screens[pth]
|
||||||
|
s.skippedUpdates.Close()
|
||||||
|
|
||||||
|
if screen.Widget != nil {
|
||||||
|
s.skippedUpdates, err = s.runWidget(screen.Widget, arg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return NoWidgetForScreenErr
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Session) runCompo(compo Component, arg any) (*UpdateChan, error) {
|
||||||
|
if compo == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
s, ok := compo.(Sendable)
|
||||||
|
if ok {
|
||||||
|
msg, err := c.Send(s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
s.SetMessage(msg)
|
||||||
|
}
|
||||||
|
updates := NewUpdateChan()
|
||||||
|
go func() {
|
||||||
|
compo.Serve(
|
||||||
|
c.WithInput(updates).
|
||||||
|
WithArg(arg),
|
||||||
|
)
|
||||||
|
// To let widgets finish themselves before
|
||||||
|
// the channel is closed and close it by themselves.
|
||||||
|
updates.Close()
|
||||||
|
}()
|
||||||
|
return updates, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run widget in background returning the new input channel for it.
|
||||||
|
func (c *Context) runWidget(widget Widget, arg any) (*UpdateChan, error) {
|
||||||
|
var err error
|
||||||
|
if widget == nil {
|
||||||
|
return nil, EmptyWidgetErr
|
||||||
|
}
|
||||||
|
|
||||||
|
pth := c.Path()
|
||||||
|
compos := widget.Render(c.WithArg(c.makeArg(args)))
|
||||||
|
// Leave if changed path or components are empty.
|
||||||
|
if compos == nil || pth != c.Path() {
|
||||||
|
return nil, EmptyCompoErr
|
||||||
|
}
|
||||||
|
chns := make([]*UpdateChan, len(compos))
|
||||||
|
for i, compo := range compos {
|
||||||
|
chns[i], err = c.runCompo(compo, arg)
|
||||||
|
if err != nil {
|
||||||
|
for _, chn := range chns {
|
||||||
|
chn.Close()
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret := NewUpdateChan()
|
||||||
|
go func() {
|
||||||
|
ln := len(compos)
|
||||||
|
UPDATE:
|
||||||
|
for u := range ret.Chan() {
|
||||||
|
if u == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
cnt := 0
|
||||||
|
for i, compo := range compos {
|
||||||
|
chn := chns[i]
|
||||||
|
if chn.Closed() {
|
||||||
|
cnt++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !compo.Filter(u) {
|
||||||
|
chn.Send(u)
|
||||||
|
continue UPDATE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if cnt == ln {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret.Close()
|
||||||
|
for _, chn := range chns {
|
||||||
|
chn.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
18
update.go
18
update.go
|
@ -5,13 +5,12 @@ import tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||||
type FileId string
|
type FileId string
|
||||||
|
|
||||||
type Update struct {
|
type Update struct {
|
||||||
*tgbotapi.Update
|
tgbotapi.Update
|
||||||
c *Context
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The type represents general update channel.
|
// The type represents general update channel.
|
||||||
type UpdateChan struct {
|
type UpdateChan struct {
|
||||||
chn chan *Update
|
chn chan Update
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return new update channel.
|
// Return new update channel.
|
||||||
|
@ -60,13 +59,13 @@ func (updates *UpdateChan) Close() {
|
||||||
close(chn)
|
close(chn)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *Update) HasDocument() bool {
|
func (u Update) HasDocument() bool {
|
||||||
return u != nil &&
|
return u != nil &&
|
||||||
u.Message != nil &&
|
u.Message != nil &&
|
||||||
u.Message.Document != nil
|
u.Message.Document != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *Update) DocumentId() FileId {
|
func (u Update) DocumentId() FileId {
|
||||||
return FileId(u.Update.Message.Document.FileID)
|
return FileId(u.Update.Message.Document.FileID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,23 +73,24 @@ func (u *Update) DocumentName() string {
|
||||||
return u.Message.Document.FileName
|
return u.Message.Document.FileName
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *Update) DocumentSize() int {
|
func (u Update) DocumentSize() int {
|
||||||
return u.Message.Document.FileSize
|
return u.Message.Document.FileSize
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *Update) DocumentMimeType() string {
|
func (u Update) DocumentMimeType() string {
|
||||||
return u.Message.Document.MimeType
|
return u.Message.Document.MimeType
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *Update) HasPhotos() bool {
|
func (u Update) HasPhotos() bool {
|
||||||
return u.Message != nil && u.Message.Photo != nil &&
|
return u.Message != nil && u.Message.Photo != nil &&
|
||||||
len(u.Message.Photo) != 0
|
len(u.Message.Photo) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *Update) PhotoIds() []FileId {
|
func (u Update) PhotoIds() []FileId {
|
||||||
ret := make([]FileId, len(u.Message.Photo))
|
ret := make([]FileId, len(u.Message.Photo))
|
||||||
for i, photo := range u.Message.Photo {
|
for i, photo := range u.Message.Photo {
|
||||||
ret[i] = FileId(photo.FileID)
|
ret[i] = FileId(photo.FileID)
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue