Skip to content

Commit

Permalink
Improved AutoChatGame module
Browse files Browse the repository at this point in the history
Now buffers every message after the trigger word and combines it into one question. This allows to support more types of questions.
It also does not react to user messages anymore.
Also improved prompt a lot. It is now more precise.
  • Loading branch information
1zun4 committed Oct 19, 2023
1 parent 959d70a commit c2f7907
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,20 @@ public class MixinMessageHandler {

@Inject(method = "method_45745", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/hud/ChatHud;addMessage(Lnet/minecraft/text/Text;)V"))
private void injectDisguisedChatLambda(MessageType.Parameters parameters, Text text, Instant instant, CallbackInfoReturnable<Boolean> cir) {
emitChatEvent(text);
emitChatEvent(text, ChatReceiveEvent.ChatType.DISGUISED_CHAT_MESSAGE);
}

@Inject(method = "processChatMessageInternal", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/hud/ChatHud;addMessage(Lnet/minecraft/text/Text;Lnet/minecraft/network/message/MessageSignatureData;Lnet/minecraft/client/gui/hud/MessageIndicator;)V"))
private void injectChatMessage(MessageType.Parameters params, SignedMessage message, Text decorated, GameProfile sender, boolean onlyShowSecureChat, Instant receptionTimestamp, CallbackInfoReturnable<Boolean> cir) {
emitChatEvent(decorated);
emitChatEvent(decorated, ChatReceiveEvent.ChatType.CHAT_MESSAGE);
}

@Inject(method = "onGameMessage", at = @At(value = "HEAD"))
private void injectGameMessage(Text message, boolean overlay, CallbackInfo ci) {
emitChatEvent(message);
emitChatEvent(message, ChatReceiveEvent.ChatType.GAME_MESSAGE);
}

private void emitChatEvent(Text text) {
EventManager.INSTANCE.callEvent(new ChatReceiveEvent(text.getString(), text));
private void emitChatEvent(Text text, ChatReceiveEvent.ChatType type) {
EventManager.INSTANCE.callEvent(new ChatReceiveEvent(text.getString(), text, type));
}
}
8 changes: 7 additions & 1 deletion src/main/kotlin/net/ccbluex/liquidbounce/event/Events.kt
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,13 @@ class ScreenEvent(val screen: Screen?) : CancellableEvent()
class ChatSendEvent(val message: String) : CancellableEvent()

@Nameable("chatReceive")
class ChatReceiveEvent(val message: String, val textData: Text) : Event()
class ChatReceiveEvent(val message: String, val textData: Text, val type: ChatType) : Event() {

enum class ChatType {
CHAT_MESSAGE, DISGUISED_CHAT_MESSAGE, GAME_MESSAGE
}

}

@Nameable("useCooldown")
class UseCooldownEvent(var cooldown: Int) : Event()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import kotlinx.coroutines.delay
import net.ccbluex.liquidbounce.event.*
import net.ccbluex.liquidbounce.features.module.Category
import net.ccbluex.liquidbounce.features.module.Module
import net.ccbluex.liquidbounce.utils.client.Chronometer
import net.ccbluex.liquidbounce.utils.client.chat
import net.ccbluex.liquidbounce.utils.openai.Gpt
import kotlin.concurrent.thread
Expand All @@ -36,11 +37,9 @@ object ModuleAutoChatGame : Module("AutoChatGame", Category.MISC) {

private val openAiKey by text("OpenAiKey", "")
private val delayResponse by intRange("ReactionTime", 1000..5000, 0..10000)
private val triggerSentences by textArray("TriggerSentences", mutableListOf(
"The first to",
"True or False"
))

private val cooldownMinutes by int("Cooldown", 2, 0..60)
private val bufferTime by int("BufferTime", 200, 0..500)
private val triggerSentence by text("TriggerSentence", "Chat Game")
private val serverName by text("ServerName", "Minecraft")

/**
Expand All @@ -56,10 +55,13 @@ object ModuleAutoChatGame : Module("AutoChatGame", Category.MISC) {
You participate in a chat game in which you have to answer questions.
The goal is to answer them as short and precise as possible.
It mostly only requires one word as an answer or if it's a math question it requires the result.
The questions might be based on the game Minecraft or the server you are playing on.
The questions might be based on the game Minecraft and the server you are playing on.
Always answer with the first thing that comes to your mind.
The server name is {SERVER_NAME}.
On true or false questions, respond without any dots, in lower-case with 'true' or 'false'.
If you do not know it, just guess with something that fits.
DO NOT PUT ANYTHING IN THE FRONT OF YOUR ANSWER!
DO NOT PUT ANY DOTS AT THE END OF YOUR ANSWER!
DO NOT SAY ANYTHING ELSE THAN THE ANSWER! If you do, you will be disqualified.
""".trimIndent().replace("\n", " ")
private val prompt by text("Prompt", defaultPrompt)
Expand All @@ -72,9 +74,18 @@ object ModuleAutoChatGame : Module("AutoChatGame", Category.MISC) {
}
}

private val chatBuffer = mutableListOf<String>()
private val triggerWordChronometer = Chronometer()
private val cooldownChronometer = Chronometer()

val chatHandler = sequenceHandler<ChatReceiveEvent> { event ->
val message = event.message

// Only handle game messages. It is unlikely that any server will use a player for the chat game.
if (event.type != ChatReceiveEvent.ChatType.GAME_MESSAGE) {
return@sequenceHandler
}

// Auto GG
if (message.contains("Show some love by typing")) {
delay(delayResponse.random().toLong())
Expand All @@ -83,36 +94,68 @@ object ModuleAutoChatGame : Module("AutoChatGame", Category.MISC) {
return@sequenceHandler
}

// Handle questions
val question = triggerSentences.firstOrNull { message.contains(it) }
?.let { message.substring(message.indexOf(it)) } ?: return@sequenceHandler
// Trigger word checking. Cooldown prevents the bot from answering the question twice if the result has the same tag.
if (cooldownChronometer.hasElapsed(cooldownMinutes * 60000L)) {
// Does the message contain the magic trigger word?
if (message.contains(triggerSentence)) {
triggerWordChronometer.reset()

// TODO: Add option for servers that have the trigger word and question in the same message.
// Do not include the trigger word in the buffer.
chatBuffer.clear()
return@sequenceHandler
}

// If the trigger word has been said, add the message to the buffer.
if (!triggerWordChronometer.hasElapsed(bufferTime.toLong())) {
chatBuffer.add(message)
}
} else {
chatBuffer.clear()
}
}

val repeatable = repeatable {
// Has the trigger word been said and has the buffer time elapsed?
if (triggerWordChronometer.hasElapsed(bufferTime.toLong())) {
// Is the buffer empty? - If it is we already answered the question.
if (chatBuffer.isEmpty()) {
return@repeatable
}

chat("§aUnderstood question: $question")
// Handle questions
var question = chatBuffer.joinToString(" ")
chatBuffer.clear()
cooldownChronometer.reset()

// Create new AI instance with OpenAI key
val ai = Gpt(openAiKey, prompt.replace("{SERVER_NAME}", serverName))
// Remove double spaces
while (question.contains(" ")) {
question = question.replace(" ", " ")
}

thread {
runCatching {
val startAsk = System.currentTimeMillis()
val answer = ai.requestNewAnswer(question)
chat("§aAnswer: $answer, took ${System.currentTimeMillis() - startAsk}ms.")
chat("§aUnderstood question: $question")

val delay = delayResponse.random()
chat("§aAnswering question: $answer, waiting for ${delay}ms.")
// Create new AI instance with OpenAI key
val ai = Gpt(openAiKey, prompt.replace("{SERVER_NAME}", serverName))

val startDelay = System.currentTimeMillis()
Thread.sleep(delay.toLong())
thread {
runCatching {
val startAsk = System.currentTimeMillis()
val answer = ai.requestNewAnswer(question)
chat("§aAnswer: $answer, took ${System.currentTimeMillis() - startAsk}ms.")

// Send answer
network.sendChatMessage(answer)
chat("§aAnswered question: $answer, waited for ${System.currentTimeMillis() - startDelay}ms.")
}.onFailure {
it.printStackTrace()
val delay = delayResponse.random()
chat("§aAnswering question: $answer, waiting for ${delay}ms.")

Thread.sleep(delay.toLong())

// Send answer
network.sendChatMessage(answer)
}.onFailure {
chat("§cFailed to answer question: ${it.message}")
}
}
}
}



}

0 comments on commit c2f7907

Please sign in to comment.