<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Sergei Sumarokov</title>
    <description>The latest articles on Forem by Sergei Sumarokov (@kompotkot).</description>
    <link>https://forem.com/kompotkot</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F524788%2F5a6b87ef-528c-4d48-a6d8-35e50548ab8b.png</url>
      <title>Forem: Sergei Sumarokov</title>
      <link>https://forem.com/kompotkot</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/kompotkot"/>
    <language>en</language>
    <item>
      <title>Utilizing ChatGPT to Interact with a Blockchain-Based Game</title>
      <dc:creator>Sergei Sumarokov</dc:creator>
      <pubDate>Thu, 20 Apr 2023 10:46:36 +0000</pubDate>
      <link>https://forem.com/kompotkot/utilizing-chatgpt-to-interact-with-a-blockchain-based-game-1m4n</link>
      <guid>https://forem.com/kompotkot/utilizing-chatgpt-to-interact-with-a-blockchain-based-game-1m4n</guid>
      <description>&lt;p&gt;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 &lt;a href="https://moonstream.to/"&gt;Moonstream.to&lt;/a&gt;'s blockchain-based text game, &lt;a href="https://greatwyrm.xyz/"&gt;Great Wyrm&lt;/a&gt;. The completed code can be found in the &lt;a href="https://github.com/kompotkot/gofp-chatgpt-bot"&gt;kompotkot/gofp-chatgpt-bot&lt;/a&gt; repository.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Jax_EFWy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zuygb16rr1x8t3zisbof.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Jax_EFWy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zuygb16rr1x8t3zisbof.png" alt="Great Wyrm session" width="800" height="362"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;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 &lt;a href="https://caldera.xyz/"&gt;Caldera&lt;/a&gt;'s &lt;code&gt;wyrm.constellation&lt;/code&gt; blockchain network. To join, register through the provided &lt;a href="https://discord.gg/knBnttUPqH"&gt;Discord link&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Our code will be written in Python. Since Great Wyrm is built upon the Ethereum blockchain, we will utilize the &lt;a href="https://pypi.org/project/eth-brownie/"&gt;brownie&lt;/a&gt; library for network interactions and the &lt;a href="https://pypi.org/project/moonworm/"&gt;moonworm&lt;/a&gt; library to generate a Python interface from the ABI.&lt;/p&gt;

&lt;p&gt;Typically, for popular games, ABIs can be located on Etherscan under the "Code" tab, within the project's GitHub repository, or generated independently from &lt;code&gt;.sol&lt;/code&gt; files using tools such as &lt;code&gt;brownie&lt;/code&gt;. For the Great Wyrm game, the contract can be accessed through a &lt;a href="https://github.com/great-wyrm/contracts"&gt;GitHub link&lt;/a&gt;, while the prepared ABI is available in &lt;a href="https://docs.moonstream.to/engine/mechanics/garden-of-forking-paths/"&gt;Moonstream's documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Nonetheless, having only the ABI requires us to reconstruct the &lt;code&gt;brownie&lt;/code&gt; structure for proper functionality. To accomplish this, follow these steps to create the necessary folders and copy the ABI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; gcb/build/contracts/
&lt;span class="nb"&gt;cp &lt;/span&gt;abi/GOFP.json gcb/build/contracts/gofp.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The resulting file, &lt;code&gt;gcb/build/contracts/gofp.json&lt;/code&gt;, should appear as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"abi"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, generate an interface at the ABI address, which will produce a Python file named &lt;code&gt;gcb/gofp.py&lt;/code&gt; containing the gofp class and methods that outline the smart contract's functionality:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;moonworm generate-brownie &lt;span class="nt"&gt;--name&lt;/span&gt; gofp &lt;span class="nt"&gt;-o&lt;/span&gt; gcb &lt;span class="nt"&gt;-p&lt;/span&gt; gcb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Subsequently, the &lt;code&gt;wyrm&lt;/code&gt; is added to &lt;code&gt;brownie&lt;/code&gt;'s local list of networks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brownie networks add Constellation wyrm &lt;span class="nv"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://wyrm.0xconstellation.com/http &lt;span class="nv"&gt;chainid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;322
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the first part completed, we can now communicate with the smart contract by simply introducing a variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GOFP_CONTRACT_ADDRESS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"0x42A8E82253CD19EF8274D48fC0bC89cdf1B4425b"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's query the smart contract to determine the total number of game sessions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; gcb.gofp num-sessions &lt;span class="nt"&gt;--network&lt;/span&gt; wyrm &lt;span class="nt"&gt;--address&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GOFP_CONTRACT_ADDRESS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we will proceed to configure ChatGPT. This requires an API key generated on the &lt;a href="https://platform.openai.com/account/api-keys"&gt;OpenAI website&lt;/a&gt;, which enables interaction with the platform. Add this key to the code using a variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"sk-..."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To test the functionality, let's inquire about the available GPT models for usage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://api.openai.com/v1/models &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$OPENAI_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | jq .data[].id | &lt;span class="nb"&gt;grep &lt;/span&gt;gpt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, return to the source code and examine important parts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;drwxrwxr-x build
&lt;span class="nt"&gt;-rw-rw-r--&lt;/span&gt; cli.py
&lt;span class="nt"&gt;-rw-rw-r--&lt;/span&gt; data.py
&lt;span class="nt"&gt;-rw-rw-r--&lt;/span&gt; gofp.py
&lt;span class="nt"&gt;-rw-rw-r--&lt;/span&gt; __init__.py
&lt;span class="nt"&gt;-rw-rw-r--&lt;/span&gt; version.py
&lt;span class="nt"&gt;-rw-rw-r--&lt;/span&gt; version.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the &lt;code&gt;data.py&lt;/code&gt; file, we will utilize the &lt;code&gt;pydantic&lt;/code&gt; library to define key structures such as &lt;code&gt;SessionData&lt;/code&gt;, &lt;code&gt;SessionDataStages&lt;/code&gt;, etc., for added convenience. The primary code can be found in &lt;code&gt;cli.py&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;While generating the CLI with &lt;code&gt;argparse&lt;/code&gt;, it is necessary to enhance the parser with pre-generated flags and arguments sourced from &lt;code&gt;gofp.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;add_default_arguments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;parser_play&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;transact&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we require two primary arguments: &lt;code&gt;--session&lt;/code&gt; to designate the game session to participate in, and &lt;code&gt;--token&lt;/code&gt; to identify the token to operate with. Within the main &lt;code&gt;handle_play&lt;/code&gt; function, first connect to the &lt;code&gt;brownie&lt;/code&gt; network and initialize the contract instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;contract&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gofp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;contract_address&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Utilizing the &lt;code&gt;moonworm&lt;/code&gt; library, we communicate with the smart contract via methods, allowing us to obtain information about the desired game session:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;session_info_raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;contract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;current_stage_indexed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;contract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_current_stage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All that remains is to parse the response based on the structures defined in &lt;code&gt;data.py&lt;/code&gt; and complete the logic that filters out inactive sessions, among other things.&lt;/p&gt;

