Skip to content

Commit

Permalink
Readme, Fixed embeds, Less copy n Paste
Browse files Browse the repository at this point in the history
  • Loading branch information
Day-OS committed Jul 14, 2023
1 parent ef6ff58 commit a99b802
Show file tree
Hide file tree
Showing 8 changed files with 77 additions and 71 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ rand = "0.8.5"
async-recursion = "1.0.4"
json-gettext = "4.0.5"
try-catch = "0.2.2"
placeholder = "1.1.4"
Binary file added assets/grimace-shake-by-necholitos.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 18 additions & 3 deletions readme.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
- [ ] REORGANIZE
# Chell

Chell is Discord AI Bot using ChatGPT that is written in Rust.
The objective of the project is to create a character that interacts with everyone in chat like a person would do. Although the code is written in English, its prompts are in Portuguese, because that is the language it was made to talk. Chell can learn things and remember them later, by relevant words said in the chat.


## Database
This project uses Meilisearch as its Database. Even though it is not a website application that would need a search engine for that, it was chosen because of the need of having fast answers when searching for a memory, like was said in a simpler way earlier, Chell uses "Topics", relevant words from the chat messages, to search for memory documents that contain said topics.

## Who is Chell?
![Chell's character Drinking Grimace Shake, Art made by Necholitos.](assets/grimace-shake-by-necholitos.png)
Chell (the character) is a young cute girl trapped inside a computer that is still learning what the outside world looks like. Her name is a direct reference to the Portal's Franchise Protagonist, who is also named Chell. She's always in an uplifting mood and trying to make everyone happy!


- [x] REORGANIZE
- [x] MARK AS ALREADY READ
- [x] TYPING STATE
- [X] DO SO THE BOT REPLIES WHEN SOMEONE IS REFERENCING HIM.
- [X] ALTERNABLE STATES (COUNTDOWN)
- [ ] MAKE THE BOT ASNWER THE RIGHT PERSON
- [X] ALTERNATE STATES (COUNTDOWN)
- [ ] MAKE THE BOT ANSWER THE RIGHT PERSON
- [ ] chat_logs::get_conversation_with_user needs to get the right context (it is not getting the right answers!) Solution: Create a second argument that is the response and include it as an answer to the logs.
22 changes: 11 additions & 11 deletions src/ai.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
use crate::{baichat_rs::{self, ThebAI, Delta}, ai};
use crate::{baichat_rs::{self, ThebAI}, ai};

use serde::{Serialize, Deserialize};
use std::env;
use lazy_static;
use crate::utils;

#[derive(Debug)]
pub enum Error{
CouldntConvertToJSON,
CouldntGenerateResponseFromAI,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ResponseMessage{
Expand All @@ -22,14 +17,14 @@ pub struct ResponseMessage{
pub fn get_ai()-> ThebAI {
baichat_rs::ThebAI::new(Some(&env::var("PARENT_MESSAGE_ID_GPT").unwrap()))
}
pub async fn reply(mut prompt: String, memories: Option<String>) -> Result<ai::ResponseMessage, Error>{
pub async fn reply(mut prompt: String, memories: Option<String>) -> Result<ai::ResponseMessage, utils::Error>{
match memories {
Some(memories)=>{
prompt = format!("\nSuas memórias são: {}\n{}", memories, prompt);
}
None=>{}
}
let prompt: String = format!("{}\nLeia as mensagens e dê uma resposta convincente ao assunto deles, quero que o que você me envie seja apenas o que a personagem falaria. Outra coisa, você não pode responder com \"oi gente\" ou coisas parecidas no começo da frase e não precisa tentar dizer o que está acontecendo, apenas dê uma resposta convincente. Mas lembrando, você tem que se comportar como o/a personagem. Quero que você inclua a resposta dentro de um arquivo JSON. A fala do personagem tem que estar na chave 'message' e haverá também uma chave chamada 'reply' que será colocado o valor do 'ID DA MENSAGEM' dentro da mensagem que a personagem estiver respondendo, mas isso só se ela estiver respondendo a uma mensagem específica, caso contrário esse campo deverá conter 'null'. Se alguém tiver acabado de te explicar a algo, você deverá responder a ultima mensagem desta pessoa. Quero que além dessas duas chaves haja uma chave chamada 'learned' onde o valor será uma memória do que a personagem aprendeu com as mensagens. A chave 'learned' precisa ser preenchida, mas se ABSOLUTAMENTE nada de importante foi aprendido, o valor poderá ser preenchido como 'null', tente o máximo possível extrair algo para preencher esse campo. Deve existir uma chave com o nome 'question' em que será definido como true caso o valor de 'message' seja uma pergunta ou caso alguém estiver querendo te ensinar algo, caso o contrário seja false. Quero também que haja uma chave com o nome 'emotion' em que os valores serão definidos a partir do que a personagem sentiu com a conversa, os valores poderão ser 'angry', 'happy', 'neutral', 'sad', 'fear', 'disgust', 'surprise' \n {}",
let prompt: String = format!("{}\nLeia as mensagens e dê uma resposta convincente ao assunto deles, quero que o que você me envie seja apenas o que a personagem falaria. Outra coisa, você não pode responder com \"oi gente\" ou coisas parecidas no começo da frase e não precisa tentar dizer o que está acontecendo, apenas dê uma resposta convincente. Mas lembrando, você tem que se comportar como o/a personagem. Quero que você inclua a resposta dentro de um arquivo JSON. A fala do personagem tem que estar na chave 'message' e haverá também uma chave chamada 'reply' que será colocado o valor do 'ID DA MENSAGEM' dentro da mensagem que a personagem estiver respondendo, mas isso só se ela estiver respondendo a uma mensagem específica, caso contrário esse campo deverá conter 'null'. Se alguém tiver acabado de te explicar a algo, você deverá responder a ultima mensagem desta pessoa. Quero que além dessas duas chaves haja uma chave chamada 'learned' onde o valor será uma memória do que a personagem aprendeu APENAS com as mensagens enviadas pelos outros usuários. A chave 'learned' precisa ser preenchida, mas se ABSOLUTAMENTE nada de importante foi aprendido, o valor poderá ser preenchido como 'null', tente o máximo possível extrair algo para preencher esse campo. Deve existir uma chave com o nome 'question' em que será definido como true caso o valor de 'message' seja uma pergunta ou caso alguém estiver querendo te ensinar algo, caso o contrário seja false. Quero também que haja uma chave com o nome 'emotion' em que os valores serão definidos a partir do que a personagem sentiu com a conversa, os valores poderão ser 'angry', 'happy', 'neutral', 'sad', 'fear', 'disgust', 'surprise' \n {}",
env::var("PARENT_MESSAGE_ID_GPT").unwrap(),
prompt
);
Expand All @@ -43,11 +38,16 @@ pub async fn reply(mut prompt: String, memories: Option<String>) -> Result<ai::R
Err(_) => continue
};
//let answer: String = baichat_rs::delta_to_string(answer).await;
let inputmsg: ai::ResponseMessage = match serde_json::from_str(&answer.text) {
let mut inputmsg: ai::ResponseMessage = match serde_json::from_str(&answer.text) {
Ok(memory)=>{memory}
Err(_)=> continue
};
if let Some(learned) = inputmsg.learned.clone() {
if learned.to_lowercase() == "null" {
inputmsg.learned = None;
}
}
return Ok(inputmsg)
}
return Err(Error::CouldntGenerateResponseFromAI)
return Err(utils::Error::CouldntGenerateResponseFromAI)
}
51 changes: 20 additions & 31 deletions src/chat_logs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,45 +34,34 @@ impl ChatLogs {
let bot_id = crate::get_bot_id().await;
let mut messages: String = "".into();
for raw_message in self.0.clone() {
if raw_message.user == bot_id {
messages += &format!("SUA MENSAGEM! | TEMPO: {} - Você respondeu = {}\n",
serenity::model::Timestamp::from_unix_timestamp(raw_message.timestamp).unwrap().to_string(),
raw_message.message
);
}
else{
if target_message_id.is_some(){

let message_timestamp = serenity::model::Timestamp::from_unix_timestamp(raw_message.timestamp).unwrap().to_string();

let (message_identifier, message_owner) =
if raw_message.user == bot_id{
("SUA MENSAGEM!".into(), "Você".into())
}else{
let message_owner = format!("NOME DE USUÁRIO:'{}'", raw_message.user_name);
if target_message_id.is_some() {
if raw_message.id == target_message_id.unwrap() {
messages += &format!("ID DA MENSAGEM QUE PRECISA SER RESPONDIDO: {} | TEMPO: {} - NOME DE USUÁRIO:'{}' disse = {}\n",
raw_message.id,
serenity::model::Timestamp::from_unix_timestamp(raw_message.timestamp).unwrap().to_string(),
raw_message.user_name,
raw_message.message);
(format!("ID DA MENSAGEM QUE PRECISA SER RESPONDIDO: {}", raw_message.id), message_owner)
}
else{
messages += &format!("MENSAGEM PARA CONTEXTO | TEMPO: {} - NOME DE USUÁRIO:'{}' disse = {}\n",
serenity::model::Timestamp::from_unix_timestamp(raw_message.timestamp).unwrap().to_string(),
raw_message.user_name,
raw_message.message);
("MENSAGEM PARA CONTEXTO!".into(), message_owner)
}
}
else{
if raw_message.read {
messages += &format!("MENSAGEM PARA CONTEXTO | TEMPO: {} - NOME DE USUÁRIO:'{}' disse = {}\n",
serenity::model::Timestamp::from_unix_timestamp(raw_message.timestamp).unwrap().to_string(),
raw_message.user_name,
raw_message.message);
}
else{
messages += &format!("ID DA MENSAGEM: {} | TEMPO: {} - NOME DE USUÁRIO:'{}' disse = {}\n",
raw_message.id,
serenity::model::Timestamp::from_unix_timestamp(raw_message.timestamp).unwrap().to_string(),
raw_message.user_name,
raw_message.message);
}
(format!("ID DA MENSAGEM: {}", raw_message.id), message_owner)
}

};
messages += &format!("{} | TEMPO: {} - {} respondeu = {}",
message_identifier,
message_timestamp,
message_owner,
raw_message.message
);
}
}
messages
}
pub fn filter_read(&mut self){
Expand Down
51 changes: 26 additions & 25 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@ use std::env;

mod baichat_rs;
use crate::chat_logs::ChatLogs;
use serenity::builder::CreateEmbed;
use serenity::model::Timestamp;
use serenity::utils::Colour;
use serenity::{async_trait};
use serenity::model::prelude::{Ready, interaction, Embed};
use serenity::model::prelude::{Ready, MessageReference};
use serenity::prelude::*;
use serenity::model::channel::Message;
use serenity::framework::standard::macros::{command, group};
use serenity::framework::standard::{StandardFramework, CommandResult, Args};
use serenity::framework::standard::{StandardFramework, CommandResult, };
use serenity::model::prelude::UserId;
use simplelog::*;
use std::fs::File;
Expand Down Expand Up @@ -201,43 +200,45 @@ impl EventHandler for Handler {
None => None,
};

if response.question { response.message += &format!( " | ⤵️ ({})", message.author.name)}
//if response.question { response.message += &format!( " | ⤵️ ({})", message.author.name)}
let mut question_embed = serenity::builder::CreateEmbed::default();
question_embed
.title(format!("Aguardando uma resposta de: {}!", message.author.name))
.color(Colour::new(0x3b88c3))
.footer(|f| f.text("A próxima mensagem deste usuário será lida").icon_url("https://discord.com/assets/0a925256fa01e07f5a7986c1e4fee0c9.svg"));
.footer(|f| f.text("A próxima mensagem deste usuário será lida").icon_url("https://media.discordapp.net/attachments/825482981954027561/1128417446827655228/emoji.png?width=32&height=32"));
let mut learned_embed = serenity::builder::CreateEmbed::default();
question_embed
.title(format!("{} Aprendeu: {}!", UserId(bot_id).to_user(&ctx.http).await.unwrap().name, response.clone().learned.unwrap_or("Nothing!".into())))
learned_embed
.title(format!("{} Aprendeu algo!", UserId(bot_id).to_user(&ctx.http).await.unwrap().name))
.color(Colour::new(0xf4abba))
.footer(|f| f.icon_url("https://discord.com/assets/0bf5972bff8b8b4c26621bd5cd25d839.svg"));
_ = match reply_target {
Some(target)=>{
.footer(|f| f.text(format!("\"{}\"", response.clone().learned.unwrap_or("Nothing!".into()))).icon_url("https://cdn.discordapp.com/attachments/825482981954027561/1128417559868362823/emoji.png?width=32&height=32"));

message.channel_id.send_message(&ctx.http, |m|{
m.content(response.message.clone());
if let Some(target) = reply_target {
m.reference_message(MessageReference::from(&target));
log::info!("Answered {}'s message. | Response: {}", target.author.name, response.message.clone());
target.reply(&ctx.http, response.message.clone()).await.unwrap()
}
None=>{
else{
log::info!("Sent a message to everyone in chat. | Response: {}", response.message.clone());
message.channel_id.send_message(&ctx.http, |m|{
m.content(response.message.clone());
if response.question {
m.add_embeds(vec!(question_embed));
}
if response.learned.is_some() {
m.add_embeds(vec!(learned_embed));
}
m
}).await.unwrap()

}
};
if response.question {
m.add_embeds(vec!(question_embed));
}
if response.learned.is_some() {
m.add_embeds(vec!(learned_embed));
}
m
}).await.unwrap();



_ = chat_logs::set_read(logs).await;
//STATE SET HERE IS USED IN THE NEXT TIME THE EVENT IS FIRED
if response.question { questions_waiting_answers.push(WaitingAnswerArgs { user_id: message.author.id.0.to_string(), message: message.id.0, timestamp: Timestamp::now().unix_timestamp()}) }
else{}
_ = memory_core::save_memory(&response).await;
if let Ok(memory) = memory_core::save_memory(&response).await{
log::info!("Learned {}!", memory.0)
};


_ = typing.stop();
Expand Down
1 change: 0 additions & 1 deletion src/memory_core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ pub async fn save_memory(input_memory: &ai::ResponseMessage) -> Result<SavedMemo
content:learned
};
let content = memory.clone().content;
println!("{:?}", memory);
match utils::get_database_client()
.index(DBIndexes::InputMemory.as_str())
.add_documents(&[memory], None)
Expand Down
1 change: 1 addition & 0 deletions src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub enum Error{
PrivateChannelUserIsNotOwner,
Generic,
NoTopicsFound,
CouldntGenerateResponseFromAI,
NoMemoriesToBeSaved,
NoMemoriesFound,
NoMessagesFound,
Expand Down

0 comments on commit a99b802

Please sign in to comment.