In this article, we will guide you through the process of developing a prototype bot using OpenAI's ChatGPT 3.5 API, designed to participate in Moonstream.to's blockchain-based text game, Great Wyrm. The completed code can be found in the kompotkot/gofp-chatgpt-bot repository.
Great Wyrm serves as a platform for hosting game sessions in a "game master - players" format. As the game undergoes active testing, its creators independently facilitate game sessions. The platform operates on Caldera's wyrm.constellation
blockchain network. To join, register through the provided Discord link.
Our code will be written in Python. Since Great Wyrm is built upon the Ethereum blockchain, we will utilize the brownie library for network interactions and the moonworm library to generate a Python interface from the ABI.
Typically, for popular games, ABIs can be located on Etherscan under the "Code" tab, within the project's GitHub repository, or generated independently from .sol
files using tools such as brownie
. For the Great Wyrm game, the contract can be accessed through a GitHub link, while the prepared ABI is available in Moonstream's documentation.
Nonetheless, having only the ABI requires us to reconstruct the brownie
structure for proper functionality. To accomplish this, follow these steps to create the necessary folders and copy the ABI:
mkdir -p gcb/build/contracts/
cp abi/GOFP.json gcb/build/contracts/gofp.json
The resulting file, gcb/build/contracts/gofp.json
, should appear as follows:
{
"abi": [
...
]
}
First, generate an interface at the ABI address, which will produce a Python file named gcb/gofp.py
containing the gofp class and methods that outline the smart contract's functionality:
moonworm generate-brownie --name gofp -o gcb -p gcb
Subsequently, the wyrm
is added to brownie
's local list of networks:
brownie networks add Constellation wyrm host=https://wyrm.0xconstellation.com/http chainid=322
With the first part completed, we can now communicate with the smart contract by simply introducing a variable:
export GOFP_CONTRACT_ADDRESS="0x42A8E82253CD19EF8274D48fC0bC89cdf1B4425b"
Let's query the smart contract to determine the total number of game sessions:
python -m gcb.gofp num-sessions --network wyrm --address "$GOFP_CONTRACT_ADDRESS"
Next, we will proceed to configure ChatGPT. This requires an API key generated on the OpenAI website, which enables interaction with the platform. Add this key to the code using a variable:
export OPENAI_API_KEY="sk-..."
To test the functionality, let's inquire about the available GPT models for usage:
curl https://api.openai.com/v1/models -H "Authorization: Bearer $OPENAI_API_KEY" | jq .data[].id | grep gpt
Finally, return to the source code and examine important parts:
drwxrwxr-x build
-rw-rw-r-- cli.py
-rw-rw-r-- data.py
-rw-rw-r-- gofp.py
-rw-rw-r-- __init__.py
-rw-rw-r-- version.py
-rw-rw-r-- version.txt
In the data.py
file, we will utilize the pydantic
library to define key structures such as SessionData
, SessionDataStages
, etc., for added convenience. The primary code can be found in cli.py
.
While generating the CLI with argparse
, it is necessary to enhance the parser with pre-generated flags and arguments sourced from gofp.py
:
add_default_arguments(parser=parser_play, transact=True)
Next, we require two primary arguments: --session
to designate the game session to participate in, and --token
to identify the token to operate with. Within the main handle_play
function, first connect to the brownie
network and initialize the contract instance:
network.connect(args.network)
contract = gofp(contract_address=args.address)
Utilizing the moonworm
library, we communicate with the smart contract via methods, allowing us to obtain information about the desired game session:
session_info_raw = contract.get_session(args.session)
...
current_stage_indexed = contract.get_current_stage(args.session)
All that remains is to parse the response based on the structures defined in data.py
and complete the logic that filters out inactive sessions, among other things.
Each session functions as a unique token, with its own tokenURI
containing game stage descriptions, lore, and more. We will obtain this information using the fundamental requests
library, implemented in the requests_call
function, and parse it according to our predefined structures:
session_data_raw = requests_call(method=data.Method.GET, url=session_info.uri)
Now, we should prepare text for ChatGPT. In this example, we will describe the expected task and request a JSON-formatted response. Under the answer
key, ChatGPT should indicate its chosen path in the game, while providing a rationale for the choice under the description
key:
message_to_bot = f"""Let's play. I will provide you with a short lore containing
different paths to choose from. Please respond in JSON format. You should select
one correct path and place it under the key 'answer' and provide an explanation
for your choice under the key 'description'.
The lore: {session_data.stages[current_stage].lore}
Paths:
{paths}
"""
Request a response from ChatGPT, ensuring a high timeout
value is specified, as responses may occasionally take longer than expected:
openapi_headers = {
"Authorization": f"Bearer {OPENAI_API_KEY}",
}
payload = {
"model": "gpt-3.5-turbo",
"messages": [{"role": "user", "content": message_to_bot}],
}
bot_resp_raw = requests_call(
method=data.Method.POST,
url=f"{OPENAPI_BASE_URL}/completions",
headers=openapi_headers,
json=payload,
timeout=60,
)
For more comprehensive interaction with the bot, OpenAI offers a range of detailed settings available in the endpoint documentation.
Upon receiving a response, we will process it and generate a transaction:
transaction_config = get_transaction_config(args)
tx_hash = contract.choose_current_stage_paths(
session_id=args.session,
token_ids=[args.token],
paths=[bot_answer + 1],
transaction_config=transaction_config
)
Once prepared, using your keyfile, execute the code with the following command:
gcb play --address "$GOFP_CONTRACT_ADDRESS" \
--network wyrm \
--confirmations 0 \
--sender "$DEV_KEYFILE" \
--password "$DEV_KEYFILE_PASSOWRD" \
--session 4 \
--token 2
The bot will retrieve the necessary data, parse the result, and report the transaction details:
INFO:gcb.cli:Fetch session 4 with stages [4, 1] and uri https://s3.amazonaws.com/static.greatwyrm.xyz/act1/reda_games/khina_beast_contest.json
INFO:gcb.cli:Current stage of session 4 is 1
INFO:gcb.cli:Fetch session data with title Khina's Beast Contest and active stage title Pit bat
INFO:gcb.cli:Asking ChatGPT to choose path
INFO:gcb.cli:Bot answer is: 1 and description: I would back the Copper hound because it has a useful skill that people could benefit from by sniffing out copper deposits. Additionally, they seem to be domesticated dogs so they may be more manageable than some of the other creatures. The downside is that they are not the most pleasant smelling animals and can be quite loud.
Transaction sent: 0x32bdff6127fdb94f0199f5cc10565c82fffb26768a1029f469a4b205fc5ed9fd
Gas price: 0.0 gwei Gas limit: 156629 Nonce: 15
gofp.chooseCurrentStagePaths confirmed Block: 470 Gas used: 118554 (75.69%)
<Transaction '0x32bdff6127fdb94f0199f5cc10565c82fffb26768a1029f469a4b205fc5ed9fd'>
In conclusion, we have successfully established an integration between ChatGPT and Web3, laying the groundwork for a chatbot capable of engaging with a blockchain-based game. This combination offers numerous possibilities for extending functionality and tailoring the solution to various tasks and objectives.
Top comments (0)