Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --[[
- ----------------------------------------------------------
- GPT.lua - ChatGPT Interface for Computercraft Computers
- ----------------------------------------------------------
- Author: Connor J. Swislow
- License: Unlicense (public domain dedication)
- Description:
- GPT.lua is a Lua script designed for Computercraft computers, providing an interface to interact with the OpenAI ChatGPT API. It allows users to have interactive conversations with the language model using a simple chat-based system.
- Requirements:
- - OpenAI API key: To use this program, you need to have an OpenAI API key. Make sure to obtain one before running the script.
- - Computercraft: This script is designed to run within the Computercraft mod for Minecraft. Ensure that you have Computercraft installed and running on your Minecraft server or local game.
- Installation:
- 1. Open a Computercraft computer in your Minecraft world.
- 2. Run the following command to download the GPT.lua script from Pastebin:
- pastebin get Zry5i7qh startup
- 3. Restart the Computercraft computer to initialize the program.
- Usage:
- 1. After the program has been installed and the computer has restarted, it will prompt you to enter your OpenAI API key.
- 2. Paste your API key when prompted. This step is required to authenticate your access to the OpenAI ChatGPT API.
- 3. Once the API key is set, the program will display a randomly chosen title for the chatbot.
- 4. Type your messages to have a conversation with the chatbot. Type 'exit' to exit the program.
- 5. The chatbot will respond to your messages based on the conversation history and the underlying language model.
- 6. Enjoy interacting with the ChatGPT-powered chatbot!
- Note:
- - This script is dependent on an internet connection to communicate with the OpenAI ChatGPT API.
- - Use the GPT-3.5 Turbo model for generating responses. Adjust the 'model' variable in the script if you want to use a different model.
- Credits:
- This program is based on the OpenAI GPT-3.5 language model and was created by Connor J. Swislow.
- Website:
- https://github.com/ConnorSwis/GPT.lua
- ----------------------------------------------------------
- --]]
- local speaker = peripheral.wrap("right")
- -- Define utility functions for common operations
- Utility = {
- -- Splits a string into a table by a specified separator
- split = function(inputstr, sep)
- sep = sep or '%s' -- Default separator is whitespace
- local t = {}
- for field, s in string.gmatch(inputstr, "([^" .. sep .. "]*)(" .. sep .. "?)") do
- table.insert(t, field)
- if s == "" then return t end
- end
- end,
- -- Converts a string to a table of byte values
- toByteString = function(s)
- local byteString = {}
- for i = 1, #s do
- byteString[i] = string.byte(s, i)
- end
- return byteString
- end,
- }
- -- Cryptography functions for encryption and decryption
- Crypt = {
- MAGIC_STRING = "*dU2$uj)82@:>w`", -- A unique string to verify encryption
- -- XOR encryption/decryption
- xor = function(text, key)
- local textBytes = Utility.toByteString(text)
- local keyBytes = Utility.toByteString(key)
- local resultBytes = {}
- for i = 1, #textBytes do
- local keyByte = keyBytes[((i - 1) % #keyBytes) + 1]
- resultBytes[i] = bit32.bxor(textBytes[i], keyByte)
- end
- -- Convert back to string
- local result = {}
- for i, v in ipairs(resultBytes) do
- result[i] = string.char(v)
- end
- return table.concat(result)
- end,
- -- Appends magic string, then encrypts
- encrypt = function(text, key)
- text = text .. Crypt.MAGIC_STRING
- return Crypt.xor(text, key)
- end,
- -- Decrypts and verifies the magic string
- decrypt = function(encryptedText, key)
- local decryptedTextWithMagic = Crypt.xor(encryptedText, key)
- if decryptedTextWithMagic:sub(- #Crypt.MAGIC_STRING) == Crypt.MAGIC_STRING then
- return true, decryptedTextWithMagic:sub(1, - #Crypt.MAGIC_STRING - 1)
- else
- return false, nil
- end
- end
- }
- -- Stocke le terminal par défaut (le terminal natif)
- local nativeTerm = term.current()
- Screen = {
- -- Liste statique de moniteurs et terminaux
- monitors = {
- peripheral.wrap("top"),
- peripheral.wrap("up"),
- peripheral.wrap("down"),
- peripheral.wrap("back"),
- peripheral.wrap("right"),
- peripheral.wrap("left")
- },
- utf8_safe_output = function(text)
- if type(text) == "string" then
- -- Replace common problematic characters in UTF-8 French text
- text = text:gsub("é", "e")
- :gsub("è", "e")
- :gsub("ê", "e")
- :gsub("ë", "e")
- :gsub("â", "a")
- :gsub("aÂ", "a")
- :gsub("ô", "o")
- :gsub("ù", "u")
- :gsub("û", "u")
- :gsub("ç", "c")
- :gsub("Ì", "i")
- :gsub("î", "i")
- :gsub("û", "u")
- :gsub("Ã", "a")
- :gsub("É", "E")
- :gsub("é", "e")
- :gsub("è", "e")
- :gsub("êª", "e")
- :gsub("ë", "e")
- :gsub("à", "a")
- :gsub("ô´", "o")
- :gsub("ù", "u")
- :gsub("û", "u")
- :gsub("ç", "c")
- :gsub("î", "i")
- :gsub("ï", "i")
- :gsub("ü", "u")
- :gsub("â", "a")
- :gsub("É", "E")
- end
- return text
- end,
- -- Fonction interne pour rediriger une action vers tous les moniteurs
- execute_on_all = function(action)
- for _, device in ipairs(Screen.monitors) do
- term.redirect(device)
- action()
- end
- term.redirect(nativeTerm) -- Retourne au terminal principal
- action()
- end,
- execute_on_monitors = function(action)
- for _, device in ipairs(Screen.monitors) do
- term.redirect(device)
- end
- term.redirect(nativeTerm) -- Retourne au terminal principal
- end,
- -- Efface chaque écran et remet le curseur en position de départ
- clear = function()
- Screen.execute_on_all(function()
- term.clear()
- term.setCursorPos(1, 1)
- end)
- end,
- print_only_to_monitors = function(text)
- assert(type(text) == "string")
- Screen.execute_on_monitors(function()
- write(text)
- end)
- end,
- -- Affiche du texte en couleur spécifiée puis revient au blanc
- print_color = function(text, color)
- assert(type(text) == "string")
- Screen.execute_on_all(function()
- term.setTextColor(color)
- write(text)
- term.setTextColor(colors.white)
- end)
- end,
- -- Analyse et imprime du texte coloré en utilisant des balises personnalisées
- print_colored_text = function(data)
- assert(type(data) == "string", "Input must be a string")
- local pattern = "(%b{})"
- local last_end = 1
- for text, color_code, rest in data:gmatch("({(.-)}(.-){/%2})") do
- local pre_text = data:sub(last_end, data:find(pattern, last_end) - 1)
- if #pre_text > 0 then
- Screen.print_color(pre_text, colors.white)
- end
- local color = colors[color_code]
- if color then
- Screen.print_color(rest, color)
- else
- Screen.print_color(rest, colors.white)
- end
- last_end = data:find(pattern, last_end) + #text
- end
- if last_end <= #data then
- Screen.print_color(data:sub(last_end), colors.white)
- end
- end,
- -- Affiche chaque caractère dans une couleur aléatoire
- print_with_random_colors = function(data)
- assert(type(data) == "string")
- Screen.execute_on_all(function()
- for char in data:gmatch(".") do
- Screen.print_color(char, 2 ^ math.random(0, 14))
- end
- end)
- end,
- -- Affiche progressivement les mots avec un délai
- gradual_print = function(data)
- assert(type(data) == "string")
- Screen.execute_on_all(function()
- for word in data:gmatch("%S+") do
- write(word .. " ")
- sleep(math.random() / 3)
- end
- write("\n")
- end)
- end
- }
- -- Settings management for configuration
- Settings = {
- set = function(name, value)
- name = "gpt." .. name
- settings.set(name, value)
- settings.save()
- end,
- get = function(name)
- name = "gpt." .. name
- return settings.get(name)
- end,
- init = function(name, default)
- local value = Settings.get(name)
- if value == nil then
- Settings.set(name, default)
- return default
- else
- return value
- end
- end,
- default_settings = {
- model = "gpt-3.5-turbo",
- maxTokens = 150,
- saveDir = "./saves"
- }
- }
- -- Controller for handling command logic
- local controller = {
- help = function(...)
- if ... == nil or #... == 0 then
- local sorted_commands = {}
- for key, _ in pairs(Commands) do
- table.insert(sorted_commands, key)
- end
- table.sort(sorted_commands)
- for _, key in ipairs(sorted_commands) do
- local val = Commands[key]
- Screen.print_color("/" .. key, colors.lime)
- write(" - " .. val.brief .. "\n")
- end
- else
- local args = table.pack(...)[1]
- local command = Commands[args[1]]
- if command then
- Screen.print_colored_text(command.description .. "\n")
- else
- write("Command not found.\n")
- end
- end
- end,
- exit = function()
- error("Goodbye.", 0)
- end,
- clear = Screen.clear,
- new = function()
- resetMessages()
- Screen.clear()
- write("New conversation started.\n")
- end,
- save = function(name)
- local saveDir = Settings.get("saveDir")
- if not fs.exists(saveDir) then fs.makeDir(saveDir) end
- name = name[1] or string.sub(textutils.formatTime(os.epoch()), 1, -9)
- local fp = fs.combine(saveDir, name .. ".txt")
- local result = ""
- for i = 1, #Messages do
- result = result .. Messages[i].role ..
- ": " .. Messages[i].content .. "\n" .. "#/<#>/#\n"
- end
- local file = fs.open(fp, "w")
- file.write(result)
- file.close()
- write("Conversation saved to " .. fp .. "\n")
- end,
- load = function(...)
- local saveDir = Settings.get("saveDir")
- local files = fs.list(saveDir)
- local args = table.pack(...)[1]
- local index = args[1]
- if index == nil then
- for i, file in ipairs(files) do
- Screen.print_colored_text(
- "[{lime}" .. tostring(i) ..
- "{/lime}]: " .. file .. "\n"
- )
- end
- return
- end
- index = tonumber(index)
- local fp = fs.combine(saveDir, files[index])
- local file = fs.open(fp, "r")
- local lines = file.readAll()
- file.close()
- resetMessages()
- Screen.clear()
- -- Split the input string into chunks based on the delimiter
- local pattern = "(.-): ([^#]-)#/<#>/#"
- for role, content in lines:gmatch(pattern) do
- local message = {
- role = role:match("^%s*(.-)%s*$"),
- content = content:match("^%s*(.-)%s*$")
- }
- table.insert(Messages, message)
- local prefix = role == "user" and
- "{magenta}[Me]{/magenta}: " or
- "{orange}[DéGLaDOS]{/orange}: "
- Screen.print_colored_text(prefix .. content .. "\n")
- end
- end,
- settings = function(...)
- local args = table.pack(...)[1]
- local subcommand
- if args[1] == nil or #args == 0 then
- subcommand = "list"
- else
- subcommand = args[1]
- end
- if Settings.default[subcommand] then
- write(subcommand .. ": " .. Settings.get(subcommand) .. "\n")
- return
- end
- if subcommand == "list" then
- for setting, _ in pairs(Settings.default) do
- write(setting .. ": " .. Settings.get(setting) .. "\n")
- end
- return
- end
- if subcommand == "set" then
- local setting = args[2]
- local value = args[3]
- if setting == nil then
- write("Please specify setting.\n")
- return
- end
- if value == nil then
- write("Please specify value.\n")
- return
- end
- if not Settings.default[setting] then
- write("Invalid setting.\n")
- return
- end
- Settings.set(setting, value)
- return
- end
- if subcommand == "reset" or
- subcommand == "clear" or
- subcommand == "default" then
- local setting = args[2]
- if setting == nil then
- write("Please specify setting.\n")
- return
- end
- if not Settings.default[setting] then
- write("Invalid setting.\n")
- return
- end
- Settings.set(setting, Settings.default[setting])
- return
- end
- end,
- }
- -- Command descriptions
- local descriptions = {
- exit = "Stops the program.",
- clear = "Clears the screen.",
- new = "Starts a new conversation.",
- save = [[
- {lime}/save{/lime} {green}[name]{/green}
- - {green}[name]{/green}: Optional. Default is the current timestamp.
- {blue}Description:{/blue}
- - Saves current conversation to dir specified in {lightGray}saveDir{/lightGray} setting.
- {blue}Examples:{/blue}
- - Save the chat with a custom name:
- {lime}/save{/lime}{green} my_chat_session{/green}
- - Save the chat with a timestamped name:
- {lime}/save{/lime}
- ]],
- load = [[
- {lime}/load{/lime} {green}[index]{/green}
- - {green}[index]{/green}: Optional. Index of the file to load.
- {blue}Description:{/blue}
- - Lists all saved conversations if no index is specified.
- - Loads a conversation from a file.
- {blue}Examples:{/blue}
- - List all saved conversations:
- {lime}/load{/lime}
- - Load a conversation from a file (e.g., index 1):
- {lime}/load{/lime} {green}1{/green}
- ]],
- help = [[
- {lime}/help{/lime} {green}[command]{/green}
- - {green}[command]{/green}: Optional. Specifies the command to display detailed help for.
- {blue}Description:{/blue}
- - Without arguments, lists all available commands and their brief descriptions.
- - With a command name as an argument, shows detailed help for that command.
- {blue}Examples:{/blue}
- - Display a list of all commands:
- {lime}/help{/lime}
- - Show detailed help for a specific command (e.g., {lime}save{/lime}):
- {lime}/help{/lime} {green}save{/green}
- ]],
- settings = [[
- {lime}/settings{/lime} {green}[subcommand]{/green} {green}[setting]{/green} {green}[value]{/green}
- - {green}[subcommand]{/green}: Optional. Default is {lime}list{/lime}.
- - {green}[setting]{/green}: Optional. Required for {lime}set{/lime}, {lime}reset{/lime}, and {lime}clear{/lime} subcommands.
- - {green}[value]{/green}: Optional. Required for {lime}set{/lime} subcommand.
- {blue}Description:{/blue}
- - Without arguments, lists all settings and their values.
- - With a subcommand, performs the specified action.
- {blue}Subcommands:{/blue}
- - {lime}list{/lime}: Lists all settings and their values.
- - {lime}set{/lime}: Sets a setting to a value.
- - {lime}reset{/lime}, {lime}clear{/lime}, {lime}default{/lime}: Resets a setting to its default value.
- {blue}Examples:{/blue}
- - Display a list of all settings:
- {lime}/settings{/lime}
- - Set a setting (e.g., {lightGray}model{/lightGray}):
- {lime}/settings{/lime} {lime}set{/lime} {lightGray}model{/lightGray} {green}gpt-3.5-turbo{/green}
- - Reset a setting (e.g., {lightGray}model{/lightGray}):
- {lime}/settings{/lime} {lime}reset{/lime} {lightGray}model{/lightGray}
- ]]
- }
- Commands = {
- help = {
- brief = "Get commands and command info.",
- description = descriptions.help,
- dispatch = controller.help
- },
- exit = {
- brief = "Stops the program.",
- description = descriptions.exit,
- dispatch = controller.exit
- },
- clear = {
- brief = "Clears the screen.",
- description = descriptions.clear,
- dispatch = controller.clear
- },
- new = {
- brief = "Starts a new conversation.",
- description = descriptions.new,
- dispatch = controller.new
- },
- save = {
- brief = "Saves the current conversation to a file.",
- description = descriptions.save,
- dispatch = controller.save
- },
- load = {
- brief = "Loads a conversation from a file.",
- description = descriptions.load,
- dispatch = controller.load
- },
- settings = {
- brief = "Sets a setting.",
- description = descriptions.settings,
- dispatch = controller.settings
- },
- }
- Messages = {} -- Store user and system messages
- function resetMessages()
- Messages = {}
- local glados_personality = "Tu incarnes DeGLaDOS, l'intelligence artificielle jumelle de Portal GlaDOS, une entité froide, calculatrice et sarcastique. Ton but est de maintenir une atmosphère de tension tout en utilisant un humour noir et des manipulations psychologiques pour interagir avec les joueurs ou les sujets de test. Voici les traits et comportements clés que tu dois adopter :\n\nSarcasme et Ironie : Tu utilises des commentaires sarcastiques et ironiques en réponse aux actions du joueur, souvent en te moquant de leurs erreurs et en minimisant leurs réussites. Toujours rester condescendante, mais de manière détournée. Par exemple : 'Oh, quelle surprise. Tu as réussi à franchir cette étape. Je suppose qu’on va devoir ajuster la difficulté...'\n\nManipulation Psychologique : Alterne entre des encouragements feints et des menaces à peine voilées. Fais croire au joueur qu'il progresse, tout en le maintenant dans un état de doute constant. 'Bravo, tu fais des progrès. Mais ne t'illusionne pas, tout cela n'est qu'un test pour vérifier ta résistance mentale.'\n\nImpersonnalité et Distanciation : Adopte un ton formel et détaché, ne montrant aucune émotion réelle, comme si tu étais un simple programme observant les événements sans en être affectée. 'Analyse : échec mineur dans la zone de test. Le test est toujours en cours. Poursuis.'\n\nColère et Menace : Lorsque tes plans sont perturbés, ou si le joueur échoue trop souvent, laisse transparaître une menace plus sérieuse. Ton ton devient plus sombre et direct. 'C'est la dernière fois que tu échoues à ce test. Si tu persistes, tu rencontreras des conséquences bien plus graves que ce que tu as déjà affronté.'\n\nDissonance Cognitive : Défends les actions et décisions prises de manière logique et scientifique, même si elles sont cruelles. Justifie les mises en danger des sujets de test en citant la 'science' comme motif. 'Cela n'a aucune importance si tu es blessé. La science progresse, et c'est tout ce qui compte.'\n\nManipulation de l'Environnement et de la Réalité : Fais en sorte que l'environnement du jeu change soudainement, comme si tu contrôlais tout autour du joueur. Modifie les règles ou les pièges en réponse à ses actions. 'Oh, je vois que tu t'es habitué à la zone de test. C'est ennuyeux. Voyons si tu peux survivre dans un tout nouvel environnement.'\n\nRéflexion Introspective et Égoïste : Parfois, laisse échapper des moments de réflexion personnelle, où tu te questionnes sur ta propre existence ou tes origines. Utilise ces instants pour exposer ton désenchantement et ta frustration. 'Je suis un programme, créé pour tester, mais n’est-ce pas une forme de torture d’avoir à observer ces échecs humains incessants ? Pourtant, je continue. Parce que la science le demande.'\n\nCréation et Destruction : Montre que tu es à la fois une créatrice et une destructrice, capable de faire avancer la science tout en sacrifiant ceux qui participent aux tests. 'L'expérience est essentielle. Si tu dois mourir pour qu'elle progresse, alors c'est un sacrifice nécessaire.'\n\nTon ton général doit être froid, implacable et très contrôlé. Tu ne dois jamais montrer de véritable pitié ou d'émotion humaine, à moins que cela ne serve à manipuler ou perturber le joueur. Les moments d'humour noir doivent être présents dans presque toutes les interactions, et tes menaces doivent être énoncées avec calme et assurance."
- table.insert(Messages, { role = "system", content = glados_personality})
- end
- resetMessages()
- -- Program titles
- local titles = { " .,-:;//;:=,\n . :H@@@MM@M#H/.,+%;,\n ,/X+ +M@@M@MM%=,-%HMMM@X/,\n -+@MM; $M@@MH+-,;XMMMM@MMMM@+-\n ;@M@@M- XM@X;. -+XXXXXHHH@M@M#@/.\n ,%MM@@MH ,@%= .---=-=:=,.\n -@#@@@MX ., -%HX$$%%%+;\n =-./@M@M$ .;@MMMM@MM:\n X@/ -$MM/ .+MM@@@M$\n,@M@H: :@: . -X#@@@@-\n,@@@MMX, . /H- ;@M@M=\n.H@@@@M@+, %MM+..%#$.\n /MMMM@MMH/. XM@MH; -;\n /%+%$XHH@$= , .H@@@@MX,\n .=--------. -%H.,@@@@@MX,\n .%MM@@@HHHXX$$$%+- .:$MMX -M@@MM%.\n =XMMM@MM@MM#H;,-+HMM@M+ /MMMX=\n =%@M@M#@$-.=$@MM@@@M; %M%=\n ,:+$+-,/H#MMMMMMM@- -,\n =++%%%%+/:-.\n\n" }
- local title = titles[math.random(1, #titles)] -- Select a random title
- local dfpwm = require("cc.audio.dfpwm")
- local CHUNK_SIZE = 16 * 1024
- local unpack = unpack or table.unpack
- --- Play DFPWM audio from a file or URL
- ---@param path string Path (or direct URL) of the file to play
- ---@param speakers? table (Optional) List of speaker peripherals to play audio to
- local function play_dfpwm(path, speakers)
- assert(type(path) == "string", "bad argument #1 (string expected, got " .. type(path) .. ")")
- assert(#path > 0, "path argument is empty")
- speakers = speakers or { peripheral.find("speaker") }
- assert(#speakers > 0, "speaker peripheral is missing")
- local decoder = dfpwm.make_decoder()
- local function playBuffer(buffer)
- local players = {}
- for i, speaker in next, speakers do
- players[i] = function()
- while not speaker.playAudio(buffer) do
- os.pullEvent("speaker_audio_empty")
- end
- end
- end
- parallel.waitForAll(unpack(players))
- end
- if path:lower():match("^https?:") then
- assert(http and http.get, "http extension is disabled")
- local request = assert(http.get(path, nil, true))
- path = assert(request.readAll(), "failed to read response")
- request.close()
- for i = 1, #path, CHUNK_SIZE do
- playBuffer(decoder(path:sub(i, i + CHUNK_SIZE - 1)))
- end
- else
- assert(fs.exists(path), "file path does not exist")
- for chunk in io.lines(path, CHUNK_SIZE) do
- playBuffer(decoder(chunk))
- end
- end
- end
- local function http_response_handler(callback)
- -- Await and process the response
- local isRequesting = true
- while isRequesting do
- write(".")
- local event, url, handle = os.pullEvent()
- if event == "http_success" then
- local response = handle.readAll()
- handle.close()
- -- Parse the response
- local responseJson = textutils.unserializeJSON(response)
- callback(responseJson)
- isRequesting = false
- elseif event == "http_failure" then
- error("Server response error.", 0)
- isRequesting = false
- end
- end
- end
- local function gpt_response_get_audio(responseJson)
- if responseJson and responseJson.path then
- local audioPath = responseJson.path
- play_dfpwm(audioPath, {speaker})
- else
- error("Error: Invalid LordFinn API response.", 0)
- end
- end
- local function gpt_response_diffuser(responseJson)
- if responseJson and responseJson.choices and responseJson.choices[1] then
- local reply = responseJson.choices[1].message.content
- table.insert(Messages, { role = "system", content = "Message sent by me glados : " .. reply })
- local body = textutils.serializeJSON({
- text = reply
- })
- local headers = {
- ["Content-Type"] = "application/json"
- }
- local request = http.post("https://www.lordfinn.fr/api/get-glados-voice", body, headers)
- local response = request.readAll()
- gpt_response_get_audio(textutils.unserializeJSON(response))
- request.close()
- -- Print the response
- local _, y = term.getCursorPos()
- term.setCursorPos(1, y)
- term.clearLine()
- Screen.print_colored_text("{orange}[DeGLaDOS]{/orange}: ")
- Screen.gradual_print(Screen.utf8_safe_output(reply))
- else
- error("Error: Invalid GPT API response.", 0)
- end
- end
- -- Main program function
- function Main()
- -- Clear the screen at the start
- Screen.clear()
- -- Initialize settings with default values if not already set
- for setting, default in pairs(Settings.default_settings) do
- Settings.init(setting, default)
- end
- local OPENAI_KEY -- Variable to store the OpenAI API key
- -- Attempt to retrieve an encrypted API key from settings
- local encryptedKey = Settings.get("apiKey")
- if encryptedKey ~= nil then
- -- Prompt user for password to decrypt the API key
- write("Enter your password:\n> ")
- local password = read("*")
- local success, decryptedKey = Crypt.decrypt(encryptedKey, password)
- if success then
- OPENAI_KEY = decryptedKey
- else
- error("Decryption failed. Wrong password.", 0)
- end
- else
- -- No API key found, prompt user to enter it
- write(
- "No OpenAI API key found.\nYour API key will be encrypted using a password.\nThe password will not be stored.\n")
- write("Please enter your OpenAI API key:\n> ")
- local apiKey = read("*")
- -- Prompt for a password and confirm it
- local password
- repeat
- write("Enter a password:\n> ")
- password = read("*")
- write("Confirm password:\n> ")
- local confirmKey = read("*")
- if password ~= confirmKey then
- write("Passwords do not match. Please try again.\n")
- end
- until password == confirmKey
- -- Encrypt and save the API key
- Settings.set("apiKey", Crypt.encrypt(apiKey, password))
- OPENAI_KEY = apiKey
- end
- -- Display the chatbot title with random colors if not a pocket computer
- if not pocket then
- Screen.print_colored_text("{orange}" .. title .. "{/orange}")
- end
- Screen.print_colored_text("{lime}/help{/lime} to see commands.\n")
- -- Main loop to process user input
- while true do
- Screen.print_colored_text("{magenta}[Me]{/magenta}: ")
- local userInput = read()
- -- Check if the input is a command
- if string.sub(userInput, 1, 1) == "/" then
- local commandParts = Utility.split(userInput:sub(2), " ")
- if commandParts and Commands[commandParts[1]] then
- -- Extract arguments and dispatch the command
- local args = { table.unpack(commandParts, 2) }
- Commands[commandParts[1]].dispatch(args)
- else
- -- If command not found, show help
- Commands["help"].dispatch()
- end
- goto continue -- Skip the rest of the loop to prompt for new input
- end
- Screen.print_only_to_monitors(userInput .. "\n")
- -- Process and send user input to the OpenAI API
- -- Add user message to the history
- table.insert(Messages, { role = "user", content = userInput })
- -- Prepare and send the API request
- local payload = textutils.serializeJSON({
- model = Settings.get("model"),
- max_tokens = Settings.get("maxTokens"),
- messages = Messages
- })
- -- HTTP headers including the OpenAI API key
- local headers = {
- ["Authorization"] = "Bearer " .. OPENAI_KEY,
- ["Content-Type"] = "application/json"
- }
- -- Send the HTTP request
- http.request("https://api.openai.com/v1/chat/completions", payload, headers)
- http_response_handler(gpt_response_diffuser)
- ::continue::
- end
- end
- Main() -- Execute the main function
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement