206 lines
6.0 KiB
Go
206 lines
6.0 KiB
Go
package bot
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/bwmarrin/discordgo"
|
|
"github.com/charmbracelet/log"
|
|
openai "github.com/sashabaranov/go-openai"
|
|
)
|
|
|
|
const (
|
|
maxTokens = 2000
|
|
maxContextTokens = 4000
|
|
maxMessageTokens = 2000
|
|
systemMessageText = "your name is !bit you are a discord bot"
|
|
)
|
|
|
|
func populateConversationHistory(session *discordgo.Session, channelID string, conversationHistory []openai.ChatCompletionMessage) []openai.ChatCompletionMessage {
|
|
messages, err := session.ChannelMessages(channelID, 50, "", "", "")
|
|
if err != nil {
|
|
log.Error("Error retrieving channel history:", err)
|
|
return conversationHistory
|
|
}
|
|
|
|
totalTokens := 0
|
|
for _, msg := range conversationHistory {
|
|
totalTokens += len(msg.Content) + len(msg.Role) + 2
|
|
}
|
|
|
|
maxHistoryTokens := maxTokens - totalTokens
|
|
if maxHistoryTokens < 0 {
|
|
maxHistoryTokens = 0
|
|
}
|
|
|
|
// Iterate from the beginning of conversationHistory (oldest messages)
|
|
for i := 0; i < len(conversationHistory); i++ {
|
|
msg := conversationHistory[i]
|
|
tokens := len(msg.Content) + len(msg.Role) + 2 // Account for role and content tokens
|
|
|
|
if totalTokens-tokens >= maxHistoryTokens {
|
|
// Remove the oldest message
|
|
conversationHistory = conversationHistory[i+1:]
|
|
totalTokens -= tokens
|
|
break
|
|
}
|
|
}
|
|
|
|
// Add new messages from the channel
|
|
addedTokens := 0
|
|
for _, message := range messages {
|
|
if len(message.Content) > 0 {
|
|
tokens := len(message.Content) + 2 // Account for role and content tokens
|
|
if totalTokens+tokens <= maxContextTokens && addedTokens+tokens <= maxContextTokens {
|
|
conversationHistory = append(conversationHistory, openai.ChatCompletionMessage{
|
|
Role: openai.ChatMessageRoleUser,
|
|
Content: message.Content,
|
|
})
|
|
totalTokens += tokens
|
|
addedTokens += tokens
|
|
} else {
|
|
if totalTokens+tokens > maxContextTokens {
|
|
log.Warn("Message token count exceeds maxContextTokens:", len(message.Content), len(message.Content)+2)
|
|
} else {
|
|
log.Warn("Conversation history length exceeds maxContextTokens:", len(conversationHistory), maxHistoryTokens)
|
|
}
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
return conversationHistory
|
|
}
|
|
|
|
func chatGPT(session *discordgo.Session, channelID string, message string, conversationHistory []openai.ChatCompletionMessage) {
|
|
client := openai.NewClient(OpenAIToken)
|
|
|
|
// Trim conversation history if it exceeds maxContextTokens
|
|
// totalTokens := 0
|
|
// trimmedMessages := []openai.ChatCompletionMessage{}
|
|
|
|
// for i := len(conversationHistory) - 1; i >= 0; i-- {
|
|
// msg := conversationHistory[i]
|
|
// tokens := len(msg.Content) + len(msg.Role) + 2 // Account for role and content tokens
|
|
|
|
// if totalTokens+tokens <= maxContextTokens {
|
|
// trimmedMessages = append([]openai.ChatCompletionMessage{msg}, trimmedMessages...)
|
|
// totalTokens += tokens
|
|
// } else {
|
|
// break
|
|
// }
|
|
// }
|
|
|
|
// Update conversationHistory with trimmed conversation history
|
|
//conversationHistory = trimmedMessages
|
|
|
|
// Add user message to conversation history
|
|
//userMessage := openai.ChatCompletionMessage{
|
|
// Role: openai.ChatMessageRoleUser,
|
|
// Content: message,
|
|
//}
|
|
//conversationHistory = append(conversationHistory, userMessage)
|
|
|
|
// Perform GPT-4 completion
|
|
log.Info("Starting completion...", conversationHistory)
|
|
resp, err := client.CreateChatCompletion(
|
|
context.Background(),
|
|
openai.ChatCompletionRequest{
|
|
MaxTokens: maxTokens,
|
|
FrequencyPenalty: 0.3,
|
|
PresencePenalty: 0.6,
|
|
Model: openai.GPT3Dot5Turbo,
|
|
Messages: conversationHistory, // Use trimmed conversation history
|
|
},
|
|
)
|
|
log.Info("completion done.")
|
|
|
|
// ...
|
|
|
|
// Handle API errors
|
|
if err != nil {
|
|
log.Error("Error connecting to the OpenAI API:", err)
|
|
return
|
|
}
|
|
|
|
// Paginate the response and send as separate messages with clickable emojis
|
|
gptResponse := resp.Choices[0].Message.Content
|
|
pageSize := maxMessageTokens
|
|
|
|
// Split the response into pages
|
|
var pages []string
|
|
for i := 0; i < len(gptResponse); i += pageSize {
|
|
end := i + pageSize
|
|
if end > len(gptResponse) {
|
|
end = len(gptResponse)
|
|
}
|
|
pages = append(pages, gptResponse[i:end])
|
|
}
|
|
|
|
// ...
|
|
|
|
// Send the first page
|
|
currentPage := 0
|
|
totalPages := len(pages)
|
|
embed := &discordgo.MessageEmbed{
|
|
Title: fmt.Sprintf("BitBot's Response (Page %d of %d)", currentPage+1, totalPages),
|
|
Description: pages[currentPage],
|
|
Color: 0x00ff00, // Green color
|
|
}
|
|
msg, err := session.ChannelMessageSendEmbed(channelID, embed)
|
|
if err != nil {
|
|
log.Error("Error sending embed message:", err)
|
|
return
|
|
}
|
|
|
|
// Add reaction emojis for pagination if there are multiple pages
|
|
if totalPages > 1 { // Only add reactions if there are multiple pages
|
|
err = session.MessageReactionAdd(channelID, msg.ID, "⬅️")
|
|
if err != nil {
|
|
log.Error("Error adding reaction emoji:", err)
|
|
return
|
|
}
|
|
err = session.MessageReactionAdd(channelID, msg.ID, "➡️")
|
|
if err != nil {
|
|
log.Error("Error adding reaction emoji:", err)
|
|
return
|
|
}
|
|
}
|
|
|
|
// Create a reaction handler function
|
|
session.AddHandler(func(s *discordgo.Session, r *discordgo.MessageReactionAdd) {
|
|
// Call the reactionHandler function and pass totalPages
|
|
reactionHandler(s, r, currentPage, msg, pages, totalPages)
|
|
})
|
|
|
|
}
|
|
|
|
func reactionHandler(session *discordgo.Session, r *discordgo.MessageReactionAdd, currentPage int, msg *discordgo.Message, pages []string, totalPages int) {
|
|
// Check if the reaction is from the same user and message
|
|
if r.UserID == session.State.User.ID || r.MessageID != msg.ID {
|
|
return
|
|
}
|
|
|
|
// Handle pagination based on reaction
|
|
if r.Emoji.Name == "⬅️" {
|
|
if currentPage > 0 {
|
|
currentPage--
|
|
}
|
|
} else if r.Emoji.Name == "➡️" {
|
|
if currentPage < len(pages)-1 {
|
|
currentPage++
|
|
}
|
|
}
|
|
|
|
// Update the message with the new page
|
|
updatedEmbed := &discordgo.MessageEmbed{
|
|
Title: fmt.Sprintf("BitBot's Response (Page %d of %d)", currentPage+1, len(pages)),
|
|
Description: pages[currentPage],
|
|
Color: 0x00ff00, // Green color
|
|
}
|
|
_, err := session.ChannelMessageEditEmbed(r.ChannelID, r.MessageID, updatedEmbed)
|
|
if err != nil {
|
|
log.Error("Error editing embed message:", err)
|
|
}
|
|
}
|