layout: add template functions

pull/341/head
Demian 4 years ago
parent 5e96001289
commit 11ccdd09f5

@ -4,6 +4,7 @@ import (
"bytes"
"fmt"
"io/ioutil"
"sync"
"text/template"
"time"
@ -14,24 +15,21 @@ import (
type (
Layout struct {
pref *tele.Settings
ctxs map[tele.Context]string
locale string
Config map[string]interface{}
Markups map[string]Markup
Locales map[string]*template.Template
pref *tele.Settings
mu sync.RWMutex // protects ctxs
ctxs map[tele.Context]string
funcs template.FuncMap
config map[string]interface{}
markups map[string]Markup
locales map[string]*template.Template
}
Markup struct {
tele.ReplyMarkup `yaml:",inline"`
Keyboard *template.Template `yaml:"-"`
inline bool
}
Button struct {
tele.ReplyButton `yaml:",inline"`
tele.InlineButton `yaml:",inline"`
keyboard *template.Template
inline bool
}
LocaleFunc func(tele.Recipient) string
@ -43,10 +41,35 @@ func New(path string) (*Layout, error) {
return nil, err
}
lt := Layout{ctxs: make(map[tele.Context]string)}
lt := Layout{
ctxs: make(map[tele.Context]string),
funcs: make(template.FuncMap),
}
for k, v := range funcs {
lt.funcs[k] = v
}
// Built-in blank and helper functions
lt.funcs["config"] = lt.Get
lt.funcs["locale"] = func() string { return "" }
lt.funcs["text"] = func(k string) string { return "" }
return &lt, yaml.Unmarshal(data, &lt)
}
var funcs = make(template.FuncMap)
func AddFunc(key string, fn interface{}) {
funcs[key] = fn
}
func AddFuncs(fm template.FuncMap) {
for k, v := range fm {
funcs[k] = v
}
}
func (lt *Layout) Settings() tele.Settings {
if lt.pref == nil {
panic("telebot/layout: settings is empty")
@ -54,38 +77,37 @@ func (lt *Layout) Settings() tele.Settings {
return *lt.pref
}
func (lt *Layout) With(c tele.Context) *Layout {
cp := *lt
cp.locale = lt.ctxs[c]
return &cp
}
func (lt *Layout) Get(k string) string {
return fmt.Sprint(lt.Config[k])
return fmt.Sprint(lt.config[k])
}
func (lt *Layout) Int(k string) int {
return cast.ToInt(lt.Config[k])
return cast.ToInt(lt.config[k])
}
func (lt *Layout) Int64(k string) int64 {
return cast.ToInt64(lt.Config[k])
return cast.ToInt64(lt.config[k])
}
func (lt *Layout) Float(k string) float64 {
return cast.ToFloat64(lt.Config[k])
return cast.ToFloat64(lt.config[k])
}
func (lt *Layout) Duration(k string) time.Duration {
return cast.ToDuration(lt.Config[k])
return cast.ToDuration(lt.config[k])
}
func (lt *Layout) Text(k string, args ...interface{}) string {
if len(lt.Locales) == 0 {
func (lt *Layout) Text(c tele.Context, k string, args ...interface{}) string {
locale, ok := lt.locale(c)
if !ok {
return ""
}
tmpl, ok := lt.Locales[lt.locale]
return lt.text(locale, k, args...)
}
func (lt *Layout) text(locale, k string, args ...interface{}) string {
tmpl, ok := lt.locales[locale]
if !ok {
return ""
}
@ -96,18 +118,15 @@ func (lt *Layout) Text(k string, args ...interface{}) string {
}
var buf bytes.Buffer
if err := tmpl.ExecuteTemplate(&buf, k, arg); err != nil {
if err := lt.template(tmpl, locale).ExecuteTemplate(&buf, k, arg); err != nil {
// TODO: Log.
}
return buf.String()
}
func (lt *Layout) Markup(k string, args ...interface{}) *tele.ReplyMarkup {
if len(lt.Markups) == 0 {
return nil
}
markup, ok := lt.Markups[k]
func (lt *Layout) Markup(c tele.Context, k string, args ...interface{}) *tele.ReplyMarkup {
markup, ok := lt.markups[k]
if !ok {
return nil
}
@ -118,7 +137,8 @@ func (lt *Layout) Markup(k string, args ...interface{}) *tele.ReplyMarkup {
}
var buf bytes.Buffer
if err := markup.Keyboard.Execute(&buf, arg); err != nil {
locale, _ := lt.locale(c)
if err := lt.template(markup.keyboard, locale).Execute(&buf, arg); err != nil {
// TODO: Log.
}
@ -142,3 +162,20 @@ func (lt *Layout) Markup(k string, args ...interface{}) *tele.ReplyMarkup {
return &r
}
func (lt *Layout) locale(c tele.Context) (string, bool) {
lt.mu.RLock()
defer lt.mu.RUnlock()
locale, ok := lt.ctxs[c]
return locale, ok
}
func (lt *Layout) template(tmpl *template.Template, locale string) *template.Template {
funcs := make(template.FuncMap)
// Redefining built-in blank functions
funcs["text"] = func(k string) string { return lt.text(locale, k) }
funcs["locale"] = func() string { return locale }
return tmpl.Funcs(funcs)
}

@ -32,11 +32,11 @@ func TestLayout(t *testing.T) {
ReplyKeyboard: [][]tele.ReplyButton{{{Text: "Send a contact", Contact: true}}},
ResizeReplyKeyboard: true,
OneTimeKeyboard: true,
}, lt.Markup("reply"))
}, lt.Markup(nil, "reply"))
assert.Equal(t, &tele.ReplyMarkup{
InlineKeyboard: [][]tele.InlineButton{{{Unique: "inline"}}},
}, lt.Markup("inline"))
}, lt.Markup(nil, "inline"))
assert.Equal(t, &tele.ReplyMarkup{
ReplyKeyboard: [][]tele.ReplyButton{
@ -44,9 +44,9 @@ func TestLayout(t *testing.T) {
{{Text: "Settings"}},
},
ResizeReplyKeyboard: true,
}, lt.Markup("embedded"))
}, lt.Markup(nil, "embedded"))
assert.Equal(t, &tele.ReplyMarkup{
InlineKeyboard: [][]tele.InlineButton{{{Unique: "anchored"}}},
}, lt.Markup("anchored"))
}, lt.Markup(nil, "anchored"))
}

@ -1,6 +1,8 @@
package layout
import tele "gopkg.in/tucnak/telebot.v3"
import (
tele "gopkg.in/tucnak/telebot.v3"
)
func (lt *Layout) Middleware(defaultLocale string, localeFunc ...LocaleFunc) tele.MiddlewareFunc {
var f LocaleFunc
@ -17,7 +19,9 @@ func (lt *Layout) Middleware(defaultLocale string, localeFunc ...LocaleFunc) tel
}
}
lt.mu.Lock()
lt.ctxs[c] = locale
lt.mu.Unlock()
return next(c)
}
}

