many changes

This commit is contained in:
2026-03-12 18:11:27 +03:00
parent c93caeb04f
commit 12c8ce99f0
13 changed files with 132 additions and 155 deletions

View File

@@ -28,12 +28,7 @@ services:
MONGO_INITDB_ROOT_USERNAME: ${MONGO_USER} MONGO_INITDB_ROOT_USERNAME: ${MONGO_USER}
MONGO_INITDB_ROOT_PASSWORD: ${MONGO_PASS} MONGO_INITDB_ROOT_PASSWORD: ${MONGO_PASS}
MONGO_INITDB_DATABASE: ${MONGO_NAME} MONGO_INITDB_DATABASE: ${MONGO_NAME}
healthcheck: GLIBC_TUNABLES: glibc.cpu.hwcaps=-SHSTK
test: [ "CMD", "mongosh", "--quiet", "admin", "--eval", "quit(db.runCommand({ ping: 1 }).ok ? 0 : 2)" ]
interval: 10s
retries: 5
timeout: 10s
start_period: 10s
volumes: volumes:
- ./scripts/mongo:/docker-entrypoint-initdb.d - ./scripts/mongo:/docker-entrypoint-initdb.d
- mongo_data:/data/db - mongo_data:/data/db
@@ -43,12 +38,6 @@ services:
- bot - bot
ports: ports:
- "6379:6379" - "6379:6379"
healthcheck:
test: ["CMD", "redis-cli", "--raw", "ping"]
interval: 5s
retries: 5
timeout: 10s
start_period: 2s
volumes: volumes:
- redis_data:/data - redis_data:/data

11
go.mod
View File

