inline keyboard

This commit is contained in:
2026-01-22 13:47:18 +03:00
parent 8c44707434
commit 015d5b904a
5 changed files with 180 additions and 99 deletions

View File

@@ -3,6 +3,7 @@ package psql
import ( import (
"database/sql" "database/sql"
"errors" "errors"
"kurumibot/laniakea"
"github.com/vinovest/sqlx" "github.com/vinovest/sqlx"
) )
@@ -17,6 +18,7 @@ type RPGeneralPreset struct {
type RPScenarios struct { type RPScenarios struct {
ID int ID int
Name string Name string
Description string
Prompt string Prompt string
} }
type RPUser struct { type RPUser struct {
@@ -30,8 +32,8 @@ type RPRepository struct {
db *sqlx.DB db *sqlx.DB
} }
func NewRPRepository(db *sqlx.DB) *RPRepository { func NewRPRepository(db *laniakea.DatabaseContext) *RPRepository {
return &RPRepository{db: db} return &RPRepository{db: db.PostgresSQL}
} }
func (rep *RPRepository) GetOrCreateUser(id int64) (*RPUser, error) { func (rep *RPRepository) GetOrCreateUser(id int64) (*RPUser, error) {

View File

@@ -37,7 +37,7 @@ func RegisterEconomy(bot *laniakea.Bot) {
func about(ctx *laniakea.MsgContext, _ *laniakea.DatabaseContext) { func about(ctx *laniakea.MsgContext, _ *laniakea.DatabaseContext) {
out := []string{ out := []string{
fmt.Sprintf("Go: %s", runtime.Version()), fmt.Sprintf("Версия Go: %s", runtime.Version()[2:]),
fmt.Sprintf("Версия Laniakea: %s", laniakea.VersionString), fmt.Sprintf("Версия Laniakea: %s", laniakea.VersionString),
fmt.Sprintf("Время сборки: %s", utils.BuildTime), fmt.Sprintf("Время сборки: %s", utils.BuildTime),
fmt.Sprintf("Git хеш: %s", utils.GitCommit), fmt.Sprintf("Git хеш: %s", utils.GitCommit),

View File

@@ -16,6 +16,9 @@ func InitLogMiddleware() *laniakea.Middleware {
} }
func logMiddleware(ctx *laniakea.MsgContext, db *laniakea.DatabaseContext) { func logMiddleware(ctx *laniakea.MsgContext, db *laniakea.DatabaseContext) {
if ctx.Msg == nil {
return
}
entry := &mdb.MessageLogEntry{ entry := &mdb.MessageLogEntry{
MessageID: ctx.Msg.MessageID, MessageID: ctx.Msg.MessageID,
SenderID: ctx.FromID, SenderID: ctx.FromID,

View File

@@ -17,66 +17,124 @@ import (
func RegisterRP(bot *laniakea.Bot) { func RegisterRP(bot *laniakea.Bot) {
rp := laniakea.NewPlugin("RP") rp := laniakea.NewPlugin("RP")
rp = rp.Command(selectWaifu, "rpwaifu", "рпвайфу") rp = rp.Command(rpInfo, "rp", "рп")
rp = rp.Payload(selectWaifu, "rp.selwaifu") rp = rp.Payload(rpInfo, "rp.info")
rp = rp.Command(rpPresetsList, "rpplist")
rp = rp.Command(rpPresetSet, "rppset") rp = rp.Payload(rpWaifuList, "rp.waifu_list")
rp = rp.Command(rpScenarioList, "rpscenlist") rp = rp.Payload(rpWaifuSet, "rp.waifu_set")
rp = rp.Command(newChat, "newchat") rp = rp.Payload(rpPresetsList, "rp.preset_list")
rp = rp.Payload(rpPresetSet, "rp.preset_set")
rp = rp.Payload(rpScenarioList, "rp.scenario_list")
rp = rp.Payload(rpScenarioSet, "rp.scenario_set")
rp = rp.Payload(chatStat, "rp.tokens")
rp = rp.Payload(newChat, "rp.new_chat")
rp = rp.Command(rpUserPromptGet, "rpuserpget") rp = rp.Command(rpUserPromptGet, "rpuserpget")
rp = rp.Command(rpUserPromptSet, "rpuserpset") rp = rp.Command(rpUserPromptSet, "rpuserpset")
rp = rp.Command(generate, "g", "gen", "г") rp = rp.Command(generate, "g", "gen", "г")
rp = rp.Command(chatStat, "chatstat") rp = rp.Payload(compress, "rp.compress_chat")
rp = rp.Payload(debugTokens, "rp.tokens")
bot.AddPlugins(rp.Build()) bot.AddPlugins(rp.Build())
} }
func debugTokens(ctx *laniakea.MsgContext, db *laniakea.DatabaseContext) { func rpInfo(ctx *laniakea.MsgContext, db *laniakea.DatabaseContext) {
rep := red.NewRPRepository(db) rpRepRed := red.NewRPRepository(db)
waifuId := rep.GetSelectedWaifu(ctx.FromID) rpRepPsql := psql.NewRPRepository(db)
tokens := rep.GetChatTokens(ctx.FromID, waifuId) waifuId := rpRepRed.GetSelectedWaifu(ctx.FromID)
ctx.Answerf("%d", tokens) waifu, err := psql.GetWaifuById(waifuId)
if err != nil {
ctx.Error(err)
}
rpUser, err := rpRepPsql.GetUser(int64(ctx.FromID))
if err != nil {
ctx.Error(err)
}
out := []string{
fmt.Sprintf("Привет, %s!", ctx.From.FirstName),
fmt.Sprintf("Выбранная вайфу: %s", waifu.Name),
fmt.Sprintf("Выбранный пресет: %s", "_TODO_"),
fmt.Sprintf("Выбранный сценарий: %s", "_TODO_"),
fmt.Sprintf("Твое описание персонажа: %s", rpUser.UserPrompt),
}
kb := laniakea.NewInlineKeyboard(1)
kb.AddCallbackButton("Статистика чата", "rp.tokens")
kb.AddCallbackButton("Сменить пресет", "rp.preset_list")
kb.AddCallbackButton("Выбрать сценарий", "rp.scenario_list")
kb.AddCallbackButton("Сменить вайфу", "rp.waifu_list")
kb.AddCallbackButton("Новый чат", "rp.new_chat")
if ctx.CallbackMsgId > 0 {
ctx.EditCallback(strings.Join(out, "\n"), kb)
} else {
ctx.Keyboard(strings.Join(out, "\n"), kb)
}
} }
func selectWaifu(ctx *laniakea.MsgContext, db *laniakea.DatabaseContext) { func rpWaifuList(ctx *laniakea.MsgContext, db *laniakea.DatabaseContext) {
waifus, err := psql.GetUserWaifus(ctx.FromID)
if err != nil {
ctx.Error(err)
return
}
out := make([]string, len(waifus))
kb := laniakea.NewInlineKeyboard(2)
for i, waifu := range waifus {
out[i] = fmt.Sprintf("*%s* %d\\* из \"%s\"", waifu.Name, waifu.Rarity, waifu.Fandom)
kb.AddCallbackButton(waifu.Name, "rp.waifu_set", waifu.ID)
}
kb.AddLine()
kb.AddCallbackButton("На главную", "rp.info")
ctx.EditCallback(strings.Join(out, "\n"), kb)
}
func rpWaifuSet(ctx *laniakea.MsgContext, db *laniakea.DatabaseContext) {
waifuId, err := strconv.Atoi(ctx.Args[0]) waifuId, err := strconv.Atoi(ctx.Args[0])
if err != nil { if err != nil {
ctx.Error(err) ctx.Error(err)
return return
} }
rpRep := red.NewRPRepository(db) rpRepRed := red.NewRPRepository(db)
err = rpRep.SetSelectedWaifu(ctx.FromID, waifuId) err = rpRepRed.SetSelectedWaifu(ctx.FromID, waifuId)
if err != nil { if err != nil {
ctx.Error(err) ctx.Error(err)
return return
} }
//rpRepPsql := psql.NewRPRepository(db)
waifu, err := psql.GetWaifuById(waifuId)
if err != nil {
ctx.Error(err)
}
ctx.Answer(fmt.Sprintf("Была выбрана вайфу %d", waifuId)) kb := laniakea.NewInlineKeyboard(1).AddCallbackButton("На главную", "rp.info")
ctx.EditCallbackf("Была выбрана вайфу %s", kb, waifu.Name)
} }
func rpPresetsList(ctx *laniakea.MsgContext, db *laniakea.DatabaseContext) { func rpPresetsList(ctx *laniakea.MsgContext, db *laniakea.DatabaseContext) {
rep := psql.NewRPRepository(db.PostgresSQL) rep := psql.NewRPRepository(db)
presets, err := rep.GetAllPresets() presets, err := rep.GetAllPresets()
if err != nil { if err != nil {
ctx.Error(err) ctx.Error(err)
return return
} }
out := make([]string, len(presets)) out := make([]string, len(presets))
kb := laniakea.NewInlineKeyboard(2)
for i, preset := range presets { for i, preset := range presets {
out[i] = fmt.Sprintf( out[i] = fmt.Sprintf(
"%s) *%s*\n%s", "*%s*\n%s",
preset.ID, laniakea.EscapeMarkdown(preset.Name), preset.Description, laniakea.EscapeMarkdown(preset.Name), preset.Description,
) )
kb.AddCallbackButton(preset.Name, "rp.preset_set", preset.ID)
} }
ctx.Answer(strings.Join(out, "\n")) kb.AddLine()
kb.AddCallbackButton("Назад", "rp.info")
ctx.EditCallback(strings.Join(out, "\n"), kb)
} }
func rpPresetSet(ctx *laniakea.MsgContext, db *laniakea.DatabaseContext) { func rpPresetSet(ctx *laniakea.MsgContext, db *laniakea.DatabaseContext) {
if len(ctx.Args) == 0 || ctx.Args[0] == "" { if len(ctx.Args) == 0 || ctx.Args[0] == "" {
return return
} }
presetId := ctx.Args[0] presetId := ctx.Args[0]
rep := psql.NewRPRepository(db.PostgresSQL) rep := psql.NewRPRepository(db)
user, err := rep.GetOrCreateUser(int64(ctx.FromID)) user, err := rep.GetOrCreateUser(int64(ctx.FromID))
if err != nil { if err != nil {
ctx.Error(err) ctx.Error(err)
@@ -91,21 +149,61 @@ func rpPresetSet(ctx *laniakea.MsgContext, db *laniakea.DatabaseContext) {
} }
return return
} }
ctx.Answer(fmt.Sprintf("Был выбран пресет %s", preset.Name))
kb := laniakea.NewInlineKeyboard(1).AddCallbackButton("На главную", "rp.info")
ctx.EditCallbackf("Был выбран пресет %s", kb, preset.Name)
} }
func rpScenarioList(ctx *laniakea.MsgContext, db *laniakea.DatabaseContext) { func rpScenarioList(ctx *laniakea.MsgContext, db *laniakea.DatabaseContext) {
rep := psql.NewRPRepository(db.PostgresSQL) rep := psql.NewRPRepository(db)
scenarios, err := rep.GetAllScenarios() scenarios, err := rep.GetAllScenarios()
if err != nil { if err != nil {
ctx.Error(err) ctx.Error(err)
return return
} }
out := make([]string, len(scenarios)) out := make([]string, len(scenarios))
kb := laniakea.NewInlineKeyboard(2)
for i, scenario := range scenarios { for i, scenario := range scenarios {
out[i] = fmt.Sprintf("%d) *%s*", scenario.ID, scenario.Name) out[i] = fmt.Sprintf("%d) *%s*\n%s\n", scenario.ID, scenario.Name, scenario.Description)
kb.AddCallbackButton(scenario.Name, "rp.scenario_set", scenario.ID)
} }
ctx.Answer(strings.Join(out, "\n")) kb.AddLine()
kb.AddCallbackButton("На главную", "rp.info")
ctx.EditCallback(strings.Join(out, "\n"), kb)
}
func rpScenarioSet(ctx *laniakea.MsgContext, db *laniakea.DatabaseContext) {}
func chatStat(ctx *laniakea.MsgContext, db *laniakea.DatabaseContext) {
redisRpRep := red.NewRPRepository(db)
waifuId := redisRpRep.GetSelectedWaifu(ctx.FromID)
if waifuId == 0 {
ctx.Answer("Не выбрана вайфу")
return
}
chatId := redisRpRep.GetChatId(ctx.FromID, waifuId)
if chatId == "" {
chatId = uuid.New().String()
err := redisRpRep.SetChatId(ctx.FromID, waifuId, chatId)
if err != nil {
ctx.Error(err)
return
}
}
messageCount, err := mdb.GetChatHistorySize(db, chatId)
if err != nil {
ctx.Error(err)
return
}
tokens := redisRpRep.GetChatTokens(ctx.FromID, waifuId)
kb := laniakea.NewInlineKeyboard(1).AddCallbackButton("На главную", "rp.info")
out := []string{
"Статистика чата",
fmt.Sprintf("ID: `%s`", chatId),
fmt.Sprintf("Кол-во сообщений: %d", messageCount),
fmt.Sprintf("Кол-во токенов: %d", tokens),
}
ctx.EditCallback(strings.Join(out, "\n"), kb)
} }
func newChat(ctx *laniakea.MsgContext, db *laniakea.DatabaseContext) { func newChat(ctx *laniakea.MsgContext, db *laniakea.DatabaseContext) {
redisRpRep := red.NewRPRepository(db) redisRpRep := red.NewRPRepository(db)
@@ -126,7 +224,7 @@ func newChat(ctx *laniakea.MsgContext, db *laniakea.DatabaseContext) {
ctx.Error(err) ctx.Error(err)
return return
} }
rep := psql.NewRPRepository(db.PostgresSQL) rep := psql.NewRPRepository(db)
scenario, err := rep.GetScenario(scenarioId) scenario, err := rep.GetScenario(scenarioId)
if err != nil { if err != nil {
if errors.Is(err, sql.ErrNoRows) { if errors.Is(err, sql.ErrNoRows) {
@@ -155,7 +253,7 @@ func newChat(ctx *laniakea.MsgContext, db *laniakea.DatabaseContext) {
} }
func rpUserPromptGet(ctx *laniakea.MsgContext, db *laniakea.DatabaseContext) { func rpUserPromptGet(ctx *laniakea.MsgContext, db *laniakea.DatabaseContext) {
rep := psql.NewRPRepository(db.PostgresSQL) rep := psql.NewRPRepository(db)
user, err := rep.GetOrCreateUser(int64(ctx.FromID)) user, err := rep.GetOrCreateUser(int64(ctx.FromID))
if err != nil { if err != nil {
ctx.Error(err) ctx.Error(err)
@@ -172,7 +270,7 @@ func rpUserPromptSet(ctx *laniakea.MsgContext, db *laniakea.DatabaseContext) {
return return
} }
prompt := strings.Join(ctx.Args[1:], " ") prompt := strings.Join(ctx.Args[1:], " ")
rep := psql.NewRPRepository(db.PostgresSQL) rep := psql.NewRPRepository(db)
user, err := rep.GetOrCreateUser(int64(ctx.FromID)) user, err := rep.GetOrCreateUser(int64(ctx.FromID))
if err != nil { if err != nil {
ctx.Error(err) ctx.Error(err)
@@ -208,7 +306,7 @@ func generate(ctx *laniakea.MsgContext, db *laniakea.DatabaseContext) {
ctx.Error(err) ctx.Error(err)
return return
} }
rpRep := psql.NewRPRepository(db.PostgresSQL) rpRep := psql.NewRPRepository(db)
rpUser, err := rpRep.GetUser(int64(ctx.FromID)) rpUser, err := rpRep.GetUser(int64(ctx.FromID))
if err != nil { if err != nil {
ctx.Error(err) ctx.Error(err)
@@ -222,14 +320,14 @@ func generate(ctx *laniakea.MsgContext, db *laniakea.DatabaseContext) {
userPrompt := "" userPrompt := ""
if rpUser.UserPrompt != "" { if rpUser.UserPrompt != "" {
userPrompt = fmt.Sprintf("Вот описание моего персонажа %s ", rpUser.UserPrompt) userPrompt = fmt.Sprintf("Вот описание моего персонажа %s.", rpUser.UserPrompt)
} }
beforeHistory := ai.Message{ beforeHistory := ai.Message{
Role: "system", Role: "system",
Content: fmt.Sprintf( Content: fmt.Sprintf(
"%s %s %s %s", "%s %s %s %s",
ai.FormatPrompt(preset.PreHistory, waifu.Name, ctx.Msg.From.FirstName), ai.FormatPrompt(preset.PreHistory, waifu.Name, ctx.Msg.From.FirstName),
fmt.Sprintf("Вот краткое описание твоего персонажа: %s", waifu.RpPrompt), fmt.Sprintf("Вот краткое описание твоего персонажа: %s.", waifu.RpPrompt),
userPrompt, userPrompt,
redisRpRep.GetChatPrompt(ctx.FromID, waifuId), redisRpRep.GetChatPrompt(ctx.FromID, waifuId),
), ),
@@ -298,65 +396,12 @@ func generate(ctx *laniakea.MsgContext, db *laniakea.DatabaseContext) {
} }
m.Delete() m.Delete()
//kb := laniakea.NewInlineKeyboard() kb := laniakea.NewInlineKeyboard(1).AddCallbackButton("Сжать чать", "rp.compress_chat")
//kb.AddCallbackButton("Перегенерировать", laniakea.CallbackData{Command: "rp.tokens"}) ctx.Keyboard(laniakea.EscapeMarkdown(agentAnswer.Content), kb)
//ctx.Keyboard("Test", kb)
ctx.Answer(laniakea.EscapeMarkdown(agentAnswer.Content))
//counter := redisRpRep.GetCounter(ctx.FromID, waifuId) + 1
//if counter == 20 {
// m := ctx.Answer("Запущено сжатие чата.")
// history, err = mdb.GetChatHistory(db, chatId)
// if err != nil {
// ctx.Error(err)
// return
// }
//
// messages = make([]ai.Message, 0)
// for _, m := range history {
// messages = append(messages, ai.Message{
// Role: m.Role,
// Content: m.Message,
// })
// }
// res, err = api.CompressChat(messages)
// if err != nil {
// ctx.Error(err)
// }
// if len(res.Choices) == 0 {
// m.Edit("Не удалось сжать диалог")
// return
// }
//
// compressedHistory := res.Choices[0].Message.Content
//
// chatId = uuid.New().String()
// err := redisRpRep.SetChatId(ctx.FromID, waifuId, chatId)
// if err != nil {
// ctx.Error(err)
// return
// }
//
// err = mdb.UpdateChatHistory(db, chatId, "assistant", compressedHistory)
// if err != nil {
// ctx.Error(err)
// return
// }
// err = mdb.UpdateChatHistory(db, chatId, agentAnswer.Role, agentAnswer.Content)
// if err != nil {
// ctx.Error(err)
// return
// }
// counter = 0
// m.Edit("Сжатие завершено")
//}
//err = redisRpRep.SetCounter(ctx.FromID, waifuId, counter)
//if err != nil {
// ctx.Error(err)
//}
} }
func chatStat(ctx *laniakea.MsgContext, db *laniakea.DatabaseContext) { func compress(ctx *laniakea.MsgContext, db *laniakea.DatabaseContext) {
m := ctx.Answer("Запущено сжатие чата.")
redisRpRep := red.NewRPRepository(db) redisRpRep := red.NewRPRepository(db)
waifuId := redisRpRep.GetSelectedWaifu(ctx.FromID) waifuId := redisRpRep.GetSelectedWaifu(ctx.FromID)
if waifuId == 0 { if waifuId == 0 {
@@ -372,12 +417,42 @@ func chatStat(ctx *laniakea.MsgContext, db *laniakea.DatabaseContext) {
return return
} }
} }
messageCount, err := mdb.GetChatHistorySize(db, chatId) history, err := mdb.GetChatHistory(db, chatId)
if err != nil { if err != nil {
ctx.Error(err) ctx.Error(err)
return return
} }
tokens := redisRpRep.GetChatTokens(ctx.FromID, waifuId) messages := make([]ai.Message, 0)
ctx.Answerf("Кол-во сообщений: %d\nКол-во токенов: %d", messageCount, tokens) for _, m := range history {
messages = append(messages, ai.Message{
Role: m.Role,
Content: m.Message,
})
}
api := ai.NewOpenAIAPI(ai.GPTBaseUrl, "", "deepseek-ai/deepseek-v3.1-terminus")
res, err := api.CompressChat(messages)
if err != nil {
ctx.Error(err)
}
if len(res.Choices) == 0 {
m.Edit("Не удалось сжать диалог")
return
}
compressedHistory := res.Choices[0].Message.Content
chatId = uuid.New().String()
err = redisRpRep.SetChatId(ctx.FromID, waifuId, chatId)
if err != nil {
ctx.Error(err)
return
}
err = mdb.UpdateChatHistory(db, chatId, "assistant", compressedHistory)
if err != nil {
ctx.Error(err)
return
}
m.Edit("Сжатие завершено")
} }

View File

@@ -1,7 +1,7 @@
CREATE TABLE rp_general_presets( CREATE TABLE rp_general_presets(
id text NOT NULL PRIMARY KEY, id text NOT NULL PRIMARY KEY,
name text NOT NULL, name text NOT NULL,
description text NOT NULL DEFAULT '', description text NOT NULL DEFAULT 'Нет описания',
pre_history text NOT NULL DEFAULT '', pre_history text NOT NULL DEFAULT '',
post_history text NOT NULL DEFAULT '' post_history text NOT NULL DEFAULT ''
); );
@@ -9,6 +9,7 @@ CREATE UNIQUE INDEX rp_general_presets_uindex ON rp_general_presets(id);
CREATE TABLE rp_scenarios( CREATE TABLE rp_scenarios(
id serial NOT NULL, id serial NOT NULL,
name text NOT NULL, name text NOT NULL,
description text NOT NULL DEFAULT 'Нет описания',
prompt text NOT NULL prompt text NOT NULL
); );
CREATE UNIQUE INDEX rp_scenarios_uindex ON rp_scenarios(id); CREATE UNIQUE INDEX rp_scenarios_uindex ON rp_scenarios(id);