@ -37,7 +37,7 @@ func (lt *Layout) UnmarshalYAML(data []byte) error {
return err
}
lt.Config = aux.Config
lt.config = aux.Config
if pref := aux.Settings; pref != nil {
lt.pref = &tele.Settings{
@ -58,7 +58,7 @@ func (lt *Layout) UnmarshalYAML(data []byte) error {
}
}
lt.Markups = make(map[string]Markup, len(aux.Markups))
lt.markups = make(map[string]Markup, len(aux.Markups))
for _, item := range aux.Markups {
k, v := item.Key.(string), item.Value
@ -79,15 +79,15 @@ func (lt *Layout) UnmarshalYAML(data []byte) error {
return err
}
tmpl, err := template.New(k).Parse(string(data))
tmpl, err := template.New(k).Funcs(lt.funcs).Parse(string(data))
if err != nil {
return err
}
markup.Markup.Keyboard = tmpl
markup.Markup.keyboard = tmpl
markup.ResizeReplyKeyboard = markup.Resize == nil || *markup.Resize
lt.Markups[k] = markup.Markup
lt.markups[k] = markup.Markup
}
// 2. Shortened reply markup.
@ -108,27 +108,27 @@ func (lt *Layout) UnmarshalYAML(data []byte) error {
return err
}
tmpl, err := template.New(k).Parse(string(data))
tmpl, err := template.New(k).Funcs(lt.funcs).Parse(string(data))
if err != nil {
return err
}
markup := Markup{Keyboard: tmpl}
markup := Markup{keyboard: tmpl}
markup.ResizeReplyKeyboard = true
lt.Markups[k] = markup
lt.markups[k] = markup
}
// 3. Shortened inline markup.
if yaml.Unmarshal(data, &[][]tele.InlineButton{}) == nil {
tmpl, err := template.New(k).Parse(string(data))
tmpl, err := template.New(k).Funcs(lt.funcs).Parse(string(data))
if err != nil {
return err
}
lt.Markups[k] = Markup{
lt.markups[k] = Markup{
keyboard: tmpl,
inline: true,
Keyboard: tmpl,
}
}
}
@ -144,7 +144,7 @@ func (lt *Layout) UnmarshalYAML(data []byte) error {
}
func (lt *Layout) parseLocales(dir string) error {
lt.Locales = make(map[string]*template.Template)
lt.locales = make(map[string]*template.Template)
return filepath.Walk(dir, func(path string, fi os.FileInfo, _ error) error {
if fi == nil || fi.IsDir() {
@ -167,13 +167,13 @@ func (lt *Layout) parseLocales(dir string) error {
tmpl := template.New(name)
for key, text := range texts {
text = strings.Trim(text, "\r\n")
tmpl, err = tmpl.New(key).Parse(text)
tmpl, err = tmpl.New(key).Funcs(lt.funcs).Parse(text)
if err != nil {
return err
}
}
lt.Locales[name] = tmpl
lt.locales[name] = tmpl
return nil
})
}

Loading…
Cancel
Save