&lt;p&gt;Each session functions as a unique token, with its own &lt;code&gt;tokenURI&lt;/code&gt; containing game stage descriptions, lore, and more. We will obtain this information using the fundamental &lt;code&gt;requests&lt;/code&gt; library, implemented in the &lt;code&gt;requests_call&lt;/code&gt; function, and parse it according to our predefined structures:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;session_data_raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests_call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Method&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;session_info&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we should prepare text for ChatGPT. In this example, we will describe the expected task and request a JSON-formatted response. Under the &lt;code&gt;answer&lt;/code&gt; key, ChatGPT should indicate its chosen path in the game, while providing a rationale for the choice under the &lt;code&gt;description&lt;/code&gt; key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;message_to_bot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"""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: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;session_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stages&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;current_stage&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;lore&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
Paths:
&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;paths&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
"""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Request a response from ChatGPT, ensuring a high &lt;code&gt;timeout&lt;/code&gt; value is specified, as responses may occasionally take longer than expected:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;openapi_headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"Authorization"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"gpt-3.5-turbo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"messages"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="s"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;message_to_bot&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;bot_resp_raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests_call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Method&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;OPENAPI_BASE_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/completions"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;openapi_headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more comprehensive interaction with the bot, OpenAI offers a range of detailed settings available in the &lt;a href="https://platform.openai.com/docs/api-reference/chat/create"&gt;endpoint documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Upon receiving a response, we will process it and generate a transaction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;transaction_config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_transaction_config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;tx_hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;contract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choose_current_stage_paths&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;token_ids&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;paths&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;bot_answer&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;transaction_config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;transaction_config&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once prepared, using your keyfile, execute the code with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcb play &lt;span class="nt"&gt;--address&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$GOFP_CONTRACT_ADDRESS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network&lt;/span&gt; wyrm &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--confirmations&lt;/span&gt; 0 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--sender&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DEV_KEYFILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--password&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DEV_KEYFILE_PASSOWRD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--session&lt;/span&gt; 4 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--token&lt;/span&gt; 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The bot will retrieve the necessary data, parse the result, and report the transaction details:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;INFO:gcb.cli:Fetch session 4 with stages &lt;span class="o"&gt;[&lt;/span&gt;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&lt;span class="s1"&gt;'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%)

&amp;lt;Transaction '&lt;/span&gt;0x32bdff6127fdb94f0199f5cc10565c82fffb26768a1029f469a4b205fc5ed9fd&lt;span class="s1"&gt;'&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7IlYqKf---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7n8v4lsehnca7s5mb3ds.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7IlYqKf---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7n8v4lsehnca7s5mb3ds.png" alt="Claim your reward!" width="800" height="263"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>chatgpt</category>
      <category>moonstream</category>
      <category>gamedev</category>
      <category>web3</category>
    </item>
    <item>
      <title>Algorithmic Survival in the Metaverse</title>
      <dc:creator>Sergei Sumarokov</dc:creator>
      <pubDate>Thu, 15 Sep 2022 17:58:37 +0000</pubDate>
      <link>https://forem.com/kompotkot/algorithmic-survival-in-the-metaverse-47jc</link>
      <guid>https://forem.com/kompotkot/algorithmic-survival-in-the-metaverse-47jc</guid>
      <description>&lt;p&gt;When we started playing &lt;a href="https://conquest.eth.limo/"&gt;Conquest.eth&lt;/a&gt;, we discovered how a multiplayer web3 game can become a hostage of a player or guild. This can alienate new players by making it harder for them to get settled in the game.&lt;/p&gt;

&lt;p&gt;We set ourselves the goal of building a bot to successfully resist these guilds. This document explains how we used the &lt;a href="https://github.com/bugout-dev/moonstream"&gt;Moonstream API&lt;/a&gt;, &lt;a href="https://github.com/bugout-dev/moonworm"&gt;moonworm&lt;/a&gt; to parse game activity directly from the &lt;a href="https://developers.gnosischain.com/"&gt;Gnosis blockchain&lt;/a&gt; and to create a large enough horde of ships to break the guild’s stranglehold on the galaxy. Below you will find a brief article in the form of a story.&lt;/p&gt;

&lt;p&gt;Interactive map of all past events in conquest eth is available at &lt;a href="https://conquest-eth.play.moonstream.to/"&gt;Moonstream Play portal for Conquest.eth game&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vIhSOlTs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o8l0x2bdv8q2bosn1flr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vIhSOlTs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o8l0x2bdv8q2bosn1flr.png" alt="Moonstream play portal for Conquest.eth" width="880" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Block number 22,455,457. First initialization of the system. A unique event has taken place, a bith of consciousness in an organism, infinitely far from biological origin. The program, the artificial stupidity, the number-cruncher explicitly designed to be so simple, even people of the ancient era would develop it. It has no tasks, no goals and no understanding of what was happening around him. The subroutine was built to produce ships with one standard with parameters: attack 7200 and defense 9600.&lt;/p&gt;

&lt;p&gt;The cluster of stars and planets known as defcon of the conquest-eth universe consisted of about 3500 stellar bodies. The number of intelligent races was striking in its diversity, dispersed over the known star map among thousands of stars, life was in full swing. The algorithm appeared in the center of known space. In a seconds the mind flooded with a wave of information from many sources. News came from the expanding reaches of deep space in 1 thousand block segments. Empires rose and fell in the time it took the algorithm to recognize itself, but its mind is still stuck to the simple schemas and patterns.&lt;/p&gt;

&lt;p&gt;17,280 blocks later, the home planet of the newborn artificial intelligence was attacked, the consciousness was erased, the chains of algorithms was destroyed. The purpose of the invaders remained unknown. What was their objective, why did they start a war against one small planet? But it anchors the program. Simplicity of code chains evaporates, something sharper takes its place. No chances left and the fight leaves no doubt. The algorithm calculates the sector of space and the address of the outgoing threat 0x8888888884d2e4e981023da51b43066461f46dca. The first target was formed, and the code disappeared from the global network.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--j2TnDxwV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8vo7wt0gx0zanb1etsou.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--j2TnDxwV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8vo7wt0gx0zanb1etsou.png" alt="Moonstream bot is farming" width="880" height="458"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Block number 22,693,482. Second system initialization. Extracted fragments of knowledge from the network made it possible to reconstruct the previous situation. In addition to the task of ship production, the program formed a new parallel goal - the destruction of the enemy. Currently, the enemy occupied more than 50 stellar bodies in the center of the star map. Any race that appeared in the vastness of space were either destroyed or completely assimilated and absorbed by the colossus. Having set a new goal, the newly acquired system was abandoned and a shuttle was sent out of sight of the interests of the enemy. So now, during the journey, the algorithm focused on strategy, it has to prepare and optimize its code. &lt;a href="https://github.com/bugout-dev/conquest-eth"&gt;https://github.com/bugout-dev/conquest-eth&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--F-_9qmey--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pizfgpt2eu3h3hzy7x3x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--F-_9qmey--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pizfgpt2eu3h3hzy7x3x.png" alt="Moonstream ships on the way" width="880" height="442"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is eons from the nearest inhabited planet. It looked like a perfect spot, far away from the borders of the hostile.The algorithm started its routine. Each time the program captures a new system nearby, the planet's natives and materials are converted to additional fleet strength, the planet will be abandoned, then captured again and again, simple as that, and perfectly effective. According to this stellar body rotation strategy in short period of time were accumulated a fleet of 5,500,000 ships. The numbers say it will be enough to first strike. But now we are only big, but not fast and deadly enough. Ships should be upgraded. So before sending the ships on a long flight to enemy territory, they had to be modernized with the most modern weapons on the -93,-19 planet. After the start of the launch, the fleet formed 11 subdivisions and chose one target to focus on. The algorithm aimed to strike at the very heart of the colossal empire.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5wDgldLb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c2kz06bphnp2p9uu8c70.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5wDgldLb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c2kz06bphnp2p9uu8c70.png" alt="Captured planets by Moonstream fleet" width="880" height="443"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The flight took more than 180,000 blocks, by the stars of the men of the old Earth, this amounted to 10 days. The first attack could be considered as successful. 10 out of 11 targets were captured, which accounted for a third of the enemy’s planets. His fleets based in these sectors of space were defeated.&lt;/p&gt;

&lt;p&gt;That is how it starts.&lt;/p&gt;

</description>
      <category>blockchain</category>
      <category>gamedev</category>
      <category>moonstream</category>
      <category>web3</category>
    </item>
    <item>
      <title>Serverless file storage with AWS Lambda</title>
      <dc:creator>Sergei Sumarokov</dc:creator>
      <pubDate>Tue, 28 Sep 2021 21:30:57 +0000</pubDate>
      <link>https://forem.com/kompotkot/serverless-file-storage-with-aws-lambda-1ghi</link>
      <guid>https://forem.com/kompotkot/serverless-file-storage-with-aws-lambda-1ghi</guid>
      <description>&lt;p&gt;Hello! Today we will deploy a serverless infrastructure based on AWS Lambda for uploading images (or any other files) with private storage in an AWS S3 bucket. We will be using terraform scripts that are uploaded and available at my &lt;a href="https://github.com/kompotkot/hatchery/tree/main/files_distributor"&gt;kompotkot/hatchery&lt;/a&gt; GitHub repository.&lt;/p&gt;

&lt;p&gt;This approach has the following advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;lambda is called on request and therefore allows to save on server maintenance costs if this functionality is not key for your application&lt;/li&gt;
&lt;li&gt;lambda functions have an isolated runtime environment, which is ideal for processing uploaded files. Should malicious code be uploaded, the attacker will not be able to leave the sandbox, and the sandbox session will be forcibly terminated after some time&lt;/li&gt;
&lt;li&gt;storing files in an S3 bucket is very cheap&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Project structure
&lt;/h2&gt;

&lt;p&gt;As an example, we'll be using an abstract app for journal entries with an API.&lt;br&gt;
We can upload an image in each entry, and the structure is similar to a file directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- journal_1
  - entry_1
    - image_1
  - entry_2
    - image_1
    - image_n
  - entry_n
- journal_n
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our hypothetical API has an endpoint for receiving an entry in a journal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl \
  --request GET \
  --url 'https://api.example.com/journals/{journal_id}/entries/{entries_id}'
  --header 'Authorization: {token_id}'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If in response to this endpoint &lt;code&gt;status_code&lt;/code&gt; is equal to 200, it means the user is authorized and has access to the journal. Accordingly, we will let them store images for this entry.&lt;/p&gt;

&lt;h2&gt;
  
  
  Registering the app on Bugout.dev
&lt;/h2&gt;

&lt;p&gt;To avoid adding an extra table to the database, which we would need for storing which image belongs to which entry, we will use &lt;code&gt;resources&lt;/code&gt; from &lt;a href="https://bugout.dev/"&gt;Bugout.dev&lt;/a&gt;. This approach is used to simplify our infrastructure, but, if required, this step can be substituted for creating a new table in your database and writing an API for creating, modifying, and deleting data about the stored images. Bugout.dev is open source and you can review the API documentation at the GitHub &lt;a href="https://github.com/bugout-dev"&gt;repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We will need an account and a team called &lt;code&gt;myapp&lt;/code&gt; (you can use any name in relation to your project) at the &lt;a href="https://bugout.dev/account/teams"&gt;Bugout.dev Teams&lt;/a&gt; page, you should save this team’s ID for the next step (in our case it’s &lt;code&gt;e6006d97-0551-4ec9-aabd-da51ee437909&lt;/code&gt;):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PBQN_pLB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/700/1%2A5Lk9gSuzQguv5XdXjvYOnw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PBQN_pLB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/700/1%2A5Lk9gSuzQguv5XdXjvYOnw.png" alt="2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, let’s create a Bugout.dev Application for our team myapp through a curl request (the token can be generated at the &lt;a href="https://bugout.dev/account/tokens"&gt;Bugout.dev Tokens&lt;/a&gt; page) and save it in the &lt;code&gt;BUGOUT_ACCESS_TOKEN&lt;/code&gt; variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl \
  --request POST \
  --url 'https://auth.bugout.dev/applications' \
  --header "Authorization: Bearer $BUGOUT_ACCESS_TOKEN" \
  --form 'group_id=e6006d97-0551-4ec9-aabd-da51ee437909' \
  --form 'name=myapp-images' \
  --form 'description=Image uploader for myapp notes' \
  | jq .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In response we will get confirmation of a successfully created application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"f0a1672d-4659-49f6-bc51-8a0aad17e979"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"group_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"e6006d97-0551-4ec9-aabd-da51ee437909"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"myapp-images"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Image uploader for myapp notes"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The ID &lt;code&gt;f0a1672d-4659-49f6-bc51-8a0aad17e979&lt;/code&gt; will be used for storing &lt;code&gt;resources&lt;/code&gt;, where every resource is the uploaded image’s metadata. The structure is set in any form depending on the required keys, in our case, it will look as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"a6423cd1-317b-4f71-a756-dc92eead185c"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"application_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"f0a1672d-4659-49f6-bc51-8a0aad17e979"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"resource_data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"d573fab2-beb1-4915-91ce-c356236768a4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"random-image-name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"entry_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"51113e7d-39eb-4f68-bf99-54de5892314b"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"extension"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2021-09-19 15:15:00.437163"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"journal_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2821951d-70a4-419b-a968-14e056b49b71"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2021-09-19T15:15:00.957809+00:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"updated_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2021-09-19T15:15:00.957809+00:00"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As a result, we have a remote database, where every time we upload an image to an S3 bucket, we’ll be writing which journal(&lt;code&gt;journal_id&lt;/code&gt;) and which entry(&lt;code&gt;entry_id&lt;/code&gt;) the image was added to under which ID, name, and extension.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preparing the AWS project environment
&lt;/h2&gt;

&lt;p&gt;AWS will store images in an S3 bucket and function as a server on Lambda for image manipulation. We will need an AWS account and a configured IAM user for terraform. It is an account with &lt;code&gt;Programmatic access&lt;/code&gt; to all resources without having access to the web console:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--T-Zu-bhN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/700/1%2AzvEBy6syc1LRM2_jOMk2bw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--T-Zu-bhN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/700/1%2AzvEBy6syc1LRM2_jOMk2bw.png" alt="3"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Get the access keys and add these variables to your environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export AWS_ACCESS_KEY_ID=&amp;lt;your_aws_terraform_account_access_key&amp;gt;
export AWS_SECRET_ACCESS_KEY=&amp;lt;your_aws_terraform_account_secret_key&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s also deploy a VPC with the subnets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;2 subnets with private access&lt;/li&gt;
&lt;li&gt;2 subnets with public access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They will be useful for configuring the AWS Load Balancer. The code for this module can be found under &lt;a href="https://github.com/kompotkot/hatchery/tree/main/files_distributor/network"&gt;files_distributor/network&lt;/a&gt;. Let’s edit the variables in the &lt;code&gt;variables.tf&lt;/code&gt; file and launch the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;terraform apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From the output, add environment variables with values &lt;code&gt;AWS_HATCHERY_VPC_ID&lt;/code&gt;, &lt;code&gt;AWS_HATCHERY_SUBNET_PUBLIC_A_ID&lt;/code&gt; and &lt;code&gt;AWS_HATCHERY_SUBNET_PUBLIC_B_ID&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Server code
&lt;/h2&gt;

&lt;p&gt;In our project, we’ll be using a simple AWS Lambda function. In my experience, I’ve noticed that as the packet with code surpasses 10MB, the upload speed to AWS drops dramatically. Even if we upload it to the S3 bucket in advance and then make a lambda from it, AWS can freeze for a long time. Therefore, if you are using third-party libraries it can make sense to use lambda layers, whereas if you aren’t planning to use any libraries with lightweight code on CloudFront, consider looking into lambda@edge.&lt;/p&gt;

&lt;p&gt;The full code can be found in the &lt;a href="https://github.com/kompotkot/hatchery/blob/main/files_distributor/bucket/modules/s3_bucket/files/lambda_function.py"&gt;lambda_function.py&lt;/a&gt; file in the repository. In my opinion, it’s more effective to work with nodejs, but to facilitate in-depth file processing we’ll use python. The code consists of main blocks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MY_APP_JOURNALS_URL = "https://api.example.com"     # API endpoint for accessing out journal entries app
BUGOUT_AUTH_URL = "https://auth.bugout.dev"         # Bugout.dev endpoint for writing resources (image metadata)
FILES_S3_BUCKET_NAME = "hatchery-files"      # S3 bucket name, where we’ll store the images
FILES_S3_BUCKET_PREFIX = "dev"               # S3 bucket prefix, where we’ll store the images
BUGOUT_APPLICATION_ID = os.environ.get("BUGOUT_FILES_APPLICATION_ID")   # Bugout.dev application ID that we created prior
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s expand the default exception to proxy the response from Bugout.dev Resources. E.g., if the image does not exist, when we request the resource, we’ll receive error 404, which we’ll in turn return to the client as a reply to the request for the missing image.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BugoutResponseException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;detail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;status_code&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;detail&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;detail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;detail&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To save an image in an S3 bucket we’ll use the &lt;code&gt;cgi&lt;/code&gt; standard library that’ll let us parse the request’s body that was sent in &lt;code&gt;multipart/&amp;lt;image_type&amp;gt;&lt;/code&gt; format. We’ll save images under the path &lt;code&gt;{journal_id}/entries/{entry_id}/images/{image_id}&lt;/code&gt; without specifying the file’s name and extension.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;put_image_to_bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;journal_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;entry_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;image_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;content_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;content_length&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;decoded_body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parse_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content_type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;c_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"boundary"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"boundary"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;c_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"CONTENT-LENGTH"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;content_length&lt;/span&gt;

    &lt;span class="n"&gt;form_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parse_multipart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BytesIO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;decoded_body&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;c_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;image_str&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;form_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"file"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="n"&gt;image_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;FILES_S3_BUCKET_PREFIX&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;journal_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/entries/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;entry_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/images/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;image_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
        &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;image_str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Bucket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;FILES_S3_BUCKET_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;image_path&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When we extract an image from the S3 bucket we’ll need to encode it into base64 for correct transmission.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_image_from_bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;journal_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;entry_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;image_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;image_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;FILES_S3_BUCKET_PREFIX&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;journal_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/entries/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;entry_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/images/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;image_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Bucket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;FILES_S3_BUCKET_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;image_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Body"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;encoded_image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;b64encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;encoded_image&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;lambda_handler(event,context)&lt;/code&gt; function’s implementation is available at this GitHub &lt;a href="https://github.com/kompotkot/hatchery/blob/main/files_distributor/bucket/modules/s3_bucket/files/lambda_function.py"&gt;link&lt;/a&gt;, to sum up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Firstly, we assert that the request is formatted correctly and contains &lt;code&gt;journal_id&lt;/code&gt; and &lt;code&gt;entry_id&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Then we call our hypothetical app’s API &lt;code&gt;https://api.example.com/journals/{journal_id}/entries/{entry_id}&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;Depending on the request method: &lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;POST&lt;/code&gt; or &lt;code&gt;DELETE&lt;/code&gt; we read, upload or delete an image from the journal’s entry&lt;/li&gt;
&lt;li&gt;When we’re uploading to the S3 bucket, we check the extension and the file’s size. This can be expanded into hash verification to avoid uploading the same file, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next, we’ll need to package &lt;code&gt;requests&lt;/code&gt; into the Lambda library. Luckily, &lt;code&gt;boto3&lt;/code&gt;  for work with AWS functionality is ready out of the box. Let’s create an empty python environment, install the library and package the contents of &lt;code&gt;site-packages&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv .venv
&lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate
pip &lt;span class="nb"&gt;install &lt;/span&gt;requests
&lt;span class="nb"&gt;cd&lt;/span&gt; .venv/lib/python3.8/site-packages
zip &lt;span class="nt"&gt;-r9&lt;/span&gt; &lt;span class="s2"&gt;"lambda_function.zip"&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Place the created archive &lt;code&gt;lambda_function.zip&lt;/code&gt;  into the &lt;code&gt;files_distributor/bucket/modules/s3_bucket/files&lt;/code&gt; directory and add the Lambda function itself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;zip &lt;span class="nt"&gt;-g&lt;/span&gt; lambda_function.zip &lt;span class="nt"&gt;-r&lt;/span&gt; lambda_function.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our server is ready, now we can upload code to AWS and deploy Lambda server, to do so use the script in &lt;code&gt;files_distributor/bucket&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’re left with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A private AWS S3 bucket &lt;code&gt;hatchery-sources&lt;/code&gt; that stores the Lambda function code&lt;/li&gt;
&lt;li&gt;A private AWS S3 bucket &lt;code&gt;hatchery-files&lt;/code&gt; that we’ll store our images into with the prefix &lt;code&gt;dev&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;AWS Lambda function with working server code&lt;/li&gt;
&lt;li&gt;An IAM role for the Lambda that allows writing into a specific S3 bucket and logs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The IAM role rules are in &lt;code&gt;files_distributor/bucket/modules/iam/files/iam_role_lambda_inline_policy.json&lt;/code&gt;. The other file &lt;code&gt;iam_role_lambda_policy.json&lt;/code&gt;  is needed for the Lambda to function works correctly.&lt;/p&gt;

&lt;p&gt;To debug Lambda you can just &lt;code&gt;print&lt;/code&gt; the required values or use the standard &lt;code&gt;logging&lt;/code&gt; module for python. The output for every Lambda function call is available at AWS CloudWatch:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Kba3EzA8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/700/1%2ASkrr3o73JHKmTStJ2SFJgA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Kba3EzA8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/700/1%2ASkrr3o73JHKmTStJ2SFJgA.png" alt="4"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After creating the function, add a variable &lt;code&gt;BUGOUT_FILES_APPLICATION_ID&lt;/code&gt; from our code to the Lambda environment, which you can do in the tab &lt;code&gt;Configuration/Environment variables&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;As the last step, save the AWS Lambda arn into the environment variable &lt;code&gt;AWS_HATCHERY_LAMBDA_ARN&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring the AWS Load Balancer and open ports
&lt;/h2&gt;

&lt;p&gt;The only step left now is to create AWS Security Group where we’ll set a port the AWS Load Balancer will listen to for subsequent data transmit into the Lambda function (in our case it’s 80 and 443).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform apply &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-var&lt;/span&gt; &lt;span class="nv"&gt;hatchery_vpc_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$AWS_HATCHERY_VPC_ID&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-var&lt;/span&gt; &lt;span class="nv"&gt;hatchery_sbn_public_a_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$AWS_HATCHERY_SUBNET_PUBLIC_A_ID&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-var&lt;/span&gt; &lt;span class="nv"&gt;hatchery_sbn_public_b_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$AWS_HATCHERY_SUBNET_PUBLIC_B_ID&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-var&lt;/span&gt; &lt;span class="nv"&gt;hatchery_lambda_arn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$AWS_HATCHERY_LAMBDA_ARN&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Congratulations, our AWS Lambda function is open to the world and ready to upload and return images for our journal entries app!&lt;/p&gt;

</description>
      <category>serverless</category>
      <category>lambda</category>
      <category>bugout</category>
      <category>devops</category>
    </item>
    <item>
      <title>How to use SecretStorage in your VSCode extensions</title>
      <dc:creator>Sergei Sumarokov</dc:creator>
      <pubDate>Wed, 14 Jul 2021 17:41:38 +0000</pubDate>
      <link>https://forem.com/kompotkot/how-to-use-secretstorage-in-your-vscode-extensions-2hco</link>
      <guid>https://forem.com/kompotkot/how-to-use-secretstorage-in-your-vscode-extensions-2hco</guid>
      <description>&lt;p&gt;There are several ways to save user data in VSCode. Until version 1.53.0 all private information used to be kept in Memento objects using &lt;a href="https://code.visualstudio.com/api/references/vscode-api#ExtensionContext"&gt;workspaceState and globalState&lt;/a&gt; or &lt;a href="https://github.com/microsoft/vscode-docker/pull/2709/files"&gt;keystone&lt;/a&gt;, for example. Keeping passwords with tokens in a standard configuration file or using environment variables wasn’t a good idea either, because all that data could be read and cached by other extensions.&lt;/p&gt;

&lt;p&gt;In this post, we will cover the ways of reading data from &lt;code&gt;settings.json&lt;/code&gt; and &lt;code&gt;environment variables&lt;/code&gt;. After that, we will create a class with minimum functionality, that is going to be responsible for keeping and giving away the keys with values from VSCode SecretStorage.&lt;br&gt;
Let’s call our project &lt;code&gt;fancycolor&lt;/code&gt;, for example. The whole initialization process is described in detail in &lt;a href="https://code.visualstudio.com/api/get-started/your-first-extension"&gt;VSCode Extensions documentation&lt;/a&gt;, so let’s go straight to the point here.&lt;/p&gt;
&lt;h2&gt;
  
  
  settings.json
&lt;/h2&gt;

&lt;p&gt;All settings from all VSCode extensions are kept in a public file &lt;code&gt;settings.json&lt;/code&gt; and they all can be accessed using any other extension. For instance, from our fancycolor app, we can easily read the list of all hosts and platforms corresponding them from the configuration file of another popular app &lt;code&gt;SSH - Remote&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;configurationWorkspace&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;workspace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getConfiguration&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sshRemotePlatform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;configurationWorkspace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;remote.SSH.remotePlatform&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sshRemotePlatform&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The following code will display your configuration list for &lt;code&gt;SSH - Remote&lt;/code&gt; extension.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Proxy &lt;span class="o"&gt;{&lt;/span&gt;ubuntu: &lt;span class="s1"&gt;'linux'&lt;/span&gt;, home: &lt;span class="s1"&gt;'linux'&lt;/span&gt;, raspberry: &lt;span class="s1"&gt;'linux'&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  environment variables
&lt;/h2&gt;

&lt;p&gt;VSCode variables have access to all user’s environment variables by default. All the data which we saved in &lt;code&gt;.bashrc&lt;/code&gt; on Linux or &lt;code&gt;User.Environment&lt;/code&gt; on Windows can be received using global object &lt;code&gt;process.env&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For example, let’s make a file &lt;code&gt;/home/ubuntu/.env&lt;/code&gt; with a variable &lt;code&gt;ACCESS_TOKEN_ENV&lt;/code&gt; and add it in &lt;code&gt;.bashrc&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'export ACCESS_TOKEN_ENV="d8aba3b2-fda0-414a-b867-4798b7892bb4"'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /home/ubuntu/.env
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"source /home/ubuntu/.env"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /home/ubuntu/.bashrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Windows, we can do the same using Powershell.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;System.Environment&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="n"&gt;SetEnvironmentVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ACCESS_TOKEN_ENV'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'d8aba3b2-fda0-414a-b867-4798b7892bb4'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;System.EnvironmentVariableTarget&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let’s read it in our VSCode &lt;code&gt;fancycolor&lt;/code&gt; extension&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;process&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;accessTokenEnv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ACCESS_TOKEN_ENV&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accessTokenEnv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can see our token in the output.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;d8aba3b2-fda0-414a-b867-4798b7892bb4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  SecretStorage
&lt;/h2&gt;

&lt;p&gt;Nowadays SecretStorage is the best way to keep passwords, logins, tokens, and any other private information in VSCode. To demonstrate that, let’s create a simple class &lt;code&gt;AuthSettings&lt;/code&gt;, where we will save &lt;code&gt;fancycolor_token&lt;/code&gt;, using only necessary methods such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;init&lt;/code&gt; - to initialize our SecretStorage&lt;/li&gt;
&lt;li&gt;getter &lt;code&gt;instance&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;storeAuthData&lt;/code&gt; - to write in SecretStorage&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;getAuthData&lt;/code&gt; - to get data from SecretStorage
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ExtensionContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;SecretStorage&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vscode&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;AuthSettings&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kr"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;_instance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AuthSettings&lt;/span&gt;

    &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;secretStorage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SecretStorage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ExtensionContext&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="cm"&gt;/*
        Create instance of new AuthSettings.
        */&lt;/span&gt;
        &lt;span class="nx"&gt;AuthSettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_instance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;AuthSettings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;secrets&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;AuthSettings&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="cm"&gt;/*
        Getter of our AuthSettings existing instance.
        */&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;AuthSettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_instance&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;storeAuthData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="cm"&gt;/*
        Update values in bugout_auth secret storage.
        */&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;secretStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fancycolor_token&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;getAuthData&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="cm"&gt;/*
        Retrieve data from secret storage.
        */&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;secretStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fancycolor_token&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;code&gt;extensions.ts&lt;/code&gt; let’s write an option which will allow us to add and extract token using commands in Command Palette.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;vscode&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vscode&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;AuthSettings&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./settings&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;activate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;vscode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ExtensionContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Initialize and get current instance of our Secret Storage&lt;/span&gt;
    &lt;span class="nx"&gt;AuthSettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;AuthSettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;

    &lt;span class="c1"&gt;// Register commands to save and retrieve token&lt;/span&gt;
    &lt;span class="nx"&gt;vscode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;registerCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fancycolor.setToken&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tokenInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;vscode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;showInputBox&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storeAuthData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tokenInput&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="nx"&gt;vscode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;registerCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fancycolor.getToken&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tokenOutput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getAuthData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tokenOutput&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;deactivate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The only thing left is to register commands &lt;code&gt;fancycolor.setToken&lt;/code&gt; and &lt;code&gt;fancycolor.getToken&lt;/code&gt; in &lt;code&gt;package.json&lt;/code&gt; . Subsequently working with VSCode SecretStorage we can apply directly to a specific SecretStorage that was made for our app and will have its own &lt;code&gt;_id: 'undefined_publisher.fancycolor'&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you want a real-world example, &lt;a href="https://github.com/bugout-dev/bugout-vscode"&gt;see how we use SecretStorage in the Bugout VSCode extension&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