@@ -4,7 +4,7 @@ go 1.26.1
require ( require (
git.nix13.pw/scuroneko/extypes v1.2.1 git.nix13.pw/scuroneko/extypes v1.2.1
git.nix13.pw/scuroneko/laniakea v1.0.0-beta.11 git.nix13.pw/scuroneko/laniakea v1.0.0-beta.16
git.nix13.pw/scuroneko/slog v1.0.2 git.nix13.pw/scuroneko/slog v1.0.2
github.com/google/uuid v1.6.0 github.com/google/uuid v1.6.0
github.com/joho/godotenv v1.5.1 github.com/joho/godotenv v1.5.1
@@ -15,13 +15,12 @@ require (
go.mongodb.org/mongo-driver/v2 v2.5.0 go.mongodb.org/mongo-driver/v2 v2.5.0
) )
replace git.nix13.pw/scuroneko/laniakea v1.0.0-beta.11 => ./laniakea //replace git.nix13.pw/scuroneko/laniakea v1.0.0-beta.16 => ./laniakea
//replace git.nix13.pw/scuroneko/extypes v1.2.1 => ../go-extypes //replace git.nix13.pw/scuroneko/extypes v1.2.1 => ../go-extypes
//replace git.nix13.pw/scuroneko/slog v1.0.2 => ../slog //replace git.nix13.pw/scuroneko/slog v1.0.2 => ../slog
require ( require (
github.com/alitto/pond/v2 v2.6.2 // indirect github.com/alitto/pond/v2 v2.7.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/fatih/color v1.18.0 // indirect github.com/fatih/color v1.18.0 // indirect
@@ -37,7 +36,7 @@ require (
go.uber.org/atomic v1.11.0 // indirect go.uber.org/atomic v1.11.0 // indirect
golang.org/x/crypto v0.48.0 // indirect golang.org/x/crypto v0.48.0 // indirect
golang.org/x/sync v0.19.0 // indirect golang.org/x/sync v0.19.0 // indirect
golang.org/x/sys v0.41.0 // indirect golang.org/x/sys v0.42.0 // indirect
golang.org/x/text v0.34.0 // indirect golang.org/x/text v0.34.0 // indirect
golang.org/x/time v0.14.0 // indirect golang.org/x/time v0.15.0 // indirect
) )

14
go.sum
View File

@@ -2,10 +2,12 @@ filippo.io/edwards25519 v1.1.1 h1:YpjwWWlNmGIDyXOn8zLzqiD+9TyIlPhGFG96P39uBpw=
filippo.io/edwards25519 v1.1.1/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= filippo.io/edwards25519 v1.1.1/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
git.nix13.pw/scuroneko/extypes v1.2.1 h1:IYrOjnWKL2EAuJYtYNa+luB1vBe6paE8VY/YD+5/RpQ= git.nix13.pw/scuroneko/extypes v1.2.1 h1:IYrOjnWKL2EAuJYtYNa+luB1vBe6paE8VY/YD+5/RpQ=
git.nix13.pw/scuroneko/extypes v1.2.1/go.mod h1:uZVs8Yo3RrYAG9dMad6qR6lsYY67t+459D9c65QAYAw= git.nix13.pw/scuroneko/extypes v1.2.1/go.mod h1:uZVs8Yo3RrYAG9dMad6qR6lsYY67t+459D9c65QAYAw=
git.nix13.pw/scuroneko/laniakea v1.0.0-beta.16 h1:QL3YGHL11p5For5Cm0I6riHsndBxT5vTHyqH3wtO0Xo=
git.nix13.pw/scuroneko/laniakea v1.0.0-beta.16/go.mod h1:M8jwm195hzAl9bj9Bkl95WfHmWvuBX6micsdtOs/gmE=
git.nix13.pw/scuroneko/slog v1.0.2 h1:vZyUROygxC2d5FJHUQM/30xFEHY1JT/aweDZXA4rm2g= git.nix13.pw/scuroneko/slog v1.0.2 h1:vZyUROygxC2d5FJHUQM/30xFEHY1JT/aweDZXA4rm2g=
git.nix13.pw/scuroneko/slog v1.0.2/go.mod h1:3Qm2wzkR5KjwOponMfG7TcGSDjmYaFqRAmLvSPTuWJI= git.nix13.pw/scuroneko/slog v1.0.2/go.mod h1:3Qm2wzkR5KjwOponMfG7TcGSDjmYaFqRAmLvSPTuWJI=
github.com/alitto/pond/v2 v2.6.2 h1:Sphe40g0ILeM1pA2c2K+Th0DGU+pt0A/Kprr+WB24Pw= github.com/alitto/pond/v2 v2.7.0 h1:c76L+yN916m/DRXjGCeUBHHu92uWnh/g1bwVk4zyyXg=
github.com/alitto/pond/v2 v2.6.2/go.mod h1:xkjYEgQ05RSpWdfSd1nM3OVv7TBhLdy7rMp3+2Nq+yE= github.com/alitto/pond/v2 v2.7.0/go.mod h1:xkjYEgQ05RSpWdfSd1nM3OVv7TBhLdy7rMp3+2Nq+yE=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
@@ -85,8 +87,8 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -95,8 +97,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=

25
main.go
View File

@@ -12,7 +12,6 @@ import (
) )
func main() { func main() {
log.Println(laniakea.EscapeMarkdownV2("Загрузка..."))
_ = godotenv.Load(".env", ".env.production", ".env.ai") _ = godotenv.Load(".env", ".env.production", ".env.ai")
database.ConnectPostgres() database.ConnectPostgres()
@@ -20,22 +19,16 @@ func main() {
database.ConnectRedis() database.ConnectRedis()
bot := laniakea.NewBot[database.Context](laniakea.LoadOptsFromEnv()) bot := laniakea.NewBot[database.Context](laniakea.LoadOptsFromEnv())
defer func(bot *laniakea.Bot[database.Context]) { defer func() {
err := bot.Close() _ = bot.Close()
if err != nil { }()
panic(err) bot = bot.ErrorTemplate(
} "Во время выполнения команды произошла ошибка!\nСообщите об этом разработчику!\n\n%s",
}(bot) ).DatabaseContext(&database.Context{
bot = bot.ErrorTemplate("Во время выполнения команды произошла ошибка!\nСообщите об этом разработчику!\n\n%s")
bot = bot.DatabaseContext(&database.Context{
Postgres: database.PostgresDatabase, Postgres: database.PostgresDatabase,
Mongo: database.MongoClient, Mongo: database.MongoClient,
Redis: database.RedisClient, Redis: database.RedisClient,
}) }).AddDatabaseLoggerWriter(plugins.DatabaseLogger).AddMiddleware(plugins.InitLogMiddleware()).AddPlugins(
bot.AddDatabaseLoggerWriter(plugins.DatabaseLogger)
bot.AddMiddleware(plugins.InitLogMiddleware())
bot.AddPlugins(
plugins.RegisterService(), plugins.RegisterService(),
plugins.RegisterAdmin(), plugins.RegisterAdmin(),
plugins.RegisterLogs(), plugins.RegisterLogs(),
@@ -47,10 +40,8 @@ func main() {
plugins.RegisterProxy(), plugins.RegisterProxy(),
) )
//plugins.RegisterRelations(bot)
if err := bot.AutoGenerateCommands(); err != nil { if err := bot.AutoGenerateCommands(); err != nil {
panic(err) log.Println(err)
} }
bot.Run() bot.Run()
} }

View File

@@ -17,10 +17,10 @@ import (
func RegisterAdmin() *laniakea.Plugin[database.Context] { func RegisterAdmin() *laniakea.Plugin[database.Context] {
p := laniakea.NewPlugin[database.Context]("Admin") p := laniakea.NewPlugin[database.Context]("Admin")
p.AddCommand(laniakea.NewCommand(uploadPhoto, "uploadPhoto").SkipCommandAutoGen()) p.NewCommand(uploadPhoto, "uploadPhoto").SkipCommandAutoGen()
p.AddCommand(laniakea.NewCommand(emojiId, "emojiId").SkipCommandAutoGen()) p.NewCommand(emojiId, "emojiId").SkipCommandAutoGen()
p.AddCommand(laniakea.NewCommand(execSql, "sql").SkipCommandAutoGen()) p.NewCommand(execSql, "sql").SkipCommandAutoGen()
p.AddCommand(laniakea.NewCommand(test, "test").SkipCommandAutoGen()) p.NewCommand(test, "test").SkipCommandAutoGen()
p.AddMiddleware(AdminMiddleware()) p.AddMiddleware(AdminMiddleware())
return p return p
@@ -75,7 +75,7 @@ func execSql(ctx *laniakea.MsgContext, db *database.Context) {
ctx.Error(err) ctx.Error(err)
return return
} }
ctx.Answerf("`%s`", data) ctx.AnswerfMarkdown("```json\n%s\n```", data)
} }
func emojiId(ctx *laniakea.MsgContext, _ *database.Context) { func emojiId(ctx *laniakea.MsgContext, _ *database.Context) {

View File

@@ -13,8 +13,7 @@ import (
func RegisterAi() *laniakea.Plugin[database.Context] { func RegisterAi() *laniakea.Plugin[database.Context] {
p := laniakea.NewPlugin[database.Context]("AI") p := laniakea.NewPlugin[database.Context]("AI")
p.AddCommand(p.NewCommand(gptTest, "gpt").SkipCommandAutoGen()) p.NewCommand(gptTest, "gpt").SkipCommandAutoGen()
//p.AddCommand(p.NewCommand(gptTest, "gpt2").SkipCommandAutoGen())
return p return p
} }

View File

@@ -9,8 +9,8 @@ import (
func RegisterProxy() *laniakea.Plugin[database.Context] { func RegisterProxy() *laniakea.Plugin[database.Context] {
p := laniakea.NewPlugin[database.Context]("Proxy") p := laniakea.NewPlugin[database.Context]("Proxy")
p.AddCommand(laniakea.NewCommand(getH2Link, "h2link")) p.NewCommand(getH2Link, "h2link")
p.AddCommand(laniakea.NewCommand(getProxy, "proxy")) p.NewCommand(getProxy, "proxy")
return p return p
} }

View File

@@ -17,22 +17,22 @@ import (
func RegisterEconomy(bot *laniakea.Bot[database.Context]) *laniakea.Plugin[database.Context] { func RegisterEconomy(bot *laniakea.Bot[database.Context]) *laniakea.Plugin[database.Context] {
p := laniakea.NewPlugin[database.Context]("Economy") p := laniakea.NewPlugin[database.Context]("Economy")
p.AddCommand(p.NewCommand(profile, "profile")) p.NewCommand(profile, "profile")
p.AddCommand(p.NewCommand(profile, "профиль").SkipCommandAutoGen()) p.NewCommand(profile, "профиль")
p.AddCommand(p.NewCommand(work, "work")) p.NewCommand(work, "work")
p.AddCommand(p.NewCommand(work, "работать").SkipCommandAutoGen()) p.NewCommand(work, "работать")
p.AddCommand(p.NewCommand(collect, "collect")) p.NewCommand(collect, "collect")
p.AddCommand(p.NewCommand(collect, "собрать").SkipCommandAutoGen()) p.NewCommand(collect, "собрать")
p.AddCommand(p.NewCommand(code, "code")) p.NewCommand(code, "code")
p.AddCommand(p.NewCommand(code, "код").SkipCommandAutoGen()) p.NewCommand(code, "код")
p.AddCommand(p.NewCommand(vacancies, "vacancies")) p.NewCommand(vacancies, "vacancies")
p.AddCommand(p.NewCommand(vacancies, "вакансии").SkipCommandAutoGen()) p.NewCommand(vacancies, "вакансии")
p.AddCommand(p.NewCommand(getAJob, "getajob")) p.NewCommand(getAJob, "getajob")
p.AddCommand(p.NewCommand(getAJob, "устроиться").SkipCommandAutoGen()) p.NewCommand(getAJob, "устроиться")
p.AddCommand(p.NewCommand(aboutGroup, "group")) p.NewCommand(aboutGroup, "group")
p.AddCommand(p.NewCommand(aboutGroup, "о группе").SkipCommandAutoGen()) p.NewCommand(aboutGroup, "о группе")
bot.AddRunner(*laniakea.NewRunner[database.Context]( bot.AddRunner(*laniakea.NewRunner[database.Context](
"economy.PassiveIncome", passiveIncome, "economy.PassiveIncome", passiveIncome,

View File

@@ -9,8 +9,8 @@ import (
func RegisterFun() *laniakea.Plugin[database.Context] { func RegisterFun() *laniakea.Plugin[database.Context] {
p := laniakea.NewPlugin[database.Context]("Fun") p := laniakea.NewPlugin[database.Context]("Fun")
p.AddCommand(p.NewCommand(beautyFont, "bf")) p.NewCommand(beautyFont, "bf")
p.AddCommand(p.NewCommand(beautyFontHeart, "bfh")) p.NewCommand(beautyFontHeart, "bfh")
return p return p
} }

View File

@@ -17,8 +17,8 @@ import (
func RegisterLogs() *laniakea.Plugin[database.Context] { func RegisterLogs() *laniakea.Plugin[database.Context] {
p := laniakea.NewPlugin[database.Context]("Logs") p := laniakea.NewPlugin[database.Context]("Logs")
p.AddCommand(p.NewCommand(getLogs, "logs").SkipCommandAutoGen()) p.NewCommand(getLogs, "logs").SkipCommandAutoGen()
p.AddCommand(p.NewCommand(getMsgLogs, "msglogs").SkipCommandAutoGen()) p.NewCommand(getMsgLogs, "msglogs").SkipCommandAutoGen()
p.AddMiddleware(AdminMiddleware()) p.AddMiddleware(AdminMiddleware())
return p return p
} }

View File

@@ -26,34 +26,35 @@ import (
func RegisterRP() *laniakea.Plugin[database.Context] { func RegisterRP() *laniakea.Plugin[database.Context] {
rp := laniakea.NewPlugin[database.Context]("RP") rp := laniakea.NewPlugin[database.Context]("RP")
rp.AddCommand(rp.NewCommand(rpUserPromptSet, "rpuserpset")) rp.NewCommand(rpUserPromptSet, "rpuserpset")
rp.AddCommand(rp.NewCommand(rpInfo, "rp").SetDescription("РП профиль пользователя")) rp.NewCommand(rpInfo, "rp").SetDescription("РП профиль пользователя")
rp.AddCommand(rp.NewCommand(rpInfo, "рп").SkipCommandAutoGen()) rp.NewCommand(rpInfo, "рп")
rp.AddPayload(rp.NewCommand(rpInfo, "rp.info"))
rp.AddPayload(rp.NewCommand(rpWaifuList, "rp.waifu_list")) rp.NewPayload(rpInfo, "rp.info")
rp.AddPayload(rp.NewCommand(rpWaifuSet, "rp.waifu_set")) rp.NewPayload(rpWaifuList, "rp.waifu_list")
rp.AddPayload(rp.NewCommand(rpPresetsList, "rp.preset_list")) rp.NewPayload(rpWaifuSet, "rp.waifu_set")
rp.AddPayload(rp.NewCommand(rpPresetSet, "rp.preset_set")) rp.NewPayload(rpPresetsList, "rp.preset_list")
rp.AddPayload(rp.NewCommand(rpModelList, "rp.model_list")) rp.NewPayload(rpPresetSet, "rp.preset_set")
rp.AddPayload(rp.NewCommand(rpModelSet, "rp.model_set")) rp.NewPayload(rpModelList, "rp.model_list")
rp.AddPayload(rp.NewCommand(rpScenarioList, "rp.scenario_list")) rp.NewPayload(rpModelSet, "rp.model_set")
rp.AddPayload(rp.NewCommand(rpSettingList, "rp.setting_list")) rp.NewPayload(rpScenarioList, "rp.scenario_list")
rp.AddPayload(rp.NewCommand(chatStat, "rp.chat_stat")) rp.NewPayload(rpSettingList, "rp.setting_list")
rp.AddPayload(rp.NewCommand(newChatStage1, "rp.new_chat_s1")) rp.NewPayload(chatStat, "rp.chat_stat")
rp.AddPayload(rp.NewCommand(newChatStage2, "rp.new_chat_s2")) rp.NewPayload(newChatStage1, "rp.new_chat_s1")
rp.AddPayload(rp.NewCommand(newChat, "rp.new_chat")) rp.NewPayload(newChatStage2, "rp.new_chat_s2")
rp.AddCommand(rp.NewCommand( rp.NewPayload(newChat, "rp.new_chat")
rp.NewCommand(
generate, "g", generate, "g",
*laniakea.NewCommandArg("prompt", laniakea.CommandValueAnyType), *laniakea.NewCommandArg("prompt", laniakea.CommandValueAnyType),
).SetDescription("Генерация РП")) ).SetDescription("Генерация РП")
rp.AddCommand(rp.NewCommand(generate, "gen").SkipCommandAutoGen()) rp.NewCommand(generate, "gen").SkipCommandAutoGen()
rp.AddCommand(rp.NewCommand(generate, "г").SkipCommandAutoGen()) rp.NewCommand(generate, "г").SkipCommandAutoGen()
rp.AddPayload(rp.NewCommand(compress, "rp.compress_chat")) rp.NewPayload(compress, "rp.compress_chat")
rp.AddPayload(rp.NewCommand(regenerateResponse, "rp.regenerate")) rp.NewPayload(regenerateResponse, "rp.regenerate")
rp.AddPayload(rp.NewCommand(compressSettingStage1, "rp.compress_setting_s1")) rp.NewPayload(compressSettingStage1, "rp.compress_setting_s1")
rp.AddPayload(rp.NewCommand(compressSettingStage2, "rp.compress_setting_s2")) rp.NewPayload(compressSettingStage2, "rp.compress_setting_s2")
rp.AddPayload(rp.NewCommand(compressSetting, "rp.compress_setting")) rp.NewPayload(compressSetting, "rp.compress_setting")
return rp return rp
} }
@@ -171,14 +172,14 @@ func rpWaifuList(ctx *laniakea.MsgContext, db *database.Context) {
owner = waifu.Owner.Name owner = waifu.Owner.Name
} }
out[i] = fmt.Sprintf( out[i] = fmt.Sprintf(
"*%s* %d\\* из \"%s\" (владелец: %s)", "*%s* %d из \"%s\" (владелец: %s)",
waifu.Name, waifu.Rarity, waifu.Fandom, owner, waifu.Name, waifu.Rarity, waifu.Fandom, owner,
) )
kb.AddCallbackButton(waifu.Name, "rp.waifu_set", waifu.ID) kb.AddCallbackButton(waifu.Name, "rp.waifu_set", waifu.ID)
} }
kb.AddLine() kb.AddLine()
kb.AddCallbackButtonStyle("На главную", laniakea.ButtonStyleDanger, "rp.info") kb.AddCallbackButtonStyle("На главную", laniakea.ButtonStyleDanger, "rp.info")
ctx.EditCallback(strings.Join(out, "\n"), kb) ctx.EditCallbackMarkdown(strings.Join(out, "\n"), kb)
ctx.AnswerCbQuery() ctx.AnswerCbQuery()
} }
func rpWaifuSet(ctx *laniakea.MsgContext, db *database.Context) { func rpWaifuSet(ctx *laniakea.MsgContext, db *database.Context) {
@@ -202,7 +203,7 @@ func rpWaifuSet(ctx *laniakea.MsgContext, db *database.Context) {
kb := laniakea.NewInlineKeyboard(1) kb := laniakea.NewInlineKeyboard(1)
kb.AddCallbackButtonStyle("На главную", laniakea.ButtonStyleSuccess, "rp.info") kb.AddCallbackButtonStyle("На главную", laniakea.ButtonStyleSuccess, "rp.info")
ctx.EditCallbackf("Была выбрана вайфу %s", kb, waifu.Name) ctx.EditCallbackfMarkdown("Была выбрана вайфу *%s*", kb, waifu.Name)
ctx.AnswerCbQuery() ctx.AnswerCbQuery()
} }
@@ -217,7 +218,7 @@ func rpPresetsList(ctx *laniakea.MsgContext, db *database.Context) {
kb := laniakea.NewInlineKeyboard(2) kb := laniakea.NewInlineKeyboard(2)
for i, preset := range presets { for i, preset := range presets {
out[i] = fmt.Sprintf( out[i] = fmt.Sprintf(
"*%s* - %s", "*%s* \\- %s",
laniakea.EscapeMarkdownV2(preset.Name), preset.Description, laniakea.EscapeMarkdownV2(preset.Name), preset.Description,
) )
kb.AddCallbackButton(preset.Name, "rp.preset_set", preset.ID) kb.AddCallbackButton(preset.Name, "rp.preset_set", preset.ID)
@@ -265,12 +266,12 @@ func rpModelList(ctx *laniakea.MsgContext, db *database.Context) {
out := make([]string, len(models)) out := make([]string, len(models))
kb := laniakea.NewInlineKeyboard(2) kb := laniakea.NewInlineKeyboard(2)
for i, model := range models { for i, model := range models {
out[i] = fmt.Sprintf("*%s* - размер контекста _%dK_", model.Name, model.ContextSize) out[i] = fmt.Sprintf("*%s* \\- размер контекста _%dK_", laniakea.EscapeMarkdownV2(model.Name), model.ContextSize)
kb.AddCallbackButton(model.Name, "rp.model_set", model.ID) kb.AddCallbackButton(model.Name, "rp.model_set", model.ID)
} }
kb.AddLine() kb.AddLine()
kb.AddCallbackButtonStyle("Назад", laniakea.ButtonStyleDanger, "rp.info") kb.AddCallbackButtonStyle("Назад", laniakea.ButtonStyleDanger, "rp.info")
ctx.EditCallbackMarkdown(laniakea.EscapePunctuation(strings.Join(out, "\n")), kb) ctx.EditCallbackMarkdown(strings.Join(out, "\n"), kb)
ctx.AnswerCbQuery() ctx.AnswerCbQuery()
} }
func rpModelSet(ctx *laniakea.MsgContext, db *database.Context) { func rpModelSet(ctx *laniakea.MsgContext, db *database.Context) {
@@ -308,10 +309,10 @@ func rpScenarioList(ctx *laniakea.MsgContext, db *database.Context) {
out := make([]string, len(scenarios)) out := make([]string, len(scenarios))
kb := laniakea.NewInlineKeyboard(1) kb := laniakea.NewInlineKeyboard(1)
for i, scenario := range scenarios { for i, scenario := range scenarios {
out[i] = fmt.Sprintf("*%s* - %s\n", scenario.Name, scenario.Description) out[i] = fmt.Sprintf("*%s* \\- %s\n", scenario.Name, scenario.Description)
} }
kb.AddCallbackButtonStyle("На главную", laniakea.ButtonStyleDanger, "rp.info") kb.AddCallbackButtonStyle("На главную", laniakea.ButtonStyleDanger, "rp.info")
ctx.EditCallbackMarkdown("Список сценариев\n"+laniakea.EscapePunctuation(strings.Join(out, "\n")), kb) ctx.EditCallbackMarkdown("Список сценариев\n"+strings.Join(out, "\n"), kb)
ctx.AnswerCbQuery() ctx.AnswerCbQuery()
} }
func rpSettingList(ctx *laniakea.MsgContext, db *database.Context) { func rpSettingList(ctx *laniakea.MsgContext, db *database.Context) {
@@ -324,11 +325,11 @@ func rpSettingList(ctx *laniakea.MsgContext, db *database.Context) {
out := make([]string, len(settings)) out := make([]string, len(settings))
for i, setting := range settings { for i, setting := range settings {
out[i] = fmt.Sprintf("*%s* - %s\n", setting.Name, setting.Description) out[i] = fmt.Sprintf("*%s* \\- %s\n", setting.Name, setting.Description)
} }
kb := laniakea.NewInlineKeyboard(1) kb := laniakea.NewInlineKeyboard(1)
kb.AddCallbackButtonStyle("На главную", laniakea.ButtonStyleSuccess, "rp.info") kb.AddCallbackButtonStyle("На главную", laniakea.ButtonStyleSuccess, "rp.info")
ctx.EditCallbackMarkdown(laniakea.EscapePunctuation(strings.Join(out, "\n")), kb) ctx.EditCallbackMarkdown(strings.Join(out, "\n"), kb)
ctx.AnswerCbQuery() ctx.AnswerCbQuery()
} }
@@ -387,13 +388,13 @@ func newChatStage1(ctx *laniakea.MsgContext, db *database.Context) {
} }
kb := laniakea.NewInlineKeyboard(2) kb := laniakea.NewInlineKeyboard(2)
for _, setting := range settings { for _, setting := range settings {
out = append(out, fmt.Sprintf("*%s* - %s", setting.Name, setting.Description)) out = append(out, fmt.Sprintf("*%s* \\- %s", setting.Name, setting.Description))
kb.AddCallbackButton(setting.Name, "rp.new_chat_s2", setting.ID) kb.AddCallbackButton(setting.Name, "rp.new_chat_s2", setting.ID)
} }
kb.AddCallbackButton("Без сеттинга", "rp.new_chat_s2", 0) kb.AddCallbackButton("Без сеттинга", "rp.new_chat_s2", 0)
kb.AddLine() kb.AddLine()
kb.AddCallbackButtonStyle("Назад", laniakea.ButtonStyleDanger, "rp.info") kb.AddCallbackButtonStyle("Назад", laniakea.ButtonStyleDanger, "rp.info")
ctx.EditCallbackMarkdown(laniakea.EscapePunctuation(strings.Join(out, "\n")), kb) ctx.EditCallbackMarkdown(strings.Join(out, "\n"), kb)
ctx.AnswerCbQuery() ctx.AnswerCbQuery()
} }
func newChatStage2(ctx *laniakea.MsgContext, db *database.Context) { func newChatStage2(ctx *laniakea.MsgContext, db *database.Context) {
@@ -536,7 +537,7 @@ func newChat(ctx *laniakea.MsgContext, db *database.Context) {
kb := laniakea.NewInlineKeyboard(2) kb := laniakea.NewInlineKeyboard(2)
kb.AddCallbackButtonStyle("На главную", laniakea.ButtonStyleSuccess, "rp.info") kb.AddCallbackButtonStyle("На главную", laniakea.ButtonStyleSuccess, "rp.info")
kb.AddCallbackButtonStyle("Закрыть", laniakea.ButtonStyleDanger, "general.close") kb.AddCallbackButtonStyle("Закрыть", laniakea.ButtonStyleDanger, "general.close")
ctx.EditCallback("Был создан новый чат. Для общения используй `/г промпт`.", kb) ctx.EditCallbackMarkdown("Был создан новый чат\\. Для общения используй `/г промпт`", kb)
ctx.AnswerCbQuery() ctx.AnswerCbQuery()
} }
@@ -989,11 +990,11 @@ func compressSettingStage1(ctx *laniakea.MsgContext, _ *database.Context) {
out := []string{ out := []string{
"Выбери метод для определения сжатия", "Выбери метод для определения сжатия",
"*По сообщениям* — чат будет сжиматься после достижения определенного кол-ва сообщений в нём", "*По сообщениям* \\— чат будет сжиматься после достижения определенного кол-ва сообщений в нём",
"*По токенам* — чат будет сжиматься после достижения определенного кол-ва токенов в нём", "*По токенам* \\— чат будет сжиматься после достижения определенного кол-ва токенов в нём",
} }
ctx.EditCallback(strings.Join(out, "\n"), kb) ctx.EditCallbackMarkdown(strings.Join(out, "\n"), kb)
} }
func compressSettingStage2(ctx *laniakea.MsgContext, _ *database.Context) { func compressSettingStage2(ctx *laniakea.MsgContext, _ *database.Context) {
if len(ctx.Args) == 0 { if len(ctx.Args) == 0 {

View File

@@ -14,32 +14,30 @@ func RegisterService() *laniakea.Plugin[database.Context] {
p := laniakea.NewPlugin[database.Context]("service") p := laniakea.NewPlugin[database.Context]("service")
p.AddPayload(p.NewCommand(generalClose, "general.close")) p.AddPayload(p.NewCommand(generalClose, "general.close"))
p.AddCommand(p.NewCommand(about, "about")) p.NewCommand(about, "about")
p.AddCommand(p.NewCommand(about, "о боте").SkipCommandAutoGen()) p.NewCommand(about, "о боте")
return p return p
} }
func about(ctx *laniakea.MsgContext, _ *database.Context) { func about(ctx *laniakea.MsgContext, _ *database.Context) {
formatVersion := func() string { formatVersion := func() string {
parts := strings.Split(laniakea.VersionString, "-") v := fmt.Sprintf("%d.%d.%d", laniakea.VersionMajor, laniakea.VersionMinor, laniakea.VersionPatch)
main := parts[0] if laniakea.VersionBeta > 0 {
if len(parts) > 1 { v += fmt.Sprintf(" Beta %d", laniakea.VersionBeta)
beta := strings.Join(strings.Split(parts[1], "."), " ")
return main + " " + beta
} }
return main return v
} }
out := []string{ out := []string{
fmt.Sprintf("Версия Go: %s", runtime.Version()[2:]), fmt.Sprintf("*Версия Go*: %s", runtime.Version()[2:]),
fmt.Sprintf("Версия Laniakea: %s", formatVersion()), fmt.Sprintf("*Версия Laniakea*: %s", formatVersion()),
fmt.Sprintf("Время сборки: %s", utils.BuildTime), fmt.Sprintf("*Время сборки*: %s", utils.BuildTime),
fmt.Sprintf("Git хеш: %s", utils.GitCommit), fmt.Sprintf("*Git хеш*: %s", utils.GitCommit),
} }
kb := laniakea.NewInlineKeyboard(2) kb := laniakea.NewInlineKeyboard(2)
kb.AddUrlButtonStyle("Канал", laniakea.ButtonStylePrimary, "https://t.me/ym_gbot_news") kb.AddUrlButtonStyle("Канал", laniakea.ButtonStylePrimary, "https://t.me/ym_gbot_news")
kb.AddUrlButtonStyle("Чат", laniakea.ButtonStylePrimary, "https://t.me/ym_gbot_chat") kb.AddUrlButtonStyle("Чат", laniakea.ButtonStylePrimary, "https://t.me/ym_gbot_chat")
ctx.Keyboard(strings.Join(out, "\n"), kb) ctx.KeyboardMarkdown(laniakea.EscapePunctuation(strings.Join(out, "\n")), kb)
} }
func generalClose(ctx *laniakea.MsgContext, _ *database.Context) { func generalClose(ctx *laniakea.MsgContext, _ *database.Context) {

View File

@@ -2,7 +2,6 @@ package plugins
import ( import (
"fmt" "fmt"
"log"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@@ -17,22 +16,22 @@ import (
func RegisterWaifus() *laniakea.Plugin[database.Context] { func RegisterWaifus() *laniakea.Plugin[database.Context] {
p := laniakea.NewPlugin[database.Context]("Waifus") p := laniakea.NewPlugin[database.Context]("Waifus")
p.AddCommand(p.NewCommand(myWaifu, "mywaifu")) p.NewCommand(myWaifu, "mywaifu").SetDescription("Список твоих вайфу")
p.AddCommand(p.NewCommand(myWaifu, "моивайфу").SkipCommandAutoGen()) p.NewCommand(myWaifu, "моивайфу")
p.AddCommand(p.NewCommand(waifuList, "wlist")) p.NewCommand(waifuList, "wlist").SetDescription("Список всех вайфу")
p.AddCommand(p.NewCommand(waifuList, "waifulist")) p.NewCommand(waifuList, "waifulist").SkipCommandAutoGen()
p.AddCommand(p.NewCommand(waifuList, "влист").SkipCommandAutoGen()) p.NewCommand(waifuList, "влист")
p.AddCommand(p.NewCommand(waifuList, "вайфулист").SkipCommandAutoGen()) p.NewCommand(waifuList, "вайфулист")
p.AddPayload(p.NewCommand(myWaifu, "waifu.my")) p.NewPayload(myWaifu, "waifu.my")
p.AddPayload(p.NewCommand(waifuList, "waifu.list")) p.NewPayload(waifuList, "waifu.list")
p.AddPayload(p.NewCommand(waifuSell, "waifu.sell")) p.NewPayload(waifuSell, "waifu.sell")
p.AddPayload(p.NewCommand(buyWaifu, "waifu.buy")) p.NewPayload(buyWaifu, "waifu.buy")
p.AddPayload(p.NewCommand(waifuInfo, "waifu.info")) p.NewPayload(waifuInfo, "waifu.info")
p.AddPayload(p.NewCommand(waifuSearch, "waifu.search")) p.NewPayload(waifuSearch, "waifu.search")
p.AddPayload(p.NewCommand(waifuNotImplemented, "waifu.confirm_buy")) p.NewPayload(waifuNotImplemented, "waifu.confirm_buy")
p.AddPayload(p.NewCommand(waifuNotImplemented, "waifu.confirm_sell")) p.NewPayload(waifuNotImplemented, "waifu.confirm_sell")
return p return p
} }
@@ -58,7 +57,7 @@ func myWaifu(ctx *laniakea.MsgContext, db *database.Context) {
"Список твоих вайфу:", "Список твоих вайфу:",
} }
for _, w := range waifus { for _, w := range waifus {
s := fmt.Sprintf("*%s* из \"*%s*\" (%d☆, ID: %d)", w.Name, w.Fandom, w.Rarity, w.ID) s := fmt.Sprintf("*%s* из \"*%s*\" \\(%d☆, ID: %d\\)", w.Name, w.Fandom, w.Rarity, w.ID)
out = append(out, s) out = append(out, s)
kb.AddCallbackButton(w.Name, "waifu.info", w.ID) kb.AddCallbackButton(w.Name, "waifu.info", w.ID)
} }
@@ -68,9 +67,9 @@ func myWaifu(ctx *laniakea.MsgContext, db *database.Context) {
kb.AddLine() kb.AddLine()
kb.AddCallbackButton("Все вайфу", "waifu.list") kb.AddCallbackButton("Все вайфу", "waifu.list")
if ctx.CallbackMsgId > 0 { if ctx.CallbackMsgId > 0 {
ctx.EditCallback(strings.Join(out, "\n"), kb) ctx.EditCallbackMarkdown(strings.Join(out, "\n"), kb)
} else { } else {
ctx.Keyboard(strings.Join(out, "\n"), kb) ctx.KeyboardMarkdown(strings.Join(out, "\n"), kb)
} }
} }
@@ -91,7 +90,7 @@ func waifuList(ctx *laniakea.MsgContext, db *database.Context) {
} else { } else {
owner = w.Owner.Name owner = w.Owner.Name
} }
out[i] = fmt.Sprintf("*%s* из \"*%s*\" (%d☆, ID: %d) Владелец: *%s*", w.Name, w.Fandom, w.Rarity, w.ID, owner) out[i] = fmt.Sprintf("*%s* из \"*%s*\" \\(%d☆, ID: %d\\) Владелец: *%s*", w.Name, w.Fandom, w.Rarity, w.ID, owner)
kb.AddCallbackButton(w.Name, "waifu.info", w.ID) kb.AddCallbackButton(w.Name, "waifu.info", w.ID)
} }
kb.AddLine() kb.AddLine()
@@ -100,12 +99,12 @@ func waifuList(ctx *laniakea.MsgContext, db *database.Context) {
if ctx.CallbackMsgId > 0 { if ctx.CallbackMsgId > 0 {
if len(ctx.Msg.Photo) > 0 { if len(ctx.Msg.Photo) > 0 {
ctx.CallbackDelete() ctx.CallbackDelete()
ctx.Keyboard(strings.Join(out, "\n"), kb) ctx.KeyboardMarkdown(strings.Join(out, "\n"), kb)
} else { } else {
ctx.EditCallback(strings.Join(out, "\n"), kb) ctx.EditCallbackMarkdown(strings.Join(out, "\n"), kb)
} }
} else { } else {
ctx.Keyboard(strings.Join(out, "\n"), kb) ctx.KeyboardMarkdown(strings.Join(out, "\n"), kb)
} }
} }
@@ -174,8 +173,8 @@ func waifuInfo(ctx *laniakea.MsgContext, db *database.Context) {
out := []string{ out := []string{
fmt.Sprintf("*%s* из \"*%s*\"", waifu.Name, waifu.Fandom), fmt.Sprintf("*%s* из \"*%s*\"", waifu.Name, waifu.Fandom),
fmt.Sprintf("Редкость: %d☆", waifu.Rarity), fmt.Sprintf("Редкость: %d☆", waifu.Rarity),
fmt.Sprintf("Бонус к деньгам: x%.2f", waifu.MoneyBonus.InexactFloat64()), laniakea.EscapePunctuation(fmt.Sprintf("Бонус к деньгам: x%.2f", waifu.MoneyBonus.InexactFloat64())),
fmt.Sprintf("Бонус к опыту: x%.2f", waifu.ExpBonus.InexactFloat64()), laniakea.EscapePunctuation(fmt.Sprintf("Бонус к опыту: x%.2f", waifu.ExpBonus.InexactFloat64())),
fmt.Sprintf("Рыночная стоимость: %s¥", utils.DecimalComma(&waifu.MarketPrice)), fmt.Sprintf("Рыночная стоимость: %s¥", utils.DecimalComma(&waifu.MarketPrice)),
} }
kb := laniakea.NewInlineKeyboard(2) kb := laniakea.NewInlineKeyboard(2)
@@ -191,10 +190,9 @@ func waifuInfo(ctx *laniakea.MsgContext, db *database.Context) {
kb.AddCallbackButton("Мои вайфу", "waifu.my") kb.AddCallbackButton("Мои вайфу", "waifu.my")
if len(waifu.Image) > 0 { if len(waifu.Image) > 0 {
ctx.CallbackDelete() ctx.CallbackDelete()
log.Println(waifu.Image) ctx.AnswerPhotoKeyboardMarkdown(waifu.Image, strings.Join(out, "\n"), kb)
ctx.AnswerPhotoKeyboard(waifu.Image, strings.Join(out, "\n"), kb)
} else { } else {
ctx.EditCallback(strings.Join(out, "\n"), kb) ctx.EditCallbackMarkdown(strings.Join(out, "\n"), kb)
} }
ctx.AnswerCbQuery() ctx.AnswerCbQuery()
} }
@@ -209,7 +207,7 @@ func waifuSearch(ctx *laniakea.MsgContext, db *database.Context) {
kb := laniakea.NewInlineKeyboard(2).AddCallbackButton("Мои вайфу", "waifu.my").AddCallbackButton("Все вайфу", "waifu.list") kb := laniakea.NewInlineKeyboard(2).AddCallbackButton("Мои вайфу", "waifu.my").AddCallbackButton("Все вайфу", "waifu.list")
if time.Now().Before(user.WaifuSearchTime.Add(time.Hour * 4)) { if time.Now().Before(user.WaifuSearchTime.Add(time.Hour * 4)) {
ctx.EditCallback("Вайфу можно искать раз в 4 часа.", kb) ctx.EditCallback("Вайфу можно искать раз в 4 часа", kb)
return return
} }
@@ -220,13 +218,13 @@ func waifuSearch(ctx *laniakea.MsgContext, db *database.Context) {
return return
} }
if count == 0 { if count == 0 {
ctx.EditCallback("Не осталось свободных вайфу... :(\nПопробуй позже или купи на рынке!", kb) ctx.EditCallbackMarkdown("Не осталось свободных вайфу… :\\(\nПопробуй позже или купи на рынке\\!", kb)
return return
} }
rand := utils.RandRange(0, 100) rand := utils.RandRange(0, 100)
if 10 < rand && rand < 90 { if 10 < rand && rand < 90 {
ctx.EditCallback("Ты ничего не нашел!", kb) ctx.EditCallback("Ты ничего не нашёл\\!", kb)
return return
} }
@@ -245,7 +243,7 @@ func waifuSearch(ctx *laniakea.MsgContext, db *database.Context) {
} }
if len(freeWaifus) == 0 { if len(freeWaifus) == 0 {
ctx.EditCallback("Ты ничего не нашел!", kb) ctx.EditCallback("Ты ничего не нашёл\\!", kb)
return return
} }
@@ -260,9 +258,9 @@ func waifuSearch(ctx *laniakea.MsgContext, db *database.Context) {
fmt.Sprintf("Рыночная стоимость: %s¥", utils.DecimalComma(&waifu.MarketPrice)), fmt.Sprintf("Рыночная стоимость: %s¥", utils.DecimalComma(&waifu.MarketPrice)),
} }
if len(waifu.Image) > 0 { if len(waifu.Image) > 0 {
ctx.AnswerPhoto(waifu.Image, strings.Join(out, "\n")) ctx.AnswerPhotoMarkdown(waifu.Image, strings.Join(out, "\n"))
} else { } else {
ctx.EditCallback(strings.Join(out, "\n"), kb) ctx.EditCallbackMarkdown(strings.Join(out, "\n"), kb)
} }
} }