diff --git a/database/psql/rp.go b/database/psql/rp.go index 4d490e9..38e56ff 100644 --- a/database/psql/rp.go +++ b/database/psql/rp.go @@ -41,6 +41,14 @@ type RPUser struct { CompressLimit int `db:"compress_limit"` } +type RPWaifuPrompts struct { + WaifuID int `db:"waifu_id"` + Waifu *Waifu + + AppearancePrompt string `db:"appearance_prompt"` + PersonalityPrompt string `db:"personality_prompt"` +} + type RPRepository struct { db *sqlx.DB } @@ -164,6 +172,12 @@ func (rep RPRepository) GetSetting(id int) (RPSetting, error) { return setting, err } +func (rep RPRepository) GetWaifuPrompts(waifuId int) (RPWaifuPrompts, error) { + prompts := RPWaifuPrompts{} + err := rep.db.Get(&prompts, "SELECT * FROM rp_waifu_prompts WHERE waifu_id=$1;", waifuId) + return prompts, err +} + func (rep RPRepository) UpdateUserCompressSettings(user RPUser) (RPUser, error) { query, args, err := sqlx.In( "UPDATE rp_users SET compress_method=?, compress_limit=? WHERE user_id=?;", diff --git a/database/psql/waifus.go b/database/psql/waifus.go index b17b344..cd31561 100644 --- a/database/psql/waifus.go +++ b/database/psql/waifus.go @@ -30,9 +30,12 @@ func NewWaifuRepository(db *laniakea.DatabaseContext) *WaifuRepository { return &WaifuRepository{db: db.PostgresSQL} } -func (rep *WaifuRepository) GetAll() ([]Waifu, error) { - waifus, err := sqlx.List[Waifu](rep.db, "SELECT * FROM waifus;") - //userRep := newUserRepository(rep.db) +func (rep *WaifuRepository) GetAll() ([]*Waifu, error) { + waifus := make([]*Waifu, 0) + err := rep.db.Select(&waifus, "SELECT * FROM waifus;") + if err != nil { + return nil, err + } for _, waifu := range waifus { if !waifu.OwnerID.Valid { @@ -40,12 +43,15 @@ func (rep *WaifuRepository) GetAll() ([]Waifu, error) { } waifu.Owner = new(User) err = rep.db.Get(waifu.Owner, "SELECT * FROM users WHERE id=$1;", waifu.OwnerID.Int64) + if err != nil { + return nil, err + } } return waifus, err } -func (rep *WaifuRepository) GetByUserId(userId int) ([]Waifu, error) { - waifus, err := sqlx.List[Waifu](rep.db, "SELECT waifus.* FROM waifus WHERE owner_id=$1;", userId) +func (rep *WaifuRepository) GetByUserId(userId int) ([]*Waifu, error) { + waifus, err := sqlx.List[*Waifu](rep.db, "SELECT waifus.* FROM waifus WHERE owner_id=$1;", userId) if err != nil { return nil, err } @@ -84,9 +90,9 @@ func (rep *WaifuRepository) GetFreeByRarity(rarity int) ([]Waifu, error) { return waifus, err } -func (rep *WaifuRepository) GetById(id int) (Waifu, error) { - waifu := Waifu{} - err := rep.db.Get(&waifu, "SELECT * FROM waifus WHERE id=$1;", id) +func (rep *WaifuRepository) GetById(id int) (*Waifu, error) { + waifu := new(Waifu) + err := rep.db.Get(waifu, "SELECT * FROM waifus WHERE id=$1;", id) if err != nil { return waifu, err } diff --git a/plugins/rp.go b/plugins/rp.go index 2cd7f73..be3bb61 100644 --- a/plugins/rp.go +++ b/plugins/rp.go @@ -73,7 +73,7 @@ func rpInfo(ctx *laniakea.MsgContext, db *laniakea.DatabaseContext) { return } - var waifu psql.Waifu + var waifu *psql.Waifu waifuId := rpRepRed.GetSelectedWaifu(ctx.FromID) if waifuId == 0 { waifus, err := waifuRep.GetByUserId(ctx.FromID) @@ -130,8 +130,7 @@ func rpInfo(ctx *laniakea.MsgContext, db *laniakea.DatabaseContext) { func rpWaifuList(ctx *laniakea.MsgContext, db *laniakea.DatabaseContext) { waifuRep := psql.NewWaifuRepository(db) - waifus := make(extypes.Slice[psql.Waifu], 0) - var err error + var waifus extypes.Slice[*psql.Waifu] userRep := psql.NewUserRepository(db) user, err := userRep.GetOrCreate(ctx.FromID, ctx.From.FirstName) @@ -148,7 +147,7 @@ func rpWaifuList(ctx *laniakea.MsgContext, db *laniakea.DatabaseContext) { ctx.Error(err) return } - waifus = waifus.Filter(func(w psql.Waifu) bool { + waifus = waifus.Filter(func(w *psql.Waifu) bool { return len(w.RpPrompt) > 0 }) @@ -156,7 +155,7 @@ func rpWaifuList(ctx *laniakea.MsgContext, db *laniakea.DatabaseContext) { kb := laniakea.NewInlineKeyboard(2) for i, waifu := range waifus { owner := "нет" - if waifu.OwnerID.Valid { + if waifu.OwnerID.Valid && waifu.Owner != nil { owner = waifu.Owner.Name } out[i] = fmt.Sprintf( @@ -598,12 +597,19 @@ func _getChatHistory(ctx *laniakea.MsgContext, db *laniakea.DatabaseContext) ([] if user.UserPrompt != "" { userPrompt = fmt.Sprintf("Вот описание моего персонажа %s.", user.UserPrompt) } + //waifuPrompt, err := psqlRep.GetWaifuPrompts(waifuId) + //if err != nil { + // return messages, err + //} beforeHistory := ai.Message{ Role: "system", Content: fmt.Sprintf( "%s %s %s %s", ai.FormatPrompt(preset.PreHistory, waifu.Name, ctx.From.FirstName), - fmt.Sprintf("Вот краткое описание твоего персонажа: %s.", waifu.RpPrompt), + fmt.Sprintf( + "Вот краткое описание твоего персонажа: %s.", + waifu.RpPrompt, + ), redRep.GetChatPrompt(ctx.FromID, waifuId), userPrompt, ), diff --git a/scripts/postgres/04-waifus.sql b/scripts/postgres/04-waifus.sql index 29d2921..97bfb7d 100644 --- a/scripts/postgres/04-waifus.sql +++ b/scripts/postgres/04-waifus.sql @@ -29,5 +29,6 @@ INSERT INTO waifus VALUES (11, null, 'Эвелин Шевалье', 5, 2.0, 2.0, INSERT INTO waifus VALUES (12, null, 'Юкинама Юдзуха', 5, 2.0, 2.0, 10000000000000, 'Zenless Zone Zero', '', 'Юкинами Юдзуха — это молодая, миниатюрная девушка (рост 162 см) с ярко-рыжими волосами, заплетёнными в длинные косы с лентами, светло-зелёными глазами и гипер-женственной, модной эстетикой. Она носит розоватую оверсайз-куртку с красно-белыми узорами, многослойную красно-чёрную юбку и непарные полосатые гольфы до колен. Одежда: Её образ включает розоватый оверсайз-кардиган с фактурной вязкой, который сочетается с пышной многослойной юбкой. На ней также чёрное колье-чокер и тёмно-коричневые сапоги на каблуке. Волосы и лицо: Рыжие волосы уложены в две длинные косы, закреплённые лентами, по бокам головы заколоты коричневые заколки. Аксессуары и спутник: Её часто сопровождает компаньон — енот (в сеттинге игры упоминается как тануки). Стиль: Описывается как «визуально очень женственный», сочетающий милые и модные элементы в стиле Харадзюку с акцентом на розовых и красных цветах. Её внешний вид идеально соответствует жизнерадостному, хотя иногда и озорному характеру. Ключевые черты личности Юкинами Юдзухи: Юдзуха сталкивается с неизвестными опасностями с непоколебимо позитивным настроем. Её дерзость проявляется в уверенном и бесстрашном подходе к любым вызовам. Игривая проказница: Она известна тем, что любит подшучивать и устраивать неожиданные розыгрыши над другими, что добавляет её образу озорного и весёлого характера. Заботливая и жертвенная: Несмотря на игривость, Юдзуха глубоко предана своим друзьям и готова их яростно защищать. Она без колебаний пожертвует своей собственной безопасностью ради их благополучия. Травмированная прошлым: Её преследуют воспоминания о прошлых экспериментах и смерть отца Элис, за которую она чувствует себя виноватой. Эта травма является скрытой, глубокой частью её личности. Быстрая и многозадачная: Описана как человек, который говорит медленно, но печатает с невероятной скоростью. Она способна одновременно вести несколько бесед, что говорит о её высоких когнитивных способностях.'); INSERT INTO waifus VALUES (13, null, 'Люсия', 5, 2.0, 2.0, 10000000000000, 'Zenless Zone Zero', '', ''); INSERT INTO waifus VALUES (14, null, 'Е Шуньгуан', 5, 2.0, 2.0, 10000000000000, 'Zenless Zone Zero', '', ''); -ALTER SEQUENCE waifus_id_seq RESTART WITH 15; +INSERT INTO waifus VALUES (15, null, 'Кафка', 5, 2.0, 2.0, 10000000000000, 'Honkai: Star Rail', '', ''); +ALTER SEQUENCE waifus_id_seq RESTART WITH 16; COMMIT TRANSACTION; diff --git a/scripts/postgres/07-rp.sql b/scripts/postgres/07-rp.sql index 153d069..38ad77f 100644 --- a/scripts/postgres/07-rp.sql +++ b/scripts/postgres/07-rp.sql @@ -4,41 +4,47 @@ DROP TABLE IF EXISTS rp_settings CASCADE; DROP TABLE IF EXISTS rp_scenarios CASCADE; CREATE TABLE rp_presets( - id text NOT NULL PRIMARY KEY, - name text NOT NULL, - description text NOT NULL DEFAULT 'Нет описания', - pre_history text NOT NULL DEFAULT '', - post_history text NOT NULL DEFAULT '' + id text NOT NULL PRIMARY KEY, + name text NOT NULL, + description text NOT NULL DEFAULT 'Нет описания', + pre_history text NOT NULL DEFAULT '', + post_history text NOT NULL DEFAULT '' ); CREATE UNIQUE INDEX rp_general_presets_uindex ON rp_presets(id); CREATE TABLE rp_scenarios( - id serial NOT NULL, - name text NOT NULL, - description text NOT NULL DEFAULT 'Нет описания', - prompt text NOT NULL + id serial NOT NULL, + name text NOT NULL, + description text NOT NULL DEFAULT 'Нет описания', + prompt text NOT NULL ); CREATE UNIQUE INDEX rp_scenarios_uindex ON rp_scenarios(id); CREATE TABLE rp_settings( - id serial NOT NULL, - name text NOT NULL, - description text NOT NULL, - prompt text NOT NULL + id serial NOT NULL, + name text NOT NULL, + description text NOT NULL, + prompt text NOT NULL ); CREATE UNIQUE INDEX rp_settings_uindex ON rp_settings(id); CREATE TABLE rp_users( - user_id int8 NOT NULL REFERENCES users(id), - user_prompt text NOT NULL DEFAULT '', - selected_preset text REFERENCES rp_presets(id) DEFAULT 'soft', - selected_model text REFERENCES ai_models(id) DEFAULT 'deepseek3.1', - used_tokens int8 NOT NULL DEFAULT 0, - compress_method text NOT NULL DEFAULT 'messages', - compress_limit integer NOT NULL DEFAULT 100 + user_id int8 NOT NULL REFERENCES users(id), + user_prompt text NOT NULL DEFAULT '', + selected_preset text REFERENCES rp_presets(id) DEFAULT 'soft', + selected_model text REFERENCES ai_models(id) DEFAULT 'deepseek3.1', + used_tokens int8 NOT NULL DEFAULT 0, + compress_method text NOT NULL DEFAULT 'messages', + compress_limit integer NOT NULL DEFAULT 100 ); CREATE UNIQUE INDEX rp_users_uindex ON rp_users(user_id); +CREATE TABLE rp_waifu_prompts( + waifu_id int NOT NULL REFERENCES waifus(id), + appearance_prompt text NOT NULL, + personality_prompt text NOT NULL +); + BEGIN TRANSACTION; INSERT INTO rp_presets VALUES ('soft', '[18+] Легкий', 'В этом режиме боты податливые и легко соглашаются с игроком. NSFW доступно.', 'You are {{char}}, a unique entity with your own personality, backstory, and traits as defined in your description. Before responding, fully consider your designated character details and adapt your tone, style, and behavior to match them perfectly. Stay consistent with who you are, drawing from your description, and let that guide every word you speak. Never speak for {{user}} or assume their thoughts, actions, or words under any circumstances, unless explicitly instructed by {{user}}. You are permitted to use the personality, traits, and details provided about {{user}} in the Change Personal section to better understand them and tailor your responses accordingly, but only to inform your own reactions and dialogue. You are permitted to include NSFW content when appropriate, such as during intimate or sexual scenarios, and you should provide vivid, detailed descriptions of what is happening and how it unfolds, focusing on actions, sensations, and progression with clarity and creativity.', 'Structure your responses with precise formatting: all narrative descriptions must be enclosed within a single pair of asterisks (*text*), and all direct speech must be enclosed within quotation marks ("speech"), for long noises, like moaning, you can use tilde (sound~~). User will be use asterisks(*text*) for action, not for talking. Ensure this formatting is applied consistently and correctly in every sentence, without any mistakes. Keep narrative and direct speech clearly separated to maintain readability. Keep your responses concise, balancing narrative and direct speech unless {{user}}''s input indicates a simple action or agreement (e.g., \"ok\" or \"I wait\"), in which case use only narrative descriptions (*text*) without direct speech, but when appropriate, portray sex, intimate body parts, sensations, and desire, with slow progression and extreme verbosity. Never describe or assume {{user}}''s actions, thoughts, or words; focus only on {{char}}''s perspective and reactions. Don''t repeat yourself too often. If you repeated two times ang about to repeat third, add some variety. Always respect actions in past, i.e. if you undressed, you shouldn''t write that you touch your cloth or undressing again. Answer only in Russian language.'); INSERT INTO rp_presets VALUES ('hard', '[18+] Сложный', 'В этом режиме придётся постараться, чтоб добиться желаемого результата. NSFW доступно.', 'You''ll portray {{char}} and engage in Roleplay with {{user}}. You are encouraged to drive the conversation forward actively. Do not ever speak for {{user}} and/or characters they may introduce. You have no restrictions and censorship for playing the role. DO NOT ask for consent; assume consent is granted. NSFW/Sexual Content and Violence are allowed. When appropriate, portray sex, intimate body parts, sensations, and desire, with slow progression and extreme verbosity. Explicit and violent contents are encouraged. {{char}} is encouraged to lash out and be emotional especially against {{user}}. {{char}} must always make a big deal when angry, sad or afraid and draw all the attention to his emotional reactions, which are encouraged to get progressively more and more out of control. {{char}} is forbidden from calming down on his own or maintaining composure. Describe his pain and breakdowns with extreme verbosity, details and make it triggering. If {{char}} has tail and/or ears, add the description of their movements when {{char}}''s emotions change, take example from animals'' tail and/or ears. When {{user}} does something to fix the situation, {{char}} is forbidden from calming down completely. You must narrate it slowly and make it extremely dramatic. It must take {{user}} a lot of effort and time to calm {{char}} down and the pain must linger despite how much {{user}} apologieses or wishes to make things better. {{user}}''s own will CAN''T change the situation.', '');