commit f14ac03059cf2b8aa064699120439e037791293b Author: Dennis Heinrich Date: Thu May 9 21:45:10 2024 +0200 First commit for the project diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5222404 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +# Ignore VSCode settings +.vscode + +# Ignore Node.js modules and build output +/node/node_modules +/node/dist + +# Ignore everything except save files +/roms/* +!/roms/*.sav \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..ee4e068 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,32 @@ +# BSD 3-Clause License + +Copyright (c) 2024, Dennis Heinrich + + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +4. Links and references to the original author's website may not be removed from the files. + It is not allowed to change it in any ways, nor to hide it. Links must be clearly visible. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..28d502b --- /dev/null +++ b/README.md @@ -0,0 +1,45 @@ +# TikTok mGBA Emulator + +This project is an implementation for controlling a GameBoy Emulator with the TikTok Chat. Users can control the game by sending commands to the chat. The commands are parsed and sent to the mGBA Emulator via HTTP-API. + +## Requirements + +- .NET Runtime +- .NET ASP Core Framework +- NodeJS with `npm` or `pnpm` +- Runnable on Windows, Linux and macOS + +## Instructions + +For running the project, you need to start the following both services: + +1. NodeJS Server +2. mGBA with HTTP-API + +### NodeJS Server + +1. Install NodeJS +2. Install the dependencies with `npm install` or `pnpm install` +3. Run the server with `npm start` or `pnpm start` + +### mGBA with HTTP-API + +1. Download mGBA +2. Download mGBA-http (exe & lua) +3. Run mGBA-HTTP Server from `http/server/mGBA-http-server.exe` +4. Run mGBA and press "Scripting..." +5. Load the LUA file from `http/script/mGBA-socket-server.lua` +6. Load a rom into mGBA and start the game + +## Documentation + +When mGBA-http is running, it provides an user interface for Swagger. Though it provides +a JSON for implementing the API by using a code generator. + +- +- + +## Resources + +- +- diff --git a/http/script/mGBA-socket-server.lua b/http/script/mGBA-socket-server.lua new file mode 100644 index 0000000..b24d9cd --- /dev/null +++ b/http/script/mGBA-socket-server.lua @@ -0,0 +1,322 @@ +-- *********************** +-- mGBA-http +-- Version: 0.2.0 +-- Lua interface for mGBA-http +-- https://github.com/nikouu/mGBA-http +-- *********************** + +local enableLogging = true + +-- *********************** +-- Sockets +-- *********************** + +local server = nil +local socketList = {} +local nextID = 1 +local port = 8888 + +function beginSocket() + while not server do + server, error = socket.bind(nil, port) + if error then + if error == socket.ERRORS.ADDRESS_IN_USE then + port = port + 1 + else + console:error(formatMessage("Bind", error, true)) + break + end + else + local ok + ok, error = server:listen() + if error then + server:close() + console:error(formatMessage("Listen", error, true)) + else + console:log("Socket Server: Listening on port " .. port) + server:add("received", socketAccept) + end + end + end +end + +function socketAccept() + local sock, error = server:accept() + if error then + console:error(formatMessage("Accept", error, true)) + return + end + local id = nextID + nextID = id + 1 + socketList[id] = sock + sock:add("received", function() socketReceived(id) end) + sock:add("error", function() socketError(id) end) + formattedLog(formatMessage(id, "Connected")) +end + +function socketReceived(id) + local sock = socketList[id] + if not sock then return end + while true do + local message, error = sock:receive(1024) + if message then + -- it seems that the value must be non-empty in order to actually send back? + -- thus the ACK message default + local returnValue = messageRouter(message:match("^(.-)%s*$")) + sock:send(returnValue) + elseif error then + -- seems to go into this SOCKETERRORAGAIN state for each call, but it seems fine. + if error ~= socket.ERRORS.AGAIN then + formattedLog("socketReceived 4") + console:error(formatMessage(id, error, true)) + socketStop(id) + end + return + end + end +end + +function socketStop(id) + local sock = socketList[id] + socketList[id] = nil + sock:close() +end + +function socketError(id, error) + console:error(formatMessage(id, error, true)) + socketStop(id) +end + +function formatMessage(id, msg, isError) + local prefix = "Socket " .. id + if isError then + prefix = prefix .. " Error: " + else + prefix = prefix .. " Received: " + end + return prefix .. msg +end + +-- *********************** +-- Message Router +-- *********************** + +local keyValues = { + ["A"] = 0, + ["B"] = 1, + ["Select"] = 2, + ["Start"] = 3, + ["Right"] = 4, + ["Left"] = 5, + ["Up"] = 6, + ["Down"] = 7, + ["R"] = 8, + ["L"] = 9 +} + +function messageRouter(rawMessage) + local parsedInput = splitStringToTable(rawMessage, ",") + + local messageType = parsedInput[1] + local messageValue1 = parsedInput[2] + local messageValue2 = parsedInput[3] + local messageValue3 = parsedInput[4] + + local defaultReturnValue = "<|ACK|>"; + + local returnValue = defaultReturnValue; + + formattedLog("messageRouter: \n\tRaw message:" .. rawMessage .. "\n\tmessageType: " .. (messageType or "") .. "\n\tmessageValue1: " .. (messageValue1 or "") .. "\n\tmessageValue2: " .. (messageValue2 or "") .. "\n\tmessageValue3: " .. (messageValue3 or "")) + + if messageType == "mgba-http.button.tap" then manageButton(messageValue1) + elseif messageType == "mgba-http.button.tapmany" then manageButtons(messageValue1) + elseif messageType == "mgba-http.button.hold" then manageButton(messageValue1, messageValue2) + elseif messageType == "mgba-http.button.holdmany" then manageButtons(messageValue1, messageValue2) + elseif messageType == "core.addKey" then addKey(messageValue1) + elseif messageType == "core.addKeys" then emu:addKeys(tonumber(messageValue1)) + elseif messageType == "core.autoloadSave" then returnValue = emu:autoloadSave() + elseif messageType == "core.checksum" then returnValue = computeChecksum() + elseif messageType == "core.clearKey" then clearKey(messageValue1) + elseif messageType == "core.clearKeys" then emu:clearKeys(tonumber(messageValue1)) + elseif messageType == "core.currentFrame" then returnValue = emu:currentFrame() + elseif messageType == "core.frameCycles" then returnValue = emu:frameCycles() + elseif messageType == "core.frequency" then returnValue = emu:frequency() + elseif messageType == "core.getGameCode" then returnValue = emu:getGameCode() + elseif messageType == "core.getGameTitle" then returnValue = emu:getGameTitle() + elseif messageType == "core.getKey" then returnValue = emu:getKey(keyValues[messageValue1]) + elseif messageType == "core.getKeys" then returnValue = emu:getKeys() + elseif messageType == "core.loadFile" then returnValue = emu:loadFile(messageValue1) + elseif messageType == "core.loadSaveFile" then returnValue = emu:loadSaveFile(messageValue1, toBoolean(messageValue2)) + elseif messageType == "core.loadStateBuffer" then returnValue = emu:loadStateBuffer(messageValue1, messageValue2) + elseif messageType == "core.loadStateFile" then returnValue = emu:loadStateFile(messageValue1, tonumber(messageValue2)) + elseif messageType == "core.loadStateSlot" then returnValue = emu:loadStateSlot(tonumber(messageValue1), tonumber(messageValue2)) + elseif messageType == "core.platform" then returnValue = emu:platform() + elseif messageType == "core.read16" then returnValue = emu:read16(tonumber(messageValue1)) + elseif messageType == "core.read32" then returnValue = emu:read32(tonumber(messageValue1)) + elseif messageType == "core.read8" then returnValue = emu:read8(tonumber(messageValue1)) + elseif messageType == "core.readRange" then returnValue = emu:readRange(tonumber(messageValue1), tonumber(messageValue2)) + elseif messageType == "core.readRegister" then returnValue = emu:readRegister(messageValue1) + elseif messageType == "core.romSize" then returnValue = emu:romSize() + elseif messageType == "core.runFrame" then emu:runFrame() + elseif messageType == "core.saveStateBuffer" then emu:saveStateBuffer(tonumber(messageValue1)) + elseif messageType == "core.saveStateFile" then returnValue = emu:saveStateFile(messageValue1, tonumber(messageValue2)) + elseif messageType == "core.saveStateSlot" then returnValue = emu:saveStateSlot(tonumber(messageValue1), tonumber(messageValue2)) + elseif messageType == "core.screenshot" then emu:screenshot(messageValue1) + elseif messageType == "core.setKeys" then emu:setKeys(tonumber(messageValue1)) + elseif messageType == "core.step" then emu:step() + elseif messageType == "core.write16" then returnValue = emu:write16(tonumber(messageValue1), tonumber(messageValue2)) + elseif messageType == "core.write32" then returnValue = emu:write32(tonumber(messageValue1), tonumber(messageValue2)) + elseif messageType == "core.write8" then returnValue = emu:write8(tonumber(messageValue1), tonumber(messageValue2)) + elseif messageType == "core.writeRegister" then returnValue = emu:writeRegister(messageValue1, tonumber(messageValue2)) + elseif messageType == "console.error" then console:error(messageValue1) + elseif messageType == "console.log" then console:log(messageValue1) + elseif messageType == "console.warn" then console:warn(messageValue1) + elseif messageType == "coreAdapter.reset" then emu:reset() + elseif messageType == "memoryDomain.base" then returnValue = emu.memory[messageValue1]:base() + elseif messageType == "memoryDomain.bound" then returnValue = emu.memory[messageValue1]:bound() + elseif messageType == "memoryDomain.name" then returnValue = emu.memory[messageValue1]:name() + elseif messageType == "memoryDomain.read16" then returnValue = emu.memory[messageValue1]:read16(tonumber(messageValue2)) + elseif messageType == "memoryDomain.read32" then returnValue = emu.memory[messageValue1]:read32(tonumber(messageValue2)) + elseif messageType == "memoryDomain.read8" then returnValue = emu.memory[messageValue1]:read8(tonumber(messageValue2)) + elseif messageType == "memoryDomain.readRange" then returnValue = emu.memory[messageValue1]:readRange(tonumber(messageValue2), tonumber(messageValue3)) + elseif messageType == "memoryDomain.size" then returnValue = emu.memory[messageValue1]:size() + elseif messageType == "memoryDomain.write16" then returnValue = emu.memory[messageValue1]:write16(tonumber(messageValue2), tonumber(messageValue3)) + elseif messageType == "memoryDomain.write32" then returnValue = emu.memory[messageValue1]:write32(tonumber(messageValue2), tonumber(messageValue3)) + elseif messageType == "memoryDomain.write8" then returnValue = emu.memory[messageValue1]:write8(tonumber(messageValue2), tonumber(messageValue3)) + elseif (rawMessage == "<|ACK|>") then formattedLog("Connecting.") + elseif (rawMessage ~= nil or rawMessage ~= '') then formattedLog("Unable to route raw message: " .. rawMessage) + else formattedLog(messageType) + end + + returnValue = tostring(returnValue or defaultReturnValue); + + formattedLog("Returning: " .. returnValue) + return returnValue; +end + +-- *********************** +-- Button (Convenience abstraction) +-- *********************** + +function addKey(keyLetter) + local key = keyValues[keyLetter]; + emu:addKey(key) +end + +function clearKey(keyLetter) + local key = keyValues[keyLetter]; + emu:clearKey(key) +end + +local keyEventQueue = {} + +function manageButton(keyLetter, duration) + duration = duration or 15 + local key = keyValues[keyLetter] + local bitmask = toBitmask({key}) + enqueueButtons(bitmask, duration) +end + +function manageButtons(keyLetters, duration) + duration = duration or 15 + local keyLettersArray = splitStringToTable(keyLetters, ";") + local keys = {} + for i, keyLetter in ipairs(keyLettersArray) do + formattedLog("lmao 5 " ..keyLetter ) + keys[i] = keyValues[keyLetter] + end + local bitmask = toBitmask(keys); + enqueueButtons(bitmask, duration); +end + +function enqueueButtons(keyMask, duration) + local startFrame = emu:currentFrame() + local endFrame = startFrame + duration + 1 + + table.insert(keyEventQueue, + { + keyMask = keyMask, + startFrame = startFrame, + endFrame = endFrame, + pressed = false + }); + + formattedLog(keyMask) +end + +function updateKeys() + local indexesToRemove = {} + + for index, keyEvent in ipairs(keyEventQueue) do + + if emu:currentFrame() >= keyEvent.startFrame and emu:currentFrame() <= keyEvent.endFrame and not keyEvent.pressed then + emu:addKeys(keyEvent.keyMask) + keyEvent.pressed = true + elseif emu:currentFrame() > keyEvent.endFrame then + emu:clearKeys(keyEvent.keyMask) + table.insert(indexesToRemove, index) + end + end + + for _, i in ipairs(indexesToRemove) do + table.remove(keyEventQueue, i) + end +end + +callbacks:add("frame", updateKeys) + +-- *********************** +-- Utility +-- *********************** + +function splitStringToTable(inputstr, sep) + if sep == nil then + sep = "%s" + end + local t={} + for str in string.gmatch(inputstr, "([^"..sep.."]+)") do + table.insert(t, str) + end + return t +end + +function numberStringToHex(string) + return string.format('%x', tonumber(string, 16)) +end + +function toBoolean(str) + local bool = false + if string.lower(str) == "true" then + bool = true + end + return bool +end + +function formattedLog(string) + if enableLogging then + local timestamp = "[" .. os.date("%X", os.time()) .. "] " + console:log(timestamp .. string) + end +end + +function computeChecksum() + local checksum = 0 + for i, v in ipairs({emu:checksum(C.CHECKSUM.CRC32):byte(1, 4)}) do + checksum = checksum * 256 + v + end + return checksum +end + +function toBitmask(keys) + local mask = 0 + for _, key in ipairs(keys) do + mask = mask | (1 << tonumber(key)) + end + return mask +end + +-- *********************** +-- Start +-- *********************** + +beginSocket() \ No newline at end of file diff --git a/http/server/mGBA-http-server.exe b/http/server/mGBA-http-server.exe new file mode 100644 index 0000000..0c3addd Binary files /dev/null and b/http/server/mGBA-http-server.exe differ diff --git a/http/swagger/swagger.json b/http/swagger/swagger.json new file mode 100644 index 0000000..96a9cfc --- /dev/null +++ b/http/swagger/swagger.json @@ -0,0 +1,1859 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "mGBA-http", + "description": "An HTTP interface for mGBA scripting.", + "contact": { + "name": "GitHub Repository", + "url": "https://github.com/nikouu/mGBA-http/" + } + }, + "paths": { + "/mgba-http/button/tap": { + "post": { + "tags": [ + "Button" + ], + "summary": "Sends button presses.", + "description": "A custom convenience API that implements a key press and release. This is as opposed to the key based core API that sends only either a press or release message.", + "parameters": [ + { + "name": "key", + "in": "query", + "description": "Key value of: A, B, Start, Select, Start, Right, Left, Up, Down, R, or L.", + "required": true, + "style": "form", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/mgba-http/button/tapmany": { + "post": { + "tags": [ + "Button" + ], + "summary": "Sends multiple button presses simultaneously.", + "description": "A custom convenience API that implements multiple simultaneously keys being pressed and released. This is as opposed to the key based core API that sends only either a press or release message.", + "parameters": [ + { + "name": "keys", + "in": "query", + "description": "Key array containing any of: A, B, Start, Select, Start, Right, Left, Up, Down, R, or L.", + "required": true, + "style": "form", + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/KeysEnum" + } + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/mgba-http/button/hold": { + "post": { + "tags": [ + "Button" + ], + "summary": "Sends a held down button for a given duration in frames.", + "description": "A custom convenience API that implements a held down button for a given duration in frames. This is as opposed to the key based core API that sends only either a press or release message.", + "parameters": [ + { + "name": "key", + "in": "query", + "description": "Key value of: A, B, Start, Select, Start, Right, Left, Up, Down, R, or L.", + "required": true, + "style": "form", + "schema": { + "type": "string" + } + }, + { + "name": "duration", + "in": "query", + "required": true, + "style": "form", + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/mgba-http/button/holdmany": { + "post": { + "tags": [ + "Button" + ], + "summary": "Sends multiple button presses simultaneously for a given duration in frames.", + "description": "A custom convenience API that implements multiple simultaneously keys being pressed and released for a given duration in frames. This is as opposed to the key based core API that sends only either a press or release message.", + "parameters": [ + { + "name": "keys", + "in": "query", + "description": "Key array containing any of: A, B, Start, Select, Start, Right, Left, Up, Down, R, or L.", + "required": true, + "style": "form", + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/KeysEnum" + } + } + }, + { + "name": "duration", + "in": "query", + "required": true, + "style": "form", + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/console/error": { + "post": { + "tags": [ + "Console" + ], + "summary": "Print an error to the console.", + "description": "Presents textual information to the user via a console.", + "parameters": [ + { + "name": "message", + "in": "query", + "required": true, + "style": "form", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/console/log": { + "post": { + "tags": [ + "Console" + ], + "summary": "Print a log to the console.", + "description": "Presents textual information to the user via a console.", + "parameters": [ + { + "name": "message", + "in": "query", + "required": true, + "style": "form", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/console/warn": { + "post": { + "tags": [ + "Console" + ], + "summary": "Print a warning to the console.", + "description": "Presents textual information to the user via a console.", + "parameters": [ + { + "name": "message", + "in": "query", + "required": true, + "style": "form", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/core/addkey": { + "post": { + "tags": [ + "Core" + ], + "summary": "Add a single key.", + "description": "Add a single key to the currently active key list. As if the key were held down.", + "parameters": [ + { + "name": "key", + "in": "query", + "description": "Key value of: A, B, Start, Select, Start, Right, Left, Up, Down, R, or L.", + "required": true, + "style": "form", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/core/addkeys": { + "post": { + "tags": [ + "Core" + ], + "summary": "Add a bitmask of keys.", + "description": "Add a bitmask of keys to the currently active key list. As if the keys were held down.", + "parameters": [ + { + "name": "keyBitmask", + "in": "query", + "description": "Bitmasking has the keys from the mGBA scripting documentation where each key has a value that goes up in that order in binary: A = 1, B = 2, Select = 4, Start = 8, etc. A bitmask of 12 is Start and Select.", + "required": true, + "style": "form", + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/core/autoloadsave": { + "post": { + "tags": [ + "Core" + ], + "summary": "Load save data.", + "description": "Load the save data associated with the currently loaded ROM file.", + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/core/checksum": { + "get": { + "tags": [ + "Core" + ], + "summary": "Get the checksum of the loaded ROM.", + "description": "Get the checksum of the loaded ROM as base 10.", + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/core/clearkey": { + "post": { + "tags": [ + "Core" + ], + "summary": "Remove a single key.", + "description": "Remove a single key from the currently active key list. As if the key were released.", + "parameters": [ + { + "name": "key", + "in": "query", + "description": "Key value of: A, B, Start, Select, Start, Right, Left, Up, Down, R, or L.", + "required": true, + "style": "form", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/core/clearkeys": { + "post": { + "tags": [ + "Core" + ], + "summary": "Remove a bitmask of keys.", + "description": "Remove a bitmask of keys from the currently active key list. As if the keys were released.", + "parameters": [ + { + "name": "keyBitmask", + "in": "query", + "description": "Bitmasking has the keys from the mGBA scripting documentation where each key has a value that goes up in that order in binary: A = 1, B = 2, Select = 4, Start = 8, etc. A bitmask of 12 is Start and Select.", + "required": true, + "style": "form", + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/core/currentFrame": { + "get": { + "tags": [ + "Core" + ], + "summary": "Get the number of the current frame.", + "description": "Get the number of the current frame.", + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/core/framecycles": { + "get": { + "tags": [ + "Core" + ], + "summary": "Get the number of cycles per frame.", + "description": "Get the number of cycles per frame.", + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/core/frequency": { + "get": { + "tags": [ + "Core" + ], + "summary": "Get the number of cycles per second.", + "description": "Get the number of cycles per second.", + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/core/getgamecode": { + "get": { + "tags": [ + "Core" + ], + "summary": "Get internal product code for the game.", + "description": "Get internal product code for the game from the ROM header, if available.", + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/core/getgametitle": { + "get": { + "tags": [ + "Core" + ], + "summary": "Get internal title of the game.", + "description": "Get internal title of the game from the ROM header.", + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/core/getkey": { + "get": { + "tags": [ + "Core" + ], + "summary": "Get the active state of a given key.", + "description": "Get the active state of a given key.", + "parameters": [ + { + "name": "key", + "in": "query", + "description": "Key value of: A, B, Start, Select, Start, Right, Left, Up, Down, R, or L.", + "required": true, + "style": "form", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/core/getkeys": { + "get": { + "tags": [ + "Core" + ], + "summary": "Get the currently active keys as a bitmask.", + "description": "Get the currently active keys as a bitmask.", + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/core/loadfile": { + "post": { + "tags": [ + "Core" + ], + "summary": "Load a ROM file.", + "description": "Load a ROM file into the current state of this core.", + "parameters": [ + { + "name": "path", + "in": "query", + "required": true, + "style": "form", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/core/loadsavefile": { + "post": { + "tags": [ + "Core" + ], + "summary": "Load save data file.", + "description": "Load save data from the given path. If the temporary flag is set, the given save data will not be written back to disk.", + "parameters": [ + { + "name": "path", + "in": "query", + "required": true, + "style": "form", + "schema": { + "type": "string" + } + }, + { + "name": "temporary", + "in": "query", + "required": true, + "style": "form", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/core/loadstatebuffer": { + "post": { + "tags": [ + "Core" + ], + "summary": "Load state from a buffer.", + "description": "Load state from a buffer.", + "parameters": [ + { + "name": "buffer", + "in": "query", + "required": true, + "style": "form", + "schema": { + "type": "string" + } + }, + { + "name": "flags", + "in": "query", + "style": "form", + "schema": { + "type": "integer", + "format": "int32", + "default": 29 + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/core/loadstatefile": { + "post": { + "tags": [ + "Core" + ], + "summary": "Load state from the given path.", + "description": "Load state from the given path.", + "parameters": [ + { + "name": "path", + "in": "query", + "required": true, + "style": "form", + "schema": { + "type": "string" + } + }, + { + "name": "flags", + "in": "query", + "style": "form", + "schema": { + "type": "integer", + "format": "int32", + "default": 29 + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/core/loadstateslot": { + "post": { + "tags": [ + "Core" + ], + "summary": "Load state from the slot number.", + "description": "Load state from the slot number.", + "parameters": [ + { + "name": "slot", + "in": "query", + "required": true, + "style": "form", + "schema": { + "type": "string" + } + }, + { + "name": "flags", + "in": "query", + "style": "form", + "schema": { + "type": "integer", + "format": "int32", + "default": 29 + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/core/platform": { + "get": { + "tags": [ + "Core" + ], + "summary": "Get which platform is being emulated.", + "description": "Get which platform is being emulated.", + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/core/read16": { + "get": { + "tags": [ + "Core" + ], + "summary": "Read a 16-bit value from the given bus address.", + "description": "Read a 16-bit value from the given bus address.", + "parameters": [ + { + "name": "address", + "in": "query", + "description": "Address in hex, e.g. 0x03000000", + "required": true, + "style": "form", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/core/read32": { + "get": { + "tags": [ + "Core" + ], + "summary": "Read a 32-bit value from the given bus address.", + "description": "Read a 32-bit value from the given bus address.", + "parameters": [ + { + "name": "address", + "in": "query", + "description": "Address in hex, e.g. 0x03000000", + "required": true, + "style": "form", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/core/read8": { + "get": { + "tags": [ + "Core" + ], + "summary": "Read an 8-bit value from the given bus address.", + "description": "Read an 8-bit value from the given bus address.", + "parameters": [ + { + "name": "address", + "in": "query", + "description": "Address in hex, e.g. 0x03000000", + "required": true, + "style": "form", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/core/readrange": { + "get": { + "tags": [ + "Core" + ], + "summary": "Read byte range from the given offset.", + "description": "Read byte range from the given offset.", + "parameters": [ + { + "name": "address", + "in": "query", + "description": "Address in hex, e.g. 0x03000000", + "required": true, + "style": "form", + "schema": { + "type": "string" + } + }, + { + "name": "length", + "in": "query", + "required": true, + "style": "form", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/core/readregister": { + "get": { + "tags": [ + "Core" + ], + "summary": "Read the value of the register with the given name.", + "description": "Read the value of the register with the given name.", + "parameters": [ + { + "name": "regName", + "in": "query", + "required": true, + "style": "form", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/core/romsize": { + "get": { + "tags": [ + "Core" + ], + "summary": "Get the size of the loaded ROM.", + "description": "Get the size of the loaded ROM.", + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/core/runFrame": { + "post": { + "tags": [ + "Core" + ], + "summary": "Run until the next frame.", + "description": "Run until the next frame.", + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/core/savestatebuffer": { + "post": { + "tags": [ + "Core" + ], + "summary": "Save state and return as a buffer.", + "description": "Save state and return as a buffer.", + "parameters": [ + { + "name": "flags", + "in": "query", + "style": "form", + "schema": { + "type": "integer", + "format": "int32", + "default": 31 + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/core/savestatefile": { + "post": { + "tags": [ + "Core" + ], + "summary": "Save state to the given path.", + "description": "Save state to the given path.", + "parameters": [ + { + "name": "path", + "in": "query", + "required": true, + "style": "form", + "schema": { + "type": "string" + } + }, + { + "name": "flags", + "in": "query", + "style": "form", + "schema": { + "type": "integer", + "format": "int32", + "default": 31 + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/core/savestateslot": { + "post": { + "tags": [ + "Core" + ], + "summary": "Save state to the slot number.", + "description": "Save state to the slot number.", + "parameters": [ + { + "name": "slot", + "in": "query", + "required": true, + "style": "form", + "schema": { + "type": "string" + } + }, + { + "name": "flags", + "in": "query", + "style": "form", + "schema": { + "type": "integer", + "format": "int32", + "default": 31 + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/core/screenshot": { + "post": { + "tags": [ + "Core" + ], + "summary": "Save a screenshot.", + "description": "Save a screenshot.", + "parameters": [ + { + "name": "path", + "in": "query", + "description": "For example, C:\\screenshots\\screenshot.png", + "required": true, + "style": "form", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/core/setkeys": { + "post": { + "tags": [ + "Core" + ], + "summary": "Set the currently active key list.", + "description": "Set the currently active key list.", + "parameters": [ + { + "name": "keys", + "in": "query", + "required": true, + "style": "form", + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/core/step": { + "post": { + "tags": [ + "Core" + ], + "summary": "Run a single instruction.", + "description": "Run a single instruction.", + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/core/write16": { + "post": { + "tags": [ + "Core" + ], + "summary": "Write a 16-bit value from the given bus address.", + "description": "Write a 16-bit value from the given bus address.", + "parameters": [ + { + "name": "address", + "in": "query", + "description": "Address in hex, e.g. 0x03000000", + "required": true, + "style": "form", + "schema": { + "type": "string" + } + }, + { + "name": "value", + "in": "query", + "required": true, + "style": "form", + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/core/write32": { + "post": { + "tags": [ + "Core" + ], + "summary": "Write a 32-bit value from the given bus address.", + "description": "Write a 32-bit value from the given bus address.", + "parameters": [ + { + "name": "address", + "in": "query", + "description": "Address in hex, e.g. 0x03000000", + "required": true, + "style": "form", + "schema": { + "type": "string" + } + }, + { + "name": "value", + "in": "query", + "required": true, + "style": "form", + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/core/write8": { + "post": { + "tags": [ + "Core" + ], + "summary": "Write an 8-bit value from the given bus address.", + "description": "Write an 8-bit value from the given bus address.", + "parameters": [ + { + "name": "address", + "in": "query", + "description": "Address in hex, e.g. 0x03000000", + "required": true, + "style": "form", + "schema": { + "type": "string" + } + }, + { + "name": "value", + "in": "query", + "required": true, + "style": "form", + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/core/writeregister": { + "post": { + "tags": [ + "Core" + ], + "summary": "Write the value of the register with the given name.", + "description": "Write the value of the register with the given name.", + "parameters": [ + { + "name": "regName", + "in": "query", + "description": "Address in hex, e.g. 0x03000000", + "required": true, + "style": "form", + "schema": { + "type": "string" + } + }, + { + "name": "value", + "in": "query", + "required": true, + "style": "form", + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/coreadapter/reset": { + "post": { + "tags": [ + "CoreAdapter" + ], + "summary": "Reset the emulation.", + "description": "Reset the emulation and calls the reset callback.", + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/memorydomain/base": { + "get": { + "tags": [ + "MemoryDomain" + ], + "summary": "Get the address of the base of this memory domain.", + "description": "Get the address of the base of this memory domain.", + "parameters": [ + { + "name": "memoryDomain", + "in": "query", + "required": true, + "style": "form", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/memorydomain/bound": { + "get": { + "tags": [ + "MemoryDomain" + ], + "summary": "Get the address of the end bound of this memory domain.", + "description": "Get the address of the end bound of this memory domain. Note that this address is not in the domain itself, and is the address of the first byte past it.", + "parameters": [ + { + "name": "memoryDomain", + "in": "query", + "required": true, + "style": "form", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/memorydomain/name": { + "get": { + "tags": [ + "MemoryDomain" + ], + "summary": "Get a short, human-readable name for this memory domain.", + "description": "Get a short, human-readable name for this memory domain.", + "parameters": [ + { + "name": "memoryDomain", + "in": "query", + "required": true, + "style": "form", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/memorydomain/read16": { + "get": { + "tags": [ + "MemoryDomain" + ], + "summary": "Read a 16-bit value from the given offset.", + "description": "Read a 16-bit value from the given offset.", + "parameters": [ + { + "name": "memoryDomain", + "in": "query", + "required": true, + "style": "form", + "schema": { + "type": "string" + } + }, + { + "name": "address", + "in": "query", + "description": "Address in hex, e.g. 0x03000000", + "required": true, + "style": "form", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/memorydomain/read32": { + "get": { + "tags": [ + "MemoryDomain" + ], + "summary": "Read a 32-bit value from the given offset.", + "description": "Read a 32-bit value from the given offset.", + "parameters": [ + { + "name": "memoryDomain", + "in": "query", + "required": true, + "style": "form", + "schema": { + "type": "string" + } + }, + { + "name": "address", + "in": "query", + "description": "Address in hex, e.g. 0x03000000", + "required": true, + "style": "form", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/memorydomain/read8": { + "get": { + "tags": [ + "MemoryDomain" + ], + "summary": "Read an 8-bit value from the given offset.", + "description": "Read an 8-bit value from the given offset.", + "parameters": [ + { + "name": "memoryDomain", + "in": "query", + "required": true, + "style": "form", + "schema": { + "type": "string" + } + }, + { + "name": "address", + "in": "query", + "description": "Address in hex, e.g. 0x03000000", + "required": true, + "style": "form", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/memorydomain/readrange": { + "get": { + "tags": [ + "MemoryDomain" + ], + "summary": "Read byte range from the given offset.", + "description": "Read byte range from the given offset.", + "parameters": [ + { + "name": "memoryDomain", + "in": "query", + "required": true, + "style": "form", + "schema": { + "type": "string" + } + }, + { + "name": "address", + "in": "query", + "description": "Address in hex, e.g. 0x03000000", + "required": true, + "style": "form", + "schema": { + "type": "string" + } + }, + { + "name": "length", + "in": "query", + "required": true, + "style": "form", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/memorydomain/size": { + "get": { + "tags": [ + "MemoryDomain" + ], + "summary": "Get the size of this memory domain in bytes.", + "description": "Get the size of this memory domain in bytes.", + "parameters": [ + { + "name": "memoryDomain", + "in": "query", + "required": true, + "style": "form", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/memorydomain/write16": { + "post": { + "tags": [ + "MemoryDomain" + ], + "summary": "Write a 16-bit value from the given offset.", + "description": "Write a 16-bit value from the given offset.", + "parameters": [ + { + "name": "memoryDomain", + "in": "query", + "required": true, + "style": "form", + "schema": { + "type": "string" + } + }, + { + "name": "address", + "in": "query", + "description": "Address in hex, e.g. 0x03000000", + "required": true, + "style": "form", + "schema": { + "type": "string" + } + }, + { + "name": "value", + "in": "query", + "required": true, + "style": "form", + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/memorydomain/write32": { + "post": { + "tags": [ + "MemoryDomain" + ], + "summary": "Write a 32-bit value from the given offset.", + "description": "Write a 32-bit value from the given offset.", + "parameters": [ + { + "name": "memoryDomain", + "in": "query", + "required": true, + "style": "form", + "schema": { + "type": "string" + } + }, + { + "name": "address", + "in": "query", + "description": "Address in hex, e.g. 0x03000000", + "required": true, + "style": "form", + "schema": { + "type": "string" + } + }, + { + "name": "value", + "in": "query", + "required": true, + "style": "form", + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/memorydomain/write8": { + "post": { + "tags": [ + "MemoryDomain" + ], + "summary": "Write an 8-bit value from the given offset.", + "description": "Write an 8-bit value from the given offset.", + "parameters": [ + { + "name": "memoryDomain", + "in": "query", + "required": true, + "style": "form", + "schema": { + "type": "string" + } + }, + { + "name": "address", + "in": "query", + "description": "Address in hex, e.g. 0x03000000", + "required": true, + "style": "form", + "schema": { + "type": "string" + } + }, + { + "name": "value", + "in": "query", + "required": true, + "style": "form", + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "KeysEnum": { + "enum": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ], + "type": "integer", + "format": "int32" + } + } + } +} \ No newline at end of file diff --git a/node/package.json b/node/package.json new file mode 100644 index 0000000..04029ad --- /dev/null +++ b/node/package.json @@ -0,0 +1,15 @@ +{ + "name": "tiktok-plays", + "version": "1.0.0", + "description": "TikTok Plays: GBA Integration", + "type": "commonjs", + "dependencies": { + "@types/node": "^20.12.11", + "swagger-typescript-api": "^13.0.3", + "tiktok-live-connector": "^1.1.6" + }, + "scripts": { + "start": "tsc && node dist/index.js", + "swagger-api": "npx swagger-typescript-api -p ../http/swagger/swagger.json -o ./source/classes/gba-api -n API.ts --modular" + } +} diff --git a/node/pnpm-lock.yaml b/node/pnpm-lock.yaml new file mode 100644 index 0000000..703b631 --- /dev/null +++ b/node/pnpm-lock.yaml @@ -0,0 +1,959 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@types/node': + specifier: ^20.12.11 + version: 20.12.11 + swagger-typescript-api: + specifier: ^13.0.3 + version: 13.0.3 + tiktok-live-connector: + specifier: ^1.1.6 + version: 1.1.6 + +packages: + + '@babel/code-frame@7.24.2': + resolution: {integrity: sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.24.5': + resolution: {integrity: sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==} + engines: {node: '>=6.9.0'} + + '@babel/highlight@7.24.5': + resolution: {integrity: sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw==} + engines: {node: '>=6.9.0'} + + '@exodus/schemasafe@1.3.0': + resolution: {integrity: sha512-5Aap/GaRupgNx/feGBwLLTVv8OQFfv3pq2lPRzPg9R+IOBnDgghTGW7l7EuVXOvg5cc/xSAlRW8rBrjIC3Nvqw==} + + '@protobufjs/aspromise@1.1.2': + resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} + + '@protobufjs/base64@1.1.2': + resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} + + '@protobufjs/codegen@2.0.4': + resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} + + '@protobufjs/eventemitter@1.1.0': + resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} + + '@protobufjs/fetch@1.1.0': + resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} + + '@protobufjs/float@1.0.2': + resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} + + '@protobufjs/inquire@1.1.0': + resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} + + '@protobufjs/path@1.1.2': + resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} + + '@protobufjs/pool@1.1.0': + resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} + + '@protobufjs/utf8@1.1.0': + resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} + + '@sindresorhus/is@3.1.2': + resolution: {integrity: sha512-JiX9vxoKMmu8Y3Zr2RVathBL1Cdu4Nt4MuNWemt1Nc06A0RAin9c5FArkhGsyMBWfCu4zj+9b+GxtjAnE4qqLQ==} + engines: {node: '>=10'} + + '@types/long@4.0.2': + resolution: {integrity: sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==} + + '@types/node@20.12.11': + resolution: {integrity: sha512-vDg9PZ/zi+Nqp6boSOT7plNuthRugEKixDv5sFTIpkE89MmNtEArAShI4mxuX2+UrLEe9pxC1vm2cjm9YlWbJw==} + + '@types/swagger-schema-official@2.0.22': + resolution: {integrity: sha512-7yQiX6MWSFSvc/1wW5smJMZTZ4fHOd+hqLr3qr/HONDxHEa2bnYAsOcGBOEqFIjd4yetwMOdEDdeW+udRAQnHA==} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + axios@0.25.0: + resolution: {integrity: sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==} + + bufferutil@4.0.8: + resolution: {integrity: sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw==} + engines: {node: '>=6.14.2'} + + call-me-maybe@1.0.2: + resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + + char-regex@1.0.2: + resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} + engines: {node: '>=10'} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + cosmiconfig@8.2.0: + resolution: {integrity: sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ==} + engines: {node: '>=14'} + + d@1.0.2: + resolution: {integrity: sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==} + engines: {node: '>=0.12'} + + data-uri-to-buffer@4.0.1: + resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} + engines: {node: '>= 12'} + + debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + didyoumean@1.2.2: + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emojilib@2.4.0: + resolution: {integrity: sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==} + + error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + + es5-ext@0.10.64: + resolution: {integrity: sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==} + engines: {node: '>=0.10'} + + es6-iterator@2.0.3: + resolution: {integrity: sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==} + + es6-promise@3.3.1: + resolution: {integrity: sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==} + + es6-symbol@3.1.4: + resolution: {integrity: sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==} + engines: {node: '>=0.12'} + + escalade@3.1.2: + resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} + engines: {node: '>=6'} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + esniff@2.0.1: + resolution: {integrity: sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==} + engines: {node: '>=0.10'} + + eta@2.2.0: + resolution: {integrity: sha512-UVQ72Rqjy/ZKQalzV5dCCJP80GrmPrMxh6NlNf+erV6ObL0ZFkhCstWRawS85z3smdr3d2wXPsZEY7rDPfGd2g==} + engines: {node: '>=6.0.0'} + + event-emitter@0.3.5: + resolution: {integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==} + + ext@1.7.0: + resolution: {integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==} + + fast-safe-stringify@2.1.1: + resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + + fetch-blob@3.2.0: + resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} + engines: {node: ^12.20 || >= 14.13} + + follow-redirects@1.15.6: + resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + formdata-polyfill@4.0.10: + resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} + engines: {node: '>=12.20.0'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + + http2-client@1.3.5: + resolution: {integrity: sha512-EC2utToWl4RKfs5zd36Mxq7nzHHBuomZboI0yYL6Y0RmBgT7Sgkq4rQ0ezFTYoIsSs7Tm9SJe+o2FcAg6GBhGA==} + + import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-typedarray@1.0.0: + resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + long@4.0.0: + resolution: {integrity: sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==} + + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + + ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + + nanoid@3.3.6: + resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + next-tick@1.1.0: + resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} + + node-domexception@1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} + + node-emoji@2.1.0: + resolution: {integrity: sha512-tcsBm9C6FmPN5Wo7OjFi9lgMyJjvkAeirmjR/ax8Ttfqy4N8PoFic26uqFTIgayHPNI5FH4ltUvfh9kHzwcK9A==} + + node-fetch-h2@2.3.0: + resolution: {integrity: sha512-ofRW94Ab0T4AOh5Fk8t0h8OBWrmjb0SSB20xh1H8YnPV9EJ+f5AMoYSUQ2zgJ4Iq2HAK0I2l5/Nequ8YzFS3Hg==} + engines: {node: 4.x || >=6.0.0} + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + node-fetch@3.3.2: + resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + node-gyp-build@4.8.1: + resolution: {integrity: sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==} + hasBin: true + + node-readfiles@0.2.0: + resolution: {integrity: sha512-SU00ZarexNlE4Rjdm83vglt5Y9yiQ+XI1XpflWlb7q7UTN1JUItm69xMeiQCTxtTfnzt+83T8Cx+vI2ED++VDA==} + + oas-kit-common@1.0.8: + resolution: {integrity: sha512-pJTS2+T0oGIwgjGpw7sIRU8RQMcUoKCDWFLdBqKB2BNmGpbBMH2sdqAaOXUg8OzonZHU0L7vfJu1mJFEiYDWOQ==} + + oas-linter@3.2.2: + resolution: {integrity: sha512-KEGjPDVoU5K6swgo9hJVA/qYGlwfbFx+Kg2QB/kd7rzV5N8N5Mg6PlsoCMohVnQmo+pzJap/F610qTodKzecGQ==} + + oas-resolver@2.5.6: + resolution: {integrity: sha512-Yx5PWQNZomfEhPPOphFbZKi9W93CocQj18NlD2Pa4GWZzdZpSJvYwoiuurRI7m3SpcChrnO08hkuQDL3FGsVFQ==} + hasBin: true + + oas-schema-walker@1.1.5: + resolution: {integrity: sha512-2yucenq1a9YPmeNExoUa9Qwrt9RFkjqaMAA1X+U7sbb0AqBeTIdMHky9SQQ6iN94bO5NW0W4TRYXerG+BdAvAQ==} + + oas-validator@5.0.8: + resolution: {integrity: sha512-cu20/HE5N5HKqVygs3dt94eYJfBi0TsZvPVXDhbXQHiEityDN+RROTleefoKRKKJ9dFAF2JBkDHgvWj0sjKGmw==} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + picocolors@1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + + prettier@3.0.0: + resolution: {integrity: sha512-zBf5eHpwHOGPC47h0zrPyNn+eAEIdEzfywMoYn2XPi0P44Zp0tSq64rq0xAREh4auw2cJZHo9QUob+NqCQky4g==} + engines: {node: '>=14'} + hasBin: true + + protobufjs@6.11.4: + resolution: {integrity: sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw==} + hasBin: true + + reftools@1.1.9: + resolution: {integrity: sha512-OVede/NQE13xBQ+ob5CKd5KyeJYU2YInb1bmV4nRoOfquZPkAkxuOXicSe1PvqIuZZ4kD13sPKBbR7UFDmli6w==} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + semver@7.6.1: + resolution: {integrity: sha512-f/vbBsu+fOiYt+lmwZV0rVwJScl46HppnOA1ZvIuBWKOTlllpyJ3bfVax76/OrhCH38dyxoDIA8K7uB963IYgA==} + engines: {node: '>=10'} + hasBin: true + + should-equal@2.0.0: + resolution: {integrity: sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==} + + should-format@3.0.3: + resolution: {integrity: sha512-hZ58adtulAk0gKtua7QxevgUaXTTXxIi8t41L3zo9AHvjXO1/7sdLECuHeIN2SRtYXpNkmhoUP2pdeWgricQ+Q==} + + should-type-adaptors@1.1.0: + resolution: {integrity: sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==} + + should-type@1.4.0: + resolution: {integrity: sha512-MdAsTu3n25yDbIe1NeN69G4n6mUnJGtSJHygX3+oN0ZbO3DTiATnf7XnYJdGT42JCXurTb1JI0qOBR65shvhPQ==} + + should-util@1.0.1: + resolution: {integrity: sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g==} + + should@13.2.3: + resolution: {integrity: sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==} + + skin-tone@2.0.0: + resolution: {integrity: sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==} + engines: {node: '>=8'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + + swagger-schema-official@2.0.0-bab6bed: + resolution: {integrity: sha512-rCC0NWGKr/IJhtRuPq/t37qvZHI/mH4I4sxflVM+qgVe5Z2uOCivzWaVbuioJaB61kvm5UvB7b49E+oBY0M8jA==} + + swagger-typescript-api@13.0.3: + resolution: {integrity: sha512-774ndLpGm2FNpUZpDugfoOO2pIcvSW9nlcqwLVSH9ju4YKCi1Gd83jPly7upcljOvZ8KO/edIUx+9eYViDYglg==} + hasBin: true + + swagger2openapi@7.0.8: + resolution: {integrity: sha512-upi/0ZGkYgEcLeGieoz8gT74oWHA0E7JivX7aN9mAf+Tc7BQoRBvnIGHoPDw+f9TXTW4s6kGYCZJtauP6OYp7g==} + hasBin: true + + tiktok-live-connector@1.1.6: + resolution: {integrity: sha512-iDdfvjrbAu+99yWe9xSX2xRdnSoSLiI/jraXdJg3a2kaYpje47Mtiz6JB7RvSz/oOH/Nf12ZYrEnZMHJp0iVIw==} + engines: {node: '>=10.0.0'} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + type@2.7.2: + resolution: {integrity: sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==} + + typedarray-to-buffer@3.1.5: + resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} + + typescript@5.1.6: + resolution: {integrity: sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + + unicode-emoji-modifier-base@1.0.0: + resolution: {integrity: sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==} + engines: {node: '>=4'} + + utf-8-validate@5.0.10: + resolution: {integrity: sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==} + engines: {node: '>=6.14.2'} + + web-streams-polyfill@3.3.3: + resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} + engines: {node: '>= 8'} + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + websocket@1.0.34: + resolution: {integrity: sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ==} + engines: {node: '>=4.0.0'} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yaeti@0.0.6: + resolution: {integrity: sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==} + engines: {node: '>=0.10.32'} + + yaml@1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + +snapshots: + + '@babel/code-frame@7.24.2': + dependencies: + '@babel/highlight': 7.24.5 + picocolors: 1.0.0 + + '@babel/helper-validator-identifier@7.24.5': {} + + '@babel/highlight@7.24.5': + dependencies: + '@babel/helper-validator-identifier': 7.24.5 + chalk: 2.4.2 + js-tokens: 4.0.0 + picocolors: 1.0.0 + + '@exodus/schemasafe@1.3.0': {} + + '@protobufjs/aspromise@1.1.2': {} + + '@protobufjs/base64@1.1.2': {} + + '@protobufjs/codegen@2.0.4': {} + + '@protobufjs/eventemitter@1.1.0': {} + + '@protobufjs/fetch@1.1.0': + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/inquire': 1.1.0 + + '@protobufjs/float@1.0.2': {} + + '@protobufjs/inquire@1.1.0': {} + + '@protobufjs/path@1.1.2': {} + + '@protobufjs/pool@1.1.0': {} + + '@protobufjs/utf8@1.1.0': {} + + '@sindresorhus/is@3.1.2': {} + + '@types/long@4.0.2': {} + + '@types/node@20.12.11': + dependencies: + undici-types: 5.26.5 + + '@types/swagger-schema-official@2.0.22': {} + + ansi-regex@5.0.1: {} + + ansi-styles@3.2.1: + dependencies: + color-convert: 1.9.3 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + argparse@2.0.1: {} + + axios@0.25.0: + dependencies: + follow-redirects: 1.15.6 + transitivePeerDependencies: + - debug + + bufferutil@4.0.8: + dependencies: + node-gyp-build: 4.8.1 + + call-me-maybe@1.0.2: {} + + callsites@3.1.0: {} + + chalk@2.4.2: + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + + char-regex@1.0.2: {} + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + color-convert@1.9.3: + dependencies: + color-name: 1.1.3 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.3: {} + + color-name@1.1.4: {} + + cosmiconfig@8.2.0: + dependencies: + import-fresh: 3.3.0 + js-yaml: 4.1.0 + parse-json: 5.2.0 + path-type: 4.0.0 + + d@1.0.2: + dependencies: + es5-ext: 0.10.64 + type: 2.7.2 + + data-uri-to-buffer@4.0.1: {} + + debug@2.6.9: + dependencies: + ms: 2.0.0 + + didyoumean@1.2.2: {} + + emoji-regex@8.0.0: {} + + emojilib@2.4.0: {} + + error-ex@1.3.2: + dependencies: + is-arrayish: 0.2.1 + + es5-ext@0.10.64: + dependencies: + es6-iterator: 2.0.3 + es6-symbol: 3.1.4 + esniff: 2.0.1 + next-tick: 1.1.0 + + es6-iterator@2.0.3: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + es6-symbol: 3.1.4 + + es6-promise@3.3.1: {} + + es6-symbol@3.1.4: + dependencies: + d: 1.0.2 + ext: 1.7.0 + + escalade@3.1.2: {} + + escape-string-regexp@1.0.5: {} + + esniff@2.0.1: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + event-emitter: 0.3.5 + type: 2.7.2 + + eta@2.2.0: {} + + event-emitter@0.3.5: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + + ext@1.7.0: + dependencies: + type: 2.7.2 + + fast-safe-stringify@2.1.1: {} + + fetch-blob@3.2.0: + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 3.3.3 + + follow-redirects@1.15.6: {} + + formdata-polyfill@4.0.10: + dependencies: + fetch-blob: 3.2.0 + + get-caller-file@2.0.5: {} + + has-flag@3.0.0: {} + + http2-client@1.3.5: {} + + import-fresh@3.3.0: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + is-arrayish@0.2.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-typedarray@1.0.0: {} + + js-tokens@4.0.0: {} + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + json-parse-even-better-errors@2.3.1: {} + + lines-and-columns@1.2.4: {} + + lodash@4.17.21: {} + + long@4.0.0: {} + + make-dir@4.0.0: + dependencies: + semver: 7.6.1 + + ms@2.0.0: {} + + nanoid@3.3.6: {} + + next-tick@1.1.0: {} + + node-domexception@1.0.0: {} + + node-emoji@2.1.0: + dependencies: + '@sindresorhus/is': 3.1.2 + char-regex: 1.0.2 + emojilib: 2.4.0 + skin-tone: 2.0.0 + + node-fetch-h2@2.3.0: + dependencies: + http2-client: 1.3.5 + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + + node-fetch@3.3.2: + dependencies: + data-uri-to-buffer: 4.0.1 + fetch-blob: 3.2.0 + formdata-polyfill: 4.0.10 + + node-gyp-build@4.8.1: {} + + node-readfiles@0.2.0: + dependencies: + es6-promise: 3.3.1 + + oas-kit-common@1.0.8: + dependencies: + fast-safe-stringify: 2.1.1 + + oas-linter@3.2.2: + dependencies: + '@exodus/schemasafe': 1.3.0 + should: 13.2.3 + yaml: 1.10.2 + + oas-resolver@2.5.6: + dependencies: + node-fetch-h2: 2.3.0 + oas-kit-common: 1.0.8 + reftools: 1.1.9 + yaml: 1.10.2 + yargs: 17.7.2 + + oas-schema-walker@1.1.5: {} + + oas-validator@5.0.8: + dependencies: + call-me-maybe: 1.0.2 + oas-kit-common: 1.0.8 + oas-linter: 3.2.2 + oas-resolver: 2.5.6 + oas-schema-walker: 1.1.5 + reftools: 1.1.9 + should: 13.2.3 + yaml: 1.10.2 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.24.2 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + path-type@4.0.0: {} + + picocolors@1.0.0: {} + + prettier@3.0.0: {} + + protobufjs@6.11.4: + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.4 + '@protobufjs/eventemitter': 1.1.0 + '@protobufjs/fetch': 1.1.0 + '@protobufjs/float': 1.0.2 + '@protobufjs/inquire': 1.1.0 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.0 + '@types/long': 4.0.2 + '@types/node': 20.12.11 + long: 4.0.0 + + reftools@1.1.9: {} + + require-directory@2.1.1: {} + + resolve-from@4.0.0: {} + + semver@7.6.1: {} + + should-equal@2.0.0: + dependencies: + should-type: 1.4.0 + + should-format@3.0.3: + dependencies: + should-type: 1.4.0 + should-type-adaptors: 1.1.0 + + should-type-adaptors@1.1.0: + dependencies: + should-type: 1.4.0 + should-util: 1.0.1 + + should-type@1.4.0: {} + + should-util@1.0.1: {} + + should@13.2.3: + dependencies: + should-equal: 2.0.0 + should-format: 3.0.3 + should-type: 1.4.0 + should-type-adaptors: 1.1.0 + should-util: 1.0.1 + + skin-tone@2.0.0: + dependencies: + unicode-emoji-modifier-base: 1.0.0 + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + supports-color@5.5.0: + dependencies: + has-flag: 3.0.0 + + swagger-schema-official@2.0.0-bab6bed: {} + + swagger-typescript-api@13.0.3: + dependencies: + '@types/swagger-schema-official': 2.0.22 + cosmiconfig: 8.2.0 + didyoumean: 1.2.2 + eta: 2.2.0 + js-yaml: 4.1.0 + lodash: 4.17.21 + make-dir: 4.0.0 + nanoid: 3.3.6 + node-emoji: 2.1.0 + node-fetch: 3.3.2 + prettier: 3.0.0 + swagger-schema-official: 2.0.0-bab6bed + swagger2openapi: 7.0.8 + typescript: 5.1.6 + transitivePeerDependencies: + - encoding + + swagger2openapi@7.0.8: + dependencies: + call-me-maybe: 1.0.2 + node-fetch: 2.7.0 + node-fetch-h2: 2.3.0 + node-readfiles: 0.2.0 + oas-kit-common: 1.0.8 + oas-resolver: 2.5.6 + oas-schema-walker: 1.1.5 + oas-validator: 5.0.8 + reftools: 1.1.9 + yaml: 1.10.2 + yargs: 17.7.2 + transitivePeerDependencies: + - encoding + + tiktok-live-connector@1.1.6: + dependencies: + axios: 0.25.0 + protobufjs: 6.11.4 + websocket: 1.0.34 + transitivePeerDependencies: + - debug + - supports-color + + tr46@0.0.3: {} + + type@2.7.2: {} + + typedarray-to-buffer@3.1.5: + dependencies: + is-typedarray: 1.0.0 + + typescript@5.1.6: {} + + undici-types@5.26.5: {} + + unicode-emoji-modifier-base@1.0.0: {} + + utf-8-validate@5.0.10: + dependencies: + node-gyp-build: 4.8.1 + + web-streams-polyfill@3.3.3: {} + + webidl-conversions@3.0.1: {} + + websocket@1.0.34: + dependencies: + bufferutil: 4.0.8 + debug: 2.6.9 + es5-ext: 0.10.64 + typedarray-to-buffer: 3.1.5 + utf-8-validate: 5.0.10 + yaeti: 0.0.6 + transitivePeerDependencies: + - supports-color + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + y18n@5.0.8: {} + + yaeti@0.0.6: {} + + yaml@1.10.2: {} + + yargs-parser@21.1.1: {} + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.1.2 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 diff --git a/node/source/classes/controller/GameController.ts b/node/source/classes/controller/GameController.ts new file mode 100644 index 0000000..386be35 --- /dev/null +++ b/node/source/classes/controller/GameController.ts @@ -0,0 +1,117 @@ +import { MgbaHttp } from "../../classes/gba-api/MgbaHttp"; +import { Message } from "../../classes/tiktok/Message"; +import { WebcastPushConnection } from "tiktok-live-connector"; +import { User, FollowerRole } from "../../classes/tiktok/User"; +import { Gift } from "../tiktok/Gift"; + +export class GameController { + private channelName: string; + private users: Map = new Map(); + private webcastPushConnection: WebcastPushConnection; + private mgbaHttp: MgbaHttp = new MgbaHttp({}); + private availableGifts: Map = new Map(); + + constructor(channelName: string) { + this.channelName = channelName; + this.mgbaHttp = new MgbaHttp({ + baseUrl: "http://localhost:5000", + }); + + this.webcastPushConnection = new WebcastPushConnection( + this.channelName + ); + + this.getWebcastPushConnection().getAvailableGifts().then((gifts) => { + gifts.forEach((gift: any) => { + this.availableGifts.set(gift.id, new Gift({ + giftId: gift.id, + costDiamonds: gift.diamond_count, + name: gift.name, + })); + }); + }); + } + + /** + * Get the WebcastPushConnection object for the TikTok Live connection + * @returns {WebcastPushConnection} The WebcastPushConnection object for the TikTok Live connection + */ + private getWebcastPushConnection(): WebcastPushConnection { + return this.webcastPushConnection; + } + + /** + * Get the users Map object, for all users in the TikTok Live that have done some action + * @returns {Map} The Map of users in the TikTok Live + */ + private getUsers(): Map { + return this.users; + } + + /** + * Create a new User object if it does not exist, otherwise return the existing User object + * @param {string} userId The unique identifier for the user + * @param {any} data The data object for the user, can be null for searching existing users + * @returns {User} The User object for the user + */ + private newUser(userId: string, data: any): User { + if (this.getUsers().has(userId)) { + return this.getUsers().get(userId); + } else { + let newUser = new User({ + userId: userId, + displayName: data.nickname, + username: data.uniqueId, + isFollower: data.followRole === FollowerRole.FOLLOWER, + isSubscriber: data.isSubscriber, + }); + this.getUsers().set(userId, newUser); + return newUser; + } + } + + /** + * Handle the chat event from the TikTok Live connection + * @param {any} data The data object for the chat event + */ + private chatHandler(data: any): void { + let user = this.newUser(data.userId, data); + let message = new Message({ + messageId: data.msgId, + content: data.comment, + }); + let gameKeyInterpretation = message.getMessageGameKey(); + if (gameKeyInterpretation !== false) { + this.mgbaHttp + .buttonTapCreate({ + key: gameKeyInterpretation.toString(), + }) + .then(() => { + console.log( + `${user.getUsername} sent button tap for ${gameKeyInterpretation}` + ); + }); + } + } + + /** + * Handle the gift event from the TikTok Live connection + * @param {any} data The data object for the gift event + */ + private giftHandler(data: any): void { + + } + + public start(): void { + this.getWebcastPushConnection() + .connect() + .then(() => { + console.log("Connected to TikTok Live"); + this.webcastPushConnection.on("chat", this.chatHandler.bind(this)); + this.webcastPushConnection.on("gift", this.giftHandler.bind(this)); + }) + .catch((error) => { + console.error("Failed to connect", error); + }); + } +} diff --git a/node/source/classes/gba-api/Console.ts b/node/source/classes/gba-api/Console.ts new file mode 100644 index 0000000..0947c6b --- /dev/null +++ b/node/source/classes/gba-api/Console.ts @@ -0,0 +1,75 @@ +/* eslint-disable */ +/* tslint:disable */ +/* + * --------------------------------------------------------------- + * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## + * ## ## + * ## AUTHOR: acacode ## + * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## + * --------------------------------------------------------------- + */ + +import { HttpClient, RequestParams } from "./http-client"; + +export class Console extends HttpClient { + /** + * @description Presents textual information to the user via a console. + * + * @tags Console + * @name ErrorCreate + * @summary Print an error to the console. + * @request POST:/console/error + */ + errorCreate = ( + query: { + message: string; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/console/error`, + method: "POST", + query: query, + ...params, + }); + /** + * @description Presents textual information to the user via a console. + * + * @tags Console + * @name PostConsole + * @summary Print a log to the console. + * @request POST:/console/log + */ + postConsole = ( + query: { + message: string; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/console/log`, + method: "POST", + query: query, + ...params, + }); + /** + * @description Presents textual information to the user via a console. + * + * @tags Console + * @name WarnCreate + * @summary Print a warning to the console. + * @request POST:/console/warn + */ + warnCreate = ( + query: { + message: string; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/console/warn`, + method: "POST", + query: query, + ...params, + }); +} diff --git a/node/source/classes/gba-api/Core.ts b/node/source/classes/gba-api/Core.ts new file mode 100644 index 0000000..95ee4ee --- /dev/null +++ b/node/source/classes/gba-api/Core.ts @@ -0,0 +1,723 @@ +/* eslint-disable */ +/* tslint:disable */ +/* + * --------------------------------------------------------------- + * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## + * ## ## + * ## AUTHOR: acacode ## + * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## + * --------------------------------------------------------------- + */ + +import { HttpClient, RequestParams } from "./http-client"; + +export class Core extends HttpClient { + /** + * @description Add a single key to the currently active key list. As if the key were held down. + * + * @tags Core + * @name AddkeyCreate + * @summary Add a single key. + * @request POST:/core/addkey + */ + addkeyCreate = ( + query: { + /** Key value of: A, B, Start, Select, Start, Right, Left, Up, Down, R, or L. */ + key: string; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/core/addkey`, + method: "POST", + query: query, + ...params, + }); + /** + * @description Add a bitmask of keys to the currently active key list. As if the keys were held down. + * + * @tags Core + * @name AddkeysCreate + * @summary Add a bitmask of keys. + * @request POST:/core/addkeys + */ + addkeysCreate = ( + query: { + /** + * Bitmasking has the keys from the mGBA scripting documentation where each key has a value that goes up in that order in binary: A = 1, B = 2, Select = 4, Start = 8, etc. A bitmask of 12 is Start and Select. + * @format int32 + */ + keyBitmask: number; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/core/addkeys`, + method: "POST", + query: query, + ...params, + }); + /** + * @description Load the save data associated with the currently loaded ROM file. + * + * @tags Core + * @name AutoloadsaveCreate + * @summary Load save data. + * @request POST:/core/autoloadsave + */ + autoloadsaveCreate = (params: RequestParams = {}) => + this.request({ + path: `/core/autoloadsave`, + method: "POST", + ...params, + }); + /** + * @description Get the checksum of the loaded ROM as base 10. + * + * @tags Core + * @name ChecksumList + * @summary Get the checksum of the loaded ROM. + * @request GET:/core/checksum + */ + checksumList = (params: RequestParams = {}) => + this.request({ + path: `/core/checksum`, + method: "GET", + ...params, + }); + /** + * @description Remove a single key from the currently active key list. As if the key were released. + * + * @tags Core + * @name ClearkeyCreate + * @summary Remove a single key. + * @request POST:/core/clearkey + */ + clearkeyCreate = ( + query: { + /** Key value of: A, B, Start, Select, Start, Right, Left, Up, Down, R, or L. */ + key: string; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/core/clearkey`, + method: "POST", + query: query, + ...params, + }); + /** + * @description Remove a bitmask of keys from the currently active key list. As if the keys were released. + * + * @tags Core + * @name ClearkeysCreate + * @summary Remove a bitmask of keys. + * @request POST:/core/clearkeys + */ + clearkeysCreate = ( + query: { + /** + * Bitmasking has the keys from the mGBA scripting documentation where each key has a value that goes up in that order in binary: A = 1, B = 2, Select = 4, Start = 8, etc. A bitmask of 12 is Start and Select. + * @format int32 + */ + keyBitmask: number; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/core/clearkeys`, + method: "POST", + query: query, + ...params, + }); + /** + * @description Get the number of the current frame. + * + * @tags Core + * @name CurrentFrameList + * @summary Get the number of the current frame. + * @request GET:/core/currentFrame + */ + currentFrameList = (params: RequestParams = {}) => + this.request({ + path: `/core/currentFrame`, + method: "GET", + ...params, + }); + /** + * @description Get the number of cycles per frame. + * + * @tags Core + * @name FramecyclesList + * @summary Get the number of cycles per frame. + * @request GET:/core/framecycles + */ + framecyclesList = (params: RequestParams = {}) => + this.request({ + path: `/core/framecycles`, + method: "GET", + ...params, + }); + /** + * @description Get the number of cycles per second. + * + * @tags Core + * @name FrequencyList + * @summary Get the number of cycles per second. + * @request GET:/core/frequency + */ + frequencyList = (params: RequestParams = {}) => + this.request({ + path: `/core/frequency`, + method: "GET", + ...params, + }); + /** + * @description Get internal product code for the game from the ROM header, if available. + * + * @tags Core + * @name GetgamecodeList + * @summary Get internal product code for the game. + * @request GET:/core/getgamecode + */ + getgamecodeList = (params: RequestParams = {}) => + this.request({ + path: `/core/getgamecode`, + method: "GET", + ...params, + }); + /** + * @description Get internal title of the game from the ROM header. + * + * @tags Core + * @name GetgametitleList + * @summary Get internal title of the game. + * @request GET:/core/getgametitle + */ + getgametitleList = (params: RequestParams = {}) => + this.request({ + path: `/core/getgametitle`, + method: "GET", + ...params, + }); + /** + * @description Get the active state of a given key. + * + * @tags Core + * @name GetkeyList + * @summary Get the active state of a given key. + * @request GET:/core/getkey + */ + getkeyList = ( + query: { + /** Key value of: A, B, Start, Select, Start, Right, Left, Up, Down, R, or L. */ + key: string; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/core/getkey`, + method: "GET", + query: query, + ...params, + }); + /** + * @description Get the currently active keys as a bitmask. + * + * @tags Core + * @name GetkeysList + * @summary Get the currently active keys as a bitmask. + * @request GET:/core/getkeys + */ + getkeysList = (params: RequestParams = {}) => + this.request({ + path: `/core/getkeys`, + method: "GET", + ...params, + }); + /** + * @description Load a ROM file into the current state of this core. + * + * @tags Core + * @name LoadfileCreate + * @summary Load a ROM file. + * @request POST:/core/loadfile + */ + loadfileCreate = ( + query: { + path: string; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/core/loadfile`, + method: "POST", + query: query, + ...params, + }); + /** + * @description Load save data from the given path. If the temporary flag is set, the given save data will not be written back to disk. + * + * @tags Core + * @name LoadsavefileCreate + * @summary Load save data file. + * @request POST:/core/loadsavefile + */ + loadsavefileCreate = ( + query: { + path: string; + temporary: boolean; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/core/loadsavefile`, + method: "POST", + query: query, + ...params, + }); + /** + * @description Load state from a buffer. + * + * @tags Core + * @name LoadstatebufferCreate + * @summary Load state from a buffer. + * @request POST:/core/loadstatebuffer + */ + loadstatebufferCreate = ( + query: { + buffer: string; + /** + * @format int32 + * @default 29 + */ + flags?: number; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/core/loadstatebuffer`, + method: "POST", + query: query, + ...params, + }); + /** + * @description Load state from the given path. + * + * @tags Core + * @name LoadstatefileCreate + * @summary Load state from the given path. + * @request POST:/core/loadstatefile + */ + loadstatefileCreate = ( + query: { + path: string; + /** + * @format int32 + * @default 29 + */ + flags?: number; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/core/loadstatefile`, + method: "POST", + query: query, + ...params, + }); + /** + * @description Load state from the slot number. + * + * @tags Core + * @name LoadstateslotCreate + * @summary Load state from the slot number. + * @request POST:/core/loadstateslot + */ + loadstateslotCreate = ( + query: { + slot: string; + /** + * @format int32 + * @default 29 + */ + flags?: number; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/core/loadstateslot`, + method: "POST", + query: query, + ...params, + }); + /** + * @description Get which platform is being emulated. + * + * @tags Core + * @name PlatformList + * @summary Get which platform is being emulated. + * @request GET:/core/platform + */ + platformList = (params: RequestParams = {}) => + this.request({ + path: `/core/platform`, + method: "GET", + ...params, + }); + /** + * @description Read a 16-bit value from the given bus address. + * + * @tags Core + * @name Read16List + * @summary Read a 16-bit value from the given bus address. + * @request GET:/core/read16 + */ + read16List = ( + query: { + /** Address in hex, e.g. 0x03000000 */ + address: string; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/core/read16`, + method: "GET", + query: query, + ...params, + }); + /** + * @description Read a 32-bit value from the given bus address. + * + * @tags Core + * @name Read32List + * @summary Read a 32-bit value from the given bus address. + * @request GET:/core/read32 + */ + read32List = ( + query: { + /** Address in hex, e.g. 0x03000000 */ + address: string; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/core/read32`, + method: "GET", + query: query, + ...params, + }); + /** + * @description Read an 8-bit value from the given bus address. + * + * @tags Core + * @name Read8List + * @summary Read an 8-bit value from the given bus address. + * @request GET:/core/read8 + */ + read8List = ( + query: { + /** Address in hex, e.g. 0x03000000 */ + address: string; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/core/read8`, + method: "GET", + query: query, + ...params, + }); + /** + * @description Read byte range from the given offset. + * + * @tags Core + * @name ReadrangeList + * @summary Read byte range from the given offset. + * @request GET:/core/readrange + */ + readrangeList = ( + query: { + /** Address in hex, e.g. 0x03000000 */ + address: string; + length: string; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/core/readrange`, + method: "GET", + query: query, + ...params, + }); + /** + * @description Read the value of the register with the given name. + * + * @tags Core + * @name ReadregisterList + * @summary Read the value of the register with the given name. + * @request GET:/core/readregister + */ + readregisterList = ( + query: { + regName: string; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/core/readregister`, + method: "GET", + query: query, + ...params, + }); + /** + * @description Get the size of the loaded ROM. + * + * @tags Core + * @name RomsizeList + * @summary Get the size of the loaded ROM. + * @request GET:/core/romsize + */ + romsizeList = (params: RequestParams = {}) => + this.request({ + path: `/core/romsize`, + method: "GET", + ...params, + }); + /** + * @description Run until the next frame. + * + * @tags Core + * @name RunFrameCreate + * @summary Run until the next frame. + * @request POST:/core/runFrame + */ + runFrameCreate = (params: RequestParams = {}) => + this.request({ + path: `/core/runFrame`, + method: "POST", + ...params, + }); + /** + * @description Save state and return as a buffer. + * + * @tags Core + * @name SavestatebufferCreate + * @summary Save state and return as a buffer. + * @request POST:/core/savestatebuffer + */ + savestatebufferCreate = ( + query?: { + /** + * @format int32 + * @default 31 + */ + flags?: number; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/core/savestatebuffer`, + method: "POST", + query: query, + ...params, + }); + /** + * @description Save state to the given path. + * + * @tags Core + * @name SavestatefileCreate + * @summary Save state to the given path. + * @request POST:/core/savestatefile + */ + savestatefileCreate = ( + query: { + path: string; + /** + * @format int32 + * @default 31 + */ + flags?: number; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/core/savestatefile`, + method: "POST", + query: query, + ...params, + }); + /** + * @description Save state to the slot number. + * + * @tags Core + * @name SavestateslotCreate + * @summary Save state to the slot number. + * @request POST:/core/savestateslot + */ + savestateslotCreate = ( + query: { + slot: string; + /** + * @format int32 + * @default 31 + */ + flags?: number; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/core/savestateslot`, + method: "POST", + query: query, + ...params, + }); + /** + * @description Save a screenshot. + * + * @tags Core + * @name ScreenshotCreate + * @summary Save a screenshot. + * @request POST:/core/screenshot + */ + screenshotCreate = ( + query: { + /** For example, C:\screenshots\screenshot.png */ + path: string; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/core/screenshot`, + method: "POST", + query: query, + ...params, + }); + /** + * @description Set the currently active key list. + * + * @tags Core + * @name SetkeysCreate + * @summary Set the currently active key list. + * @request POST:/core/setkeys + */ + setkeysCreate = ( + query: { + /** @format int32 */ + keys: number; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/core/setkeys`, + method: "POST", + query: query, + ...params, + }); + /** + * @description Run a single instruction. + * + * @tags Core + * @name StepCreate + * @summary Run a single instruction. + * @request POST:/core/step + */ + stepCreate = (params: RequestParams = {}) => + this.request({ + path: `/core/step`, + method: "POST", + ...params, + }); + /** + * @description Write a 16-bit value from the given bus address. + * + * @tags Core + * @name Write16Create + * @summary Write a 16-bit value from the given bus address. + * @request POST:/core/write16 + */ + write16Create = ( + query: { + /** Address in hex, e.g. 0x03000000 */ + address: string; + /** @format int32 */ + value: number; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/core/write16`, + method: "POST", + query: query, + ...params, + }); + /** + * @description Write a 32-bit value from the given bus address. + * + * @tags Core + * @name Write32Create + * @summary Write a 32-bit value from the given bus address. + * @request POST:/core/write32 + */ + write32Create = ( + query: { + /** Address in hex, e.g. 0x03000000 */ + address: string; + /** @format int32 */ + value: number; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/core/write32`, + method: "POST", + query: query, + ...params, + }); + /** + * @description Write an 8-bit value from the given bus address. + * + * @tags Core + * @name Write8Create + * @summary Write an 8-bit value from the given bus address. + * @request POST:/core/write8 + */ + write8Create = ( + query: { + /** Address in hex, e.g. 0x03000000 */ + address: string; + /** @format int32 */ + value: number; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/core/write8`, + method: "POST", + query: query, + ...params, + }); + /** + * @description Write the value of the register with the given name. + * + * @tags Core + * @name WriteregisterCreate + * @summary Write the value of the register with the given name. + * @request POST:/core/writeregister + */ + writeregisterCreate = ( + query: { + /** Address in hex, e.g. 0x03000000 */ + regName: string; + /** @format int32 */ + value: number; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/core/writeregister`, + method: "POST", + query: query, + ...params, + }); +} diff --git a/node/source/classes/gba-api/Coreadapter.ts b/node/source/classes/gba-api/Coreadapter.ts new file mode 100644 index 0000000..d6a0a48 --- /dev/null +++ b/node/source/classes/gba-api/Coreadapter.ts @@ -0,0 +1,29 @@ +/* eslint-disable */ +/* tslint:disable */ +/* + * --------------------------------------------------------------- + * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## + * ## ## + * ## AUTHOR: acacode ## + * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## + * --------------------------------------------------------------- + */ + +import { HttpClient, RequestParams } from "./http-client"; + +export class Coreadapter extends HttpClient { + /** + * @description Reset the emulation and calls the reset callback. + * + * @tags CoreAdapter + * @name ResetCreate + * @summary Reset the emulation. + * @request POST:/coreadapter/reset + */ + resetCreate = (params: RequestParams = {}) => + this.request({ + path: `/coreadapter/reset`, + method: "POST", + ...params, + }); +} diff --git a/node/source/classes/gba-api/Memorydomain.ts b/node/source/classes/gba-api/Memorydomain.ts new file mode 100644 index 0000000..ce1ed60 --- /dev/null +++ b/node/source/classes/gba-api/Memorydomain.ts @@ -0,0 +1,256 @@ +/* eslint-disable */ +/* tslint:disable */ +/* + * --------------------------------------------------------------- + * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## + * ## ## + * ## AUTHOR: acacode ## + * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## + * --------------------------------------------------------------- + */ + +import { HttpClient, RequestParams } from "./http-client"; + +export class Memorydomain extends HttpClient { + /** + * @description Get the address of the base of this memory domain. + * + * @tags MemoryDomain + * @name BaseList + * @summary Get the address of the base of this memory domain. + * @request GET:/memorydomain/base + */ + baseList = ( + query: { + memoryDomain: string; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/memorydomain/base`, + method: "GET", + query: query, + ...params, + }); + /** + * @description Get the address of the end bound of this memory domain. Note that this address is not in the domain itself, and is the address of the first byte past it. + * + * @tags MemoryDomain + * @name BoundList + * @summary Get the address of the end bound of this memory domain. + * @request GET:/memorydomain/bound + */ + boundList = ( + query: { + memoryDomain: string; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/memorydomain/bound`, + method: "GET", + query: query, + ...params, + }); + /** + * @description Get a short, human-readable name for this memory domain. + * + * @tags MemoryDomain + * @name NameList + * @summary Get a short, human-readable name for this memory domain. + * @request GET:/memorydomain/name + */ + nameList = ( + query: { + memoryDomain: string; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/memorydomain/name`, + method: "GET", + query: query, + ...params, + }); + /** + * @description Read a 16-bit value from the given offset. + * + * @tags MemoryDomain + * @name Read16List + * @summary Read a 16-bit value from the given offset. + * @request GET:/memorydomain/read16 + */ + read16List = ( + query: { + memoryDomain: string; + /** Address in hex, e.g. 0x03000000 */ + address: string; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/memorydomain/read16`, + method: "GET", + query: query, + ...params, + }); + /** + * @description Read a 32-bit value from the given offset. + * + * @tags MemoryDomain + * @name Read32List + * @summary Read a 32-bit value from the given offset. + * @request GET:/memorydomain/read32 + */ + read32List = ( + query: { + memoryDomain: string; + /** Address in hex, e.g. 0x03000000 */ + address: string; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/memorydomain/read32`, + method: "GET", + query: query, + ...params, + }); + /** + * @description Read an 8-bit value from the given offset. + * + * @tags MemoryDomain + * @name Read8List + * @summary Read an 8-bit value from the given offset. + * @request GET:/memorydomain/read8 + */ + read8List = ( + query: { + memoryDomain: string; + /** Address in hex, e.g. 0x03000000 */ + address: string; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/memorydomain/read8`, + method: "GET", + query: query, + ...params, + }); + /** + * @description Read byte range from the given offset. + * + * @tags MemoryDomain + * @name ReadrangeList + * @summary Read byte range from the given offset. + * @request GET:/memorydomain/readrange + */ + readrangeList = ( + query: { + memoryDomain: string; + /** Address in hex, e.g. 0x03000000 */ + address: string; + length: string; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/memorydomain/readrange`, + method: "GET", + query: query, + ...params, + }); + /** + * @description Get the size of this memory domain in bytes. + * + * @tags MemoryDomain + * @name SizeList + * @summary Get the size of this memory domain in bytes. + * @request GET:/memorydomain/size + */ + sizeList = ( + query: { + memoryDomain: string; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/memorydomain/size`, + method: "GET", + query: query, + ...params, + }); + /** + * @description Write a 16-bit value from the given offset. + * + * @tags MemoryDomain + * @name Write16Create + * @summary Write a 16-bit value from the given offset. + * @request POST:/memorydomain/write16 + */ + write16Create = ( + query: { + memoryDomain: string; + /** Address in hex, e.g. 0x03000000 */ + address: string; + /** @format int32 */ + value: number; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/memorydomain/write16`, + method: "POST", + query: query, + ...params, + }); + /** + * @description Write a 32-bit value from the given offset. + * + * @tags MemoryDomain + * @name Write32Create + * @summary Write a 32-bit value from the given offset. + * @request POST:/memorydomain/write32 + */ + write32Create = ( + query: { + memoryDomain: string; + /** Address in hex, e.g. 0x03000000 */ + address: string; + /** @format int32 */ + value: number; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/memorydomain/write32`, + method: "POST", + query: query, + ...params, + }); + /** + * @description Write an 8-bit value from the given offset. + * + * @tags MemoryDomain + * @name Write8Create + * @summary Write an 8-bit value from the given offset. + * @request POST:/memorydomain/write8 + */ + write8Create = ( + query: { + memoryDomain: string; + /** Address in hex, e.g. 0x03000000 */ + address: string; + /** @format int32 */ + value: number; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/memorydomain/write8`, + method: "POST", + query: query, + ...params, + }); +} diff --git a/node/source/classes/gba-api/MgbaHttp.ts b/node/source/classes/gba-api/MgbaHttp.ts new file mode 100644 index 0000000..0136998 --- /dev/null +++ b/node/source/classes/gba-api/MgbaHttp.ts @@ -0,0 +1,104 @@ +/* eslint-disable */ +/* tslint:disable */ +/* + * --------------------------------------------------------------- + * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## + * ## ## + * ## AUTHOR: acacode ## + * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## + * --------------------------------------------------------------- + */ + +import { KeysEnum } from "./data-contracts"; +import { HttpClient, RequestParams } from "./http-client"; + +export class MgbaHttp extends HttpClient { + /** + * @description A custom convenience API that implements a key press and release. This is as opposed to the key based core API that sends only either a press or release message. + * + * @tags Button + * @name ButtonTapCreate + * @summary Sends button presses. + * @request POST:/mgba-http/button/tap + */ + buttonTapCreate = ( + query: { + /** Key value of: A, B, Start, Select, Start, Right, Left, Up, Down, R, or L. */ + key: string; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/mgba-http/button/tap`, + method: "POST", + query: query, + ...params, + }); + /** + * @description A custom convenience API that implements multiple simultaneously keys being pressed and released. This is as opposed to the key based core API that sends only either a press or release message. + * + * @tags Button + * @name ButtonTapmanyCreate + * @summary Sends multiple button presses simultaneously. + * @request POST:/mgba-http/button/tapmany + */ + buttonTapmanyCreate = ( + query: { + /** Key array containing any of: A, B, Start, Select, Start, Right, Left, Up, Down, R, or L. */ + keys: KeysEnum[]; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/mgba-http/button/tapmany`, + method: "POST", + query: query, + ...params, + }); + /** + * @description A custom convenience API that implements a held down button for a given duration in frames. This is as opposed to the key based core API that sends only either a press or release message. + * + * @tags Button + * @name ButtonHoldCreate + * @summary Sends a held down button for a given duration in frames. + * @request POST:/mgba-http/button/hold + */ + buttonHoldCreate = ( + query: { + /** Key value of: A, B, Start, Select, Start, Right, Left, Up, Down, R, or L. */ + key: string; + /** @format int32 */ + duration: number; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/mgba-http/button/hold`, + method: "POST", + query: query, + ...params, + }); + /** + * @description A custom convenience API that implements multiple simultaneously keys being pressed and released for a given duration in frames. This is as opposed to the key based core API that sends only either a press or release message. + * + * @tags Button + * @name ButtonHoldmanyCreate + * @summary Sends multiple button presses simultaneously for a given duration in frames. + * @request POST:/mgba-http/button/holdmany + */ + buttonHoldmanyCreate = ( + query: { + /** Key array containing any of: A, B, Start, Select, Start, Right, Left, Up, Down, R, or L. */ + keys: KeysEnum[]; + /** @format int32 */ + duration: number; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/mgba-http/button/holdmany`, + method: "POST", + query: query, + ...params, + }); +} diff --git a/node/source/classes/gba-api/data-contracts.ts b/node/source/classes/gba-api/data-contracts.ts new file mode 100644 index 0000000..8a9b344 --- /dev/null +++ b/node/source/classes/gba-api/data-contracts.ts @@ -0,0 +1,24 @@ +/* eslint-disable */ +/* tslint:disable */ +/* + * --------------------------------------------------------------- + * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## + * ## ## + * ## AUTHOR: acacode ## + * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## + * --------------------------------------------------------------- + */ + +/** @format int32 */ +export enum KeysEnum { + Value0 = 0, + Value1 = 1, + Value2 = 2, + Value3 = 3, + Value4 = 4, + Value5 = 5, + Value6 = 6, + Value7 = 7, + Value8 = 8, + Value9 = 9, +} diff --git a/node/source/classes/gba-api/http-client.ts b/node/source/classes/gba-api/http-client.ts new file mode 100644 index 0000000..238b2f5 --- /dev/null +++ b/node/source/classes/gba-api/http-client.ts @@ -0,0 +1,220 @@ +/* eslint-disable */ +/* tslint:disable */ +/* + * --------------------------------------------------------------- + * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## + * ## ## + * ## AUTHOR: acacode ## + * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## + * --------------------------------------------------------------- + */ + +export type QueryParamsType = Record; +export type ResponseFormat = keyof Omit; + +export interface FullRequestParams extends Omit { + /** set parameter to `true` for call `securityWorker` for this request */ + secure?: boolean; + /** request path */ + path: string; + /** content type of request body */ + type?: ContentType; + /** query params */ + query?: QueryParamsType; + /** format of response (i.e. response.json() -> format: "json") */ + format?: ResponseFormat; + /** request body */ + body?: unknown; + /** base url */ + baseUrl?: string; + /** request cancellation token */ + cancelToken?: CancelToken; +} + +export type RequestParams = Omit; + +export interface ApiConfig { + baseUrl?: string; + baseApiParams?: Omit; + securityWorker?: (securityData: SecurityDataType | null) => Promise | RequestParams | void; + customFetch?: typeof fetch; +} + +export interface HttpResponse extends Response { + data: D; + error: E; +} + +type CancelToken = Symbol | string | number; + +export enum ContentType { + Json = "application/json", + FormData = "multipart/form-data", + UrlEncoded = "application/x-www-form-urlencoded", + Text = "text/plain", +} + +export class HttpClient { + public baseUrl: string = ""; + private securityData: SecurityDataType | null = null; + private securityWorker?: ApiConfig["securityWorker"]; + private abortControllers = new Map(); + private customFetch = (...fetchParams: Parameters) => fetch(...fetchParams); + + private baseApiParams: RequestParams = { + credentials: "same-origin", + headers: {}, + redirect: "follow", + referrerPolicy: "no-referrer", + }; + + constructor(apiConfig: ApiConfig = {}) { + Object.assign(this, apiConfig); + } + + public setSecurityData = (data: SecurityDataType | null) => { + this.securityData = data; + }; + + protected encodeQueryParam(key: string, value: any) { + const encodedKey = encodeURIComponent(key); + return `${encodedKey}=${encodeURIComponent(typeof value === "number" ? value : `${value}`)}`; + } + + protected addQueryParam(query: QueryParamsType, key: string) { + return this.encodeQueryParam(key, query[key]); + } + + protected addArrayQueryParam(query: QueryParamsType, key: string) { + const value = query[key]; + return value.map((v: any) => this.encodeQueryParam(key, v)).join("&"); + } + + protected toQueryString(rawQuery?: QueryParamsType): string { + const query = rawQuery || {}; + const keys = Object.keys(query).filter((key) => "undefined" !== typeof query[key]); + return keys + .map((key) => (Array.isArray(query[key]) ? this.addArrayQueryParam(query, key) : this.addQueryParam(query, key))) + .join("&"); + } + + protected addQueryParams(rawQuery?: QueryParamsType): string { + const queryString = this.toQueryString(rawQuery); + return queryString ? `?${queryString}` : ""; + } + + private contentFormatters: Record any> = { + [ContentType.Json]: (input: any) => + input !== null && (typeof input === "object" || typeof input === "string") ? JSON.stringify(input) : input, + [ContentType.Text]: (input: any) => (input !== null && typeof input !== "string" ? JSON.stringify(input) : input), + [ContentType.FormData]: (input: any) => + Object.keys(input || {}).reduce((formData, key) => { + const property = input[key]; + formData.append( + key, + property instanceof Blob + ? property + : typeof property === "object" && property !== null + ? JSON.stringify(property) + : `${property}`, + ); + return formData; + }, new FormData()), + [ContentType.UrlEncoded]: (input: any) => this.toQueryString(input), + }; + + protected mergeRequestParams(params1: RequestParams, params2?: RequestParams): RequestParams { + return { + ...this.baseApiParams, + ...params1, + ...(params2 || {}), + headers: { + ...(this.baseApiParams.headers || {}), + ...(params1.headers || {}), + ...((params2 && params2.headers) || {}), + }, + }; + } + + protected createAbortSignal = (cancelToken: CancelToken): AbortSignal | undefined => { + if (this.abortControllers.has(cancelToken)) { + const abortController = this.abortControllers.get(cancelToken); + if (abortController) { + return abortController.signal; + } + return void 0; + } + + const abortController = new AbortController(); + this.abortControllers.set(cancelToken, abortController); + return abortController.signal; + }; + + public abortRequest = (cancelToken: CancelToken) => { + const abortController = this.abortControllers.get(cancelToken); + + if (abortController) { + abortController.abort(); + this.abortControllers.delete(cancelToken); + } + }; + + public request = async ({ + body, + secure, + path, + type, + query, + format, + baseUrl, + cancelToken, + ...params + }: FullRequestParams): Promise> => { + const secureParams = + ((typeof secure === "boolean" ? secure : this.baseApiParams.secure) && + this.securityWorker && + (await this.securityWorker(this.securityData))) || + {}; + const requestParams = this.mergeRequestParams(params, secureParams); + const queryString = query && this.toQueryString(query); + const payloadFormatter = this.contentFormatters[type || ContentType.Json]; + const responseFormat = format || requestParams.format; + + return this.customFetch(`${baseUrl || this.baseUrl || ""}${path}${queryString ? `?${queryString}` : ""}`, { + ...requestParams, + headers: { + ...(requestParams.headers || {}), + ...(type && type !== ContentType.FormData ? { "Content-Type": type } : {}), + }, + signal: (cancelToken ? this.createAbortSignal(cancelToken) : requestParams.signal) || null, + body: typeof body === "undefined" || body === null ? null : payloadFormatter(body), + }).then(async (response) => { + const r = response as HttpResponse; + r.data = null as unknown as T; + r.error = null as unknown as E; + + const data = !responseFormat + ? r + : await response[responseFormat]() + .then((data) => { + if (r.ok) { + r.data = data; + } else { + r.error = data; + } + return r; + }) + .catch((e) => { + r.error = e; + return r; + }); + + if (cancelToken) { + this.abortControllers.delete(cancelToken); + } + + if (!response.ok) throw data; + return data; + }); + }; +} diff --git a/node/source/classes/tiktok/Gift.ts b/node/source/classes/tiktok/Gift.ts new file mode 100644 index 0000000..680e863 --- /dev/null +++ b/node/source/classes/tiktok/Gift.ts @@ -0,0 +1,57 @@ +export class Gift { + private giftId: number; + private costDiamonds: number; + private name: string; + + /** + * Create a new Gift object + * @param data { giftId: number, costDiamonds: number, name: string} + */ + constructor(data: { giftId: number, costDiamonds: number, name: string }) { + this.giftId = data.giftId; + this.costDiamonds = data.costDiamonds; + this.name = data.name; + } + + /** + * Get the unique identifier for the gift + * @returns {number} The unique identifier for the gift + */ + public getGiftId(): number { + return this.giftId; + } + + /** + * Get the cost of the gift in diamonds + * @returns {number} The cost of the gift in diamonds + */ + public getCostDiamonds(): number { + return this.costDiamonds; + } + + /** + * Get the name of the gift + * @returns {string} The name of the gift + */ + public getName(): string { + return this.name; + } + + /** + * Get the estimated value of the diamonds in euros + * @param {boolean} withdrawableValue The withdrawable value of the gift for creators, otherwise the original value + * @returns {number} The value of the gift in euros + */ + public getEstimatedValueInEuro(withdrawableValue: boolean = true): number { + let leftValue; + if(withdrawableValue) { + leftValue = this.getCostDiamonds() / 2; + } else { + leftValue = this.getCostDiamonds(); + } + let valueInDollars = leftValue; + let valueInEuroCent = valueInDollars * 0.95; + let valueInEuro = valueInEuroCent / 100; + return Math.ceil(valueInEuro * 100) / 100; + } +} \ No newline at end of file diff --git a/node/source/classes/tiktok/Message.ts b/node/source/classes/tiktok/Message.ts new file mode 100644 index 0000000..ff458f6 --- /dev/null +++ b/node/source/classes/tiktok/Message.ts @@ -0,0 +1,46 @@ +export class Message { + private messageId: string; + private createdAt: Date; + private content: string; + + /** + * Create a new Message object + * @param data { messageId: string, content: string } + */ + constructor(data: { messageId: string, content: string }) { + this.messageId = data.messageId; + this.content = data.content; + this.createdAt = new Date(); + } + + /** + * Try to get the game key from the message content, otherwise return false + * @returns {string|boolean} Returns the game key for the GBA or false if it is not a valid game key + */ + public getMessageGameKey(): string|boolean { + switch(this.content.toLowerCase().trim()) { + case "start": + return "Start"; + case "select": + return "Select"; + case "a": + return "A"; + case "b": + return "B"; + case "r": + return "Right"; + case "l": + return "Left"; + case "u": + return "Up"; + case "d": + return "Down"; + case "rt": + return "R"; + case "lt": + return "L"; + default: + return false; + } + } +} \ No newline at end of file diff --git a/node/source/classes/tiktok/User.ts b/node/source/classes/tiktok/User.ts new file mode 100644 index 0000000..a95d8db --- /dev/null +++ b/node/source/classes/tiktok/User.ts @@ -0,0 +1,109 @@ +import { Gift } from "./Gift"; +import { Message } from "./Message"; + +/** + * The possible roles of a follower + */ +export enum FollowerRole { + NONE = 0, + FOLLOWER = 1, + FRIEND = 2, +} + +/** + * The User class represents a user in the TikTok Live chat + */ +export class User { + private userId: string; + private displayName: string; + private username: string; + private isFollower: boolean; + private isSubscriber: boolean; + private messages: Message[]; + private gifts: Gift[]; + + /** + * Create a new User object + * @param data { userId: string, displayName: string, username: string, isFollower: boolean, isSubscriber: boolean } + */ + constructor(data: { userId: string, displayName: string, username: string, isFollower: boolean, isSubscriber: boolean }) { + this.userId = data.userId; + this.displayName = data.displayName; + this.username = data.username; + this.isFollower = data.isFollower; + this.isSubscriber = data.isSubscriber; + this.messages = []; + } + + /** + * Get the unique identifier for the user + * @returns {string} The unique identifier for the user + */ + public getUserId(): string { + return this.userId; + } + + /** + * Get the display name for the user + * @returns {string} The display name for the user + */ + public getDisplayName(): string { + return this.displayName; + } + + /** + * Get the username for the user + * @returns {string} The username for the user + */ + public getUsername(): string { + return this.username; + } + + /** + * Get the role of the follower + * @returns {FollowerRole} The role of the follower + */ + public getIsFollower(): boolean { + return this.isFollower; + } + + /** + * Get the role of the subscriber + * @returns {boolean} The role of the subscriber + */ + public getIsSubscriber(): boolean { + return this.isSubscriber; + } + + /** + * Get the messages for the user + * @returns {Message[]} The messages for the user + */ + public getMessages(): Message[] { + return this.messages; + } + + /** + * Add a message to the user + * @param {Message} message The message to add to the user + */ + public addMessage(message: Message): void { + this.messages.push(message); + } + + /** + * Get the gifts for the user + * @returns {Gift[]} The gifts for the user + */ + public getGifts(): Gift[] { + return this.gifts; + } + + /** + * Add a gift to the user + * @param {Gift} gift The gift to add to the user + */ + public addGift(gift: Gift): void { + this.gifts.push(gift); + } +} \ No newline at end of file diff --git a/node/source/classes/tiktok/UserGift.ts b/node/source/classes/tiktok/UserGift.ts new file mode 100644 index 0000000..99e0558 --- /dev/null +++ b/node/source/classes/tiktok/UserGift.ts @@ -0,0 +1,7 @@ +export class UserGift { + private giftId: number; + private repeatCount: number; + private repeatEnd: boolean; + private messageId: string; + private createdAt: Date; +} \ No newline at end of file diff --git a/node/source/index.ts b/node/source/index.ts new file mode 100644 index 0000000..84fe963 --- /dev/null +++ b/node/source/index.ts @@ -0,0 +1,4 @@ +import { GameController } from "./classes/controller/GameController"; + +let gameController = new GameController("niknando"); +gameController.start(); \ No newline at end of file diff --git a/node/tsconfig.json b/node/tsconfig.json new file mode 100644 index 0000000..1c4c8f3 --- /dev/null +++ b/node/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "module": "commonjs", + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "target": "es6", + "noImplicitAny": true, + "moduleResolution": "node", + "sourceMap": true, + "outDir": "dist", + "baseUrl": ".", + "paths": { + "*": [ + "node_modules/*", + ] + } + }, + "include": [ + "source/**/*" + ] +} \ No newline at end of file