<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Nipuna Weerasekara on Medium]]></title>
        <description><![CDATA[Stories by Nipuna Weerasekara on Medium]]></description>
        <link>https://medium.com/@niweera?source=rss-b6269cc7ba50------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*Pl6FUa92TvTZjboMSloA8Q.jpeg</url>
            <title>Stories by Nipuna Weerasekara on Medium</title>
            <link>https://medium.com/@niweera?source=rss-b6269cc7ba50------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Thu, 25 Jun 2026 04:24:15 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@niweera/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[How I Automated Minting My Tweets as NFTs on OpenSea —Part 3]]></title>
            <link>https://medium.com/better-programming/how-i-automated-minting-my-tweets-as-nfts-on-opensea-part-3-9464d2652cc0?source=rss-b6269cc7ba50------2</link>
            <guid isPermaLink="false">https://medium.com/p/9464d2652cc0</guid>
            <category><![CDATA[dapps]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[nodejs]]></category>
            <category><![CDATA[web3]]></category>
            <category><![CDATA[blockchain]]></category>
            <dc:creator><![CDATA[Nipuna Weerasekara]]></dc:creator>
            <pubDate>Sun, 27 Feb 2022 05:32:21 GMT</pubDate>
            <atom:updated>2022-02-28T17:23:18.784Z</atom:updated>
            <content:encoded><![CDATA[<h4>Undertake the queued job, mint, and publish my tweets on the OpenSea.io platform</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*aWLy7uNrviDQIDWaMV1svQ.jpeg" /><figcaption>Photo by <a href="https://unsplash.com/@theshubhamdhage?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Shubham Dhage</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></figcaption></figure><p>This is part three of How I Automated Minting My Tweets as NFTs on OpenSea article series. In this article, I discuss how I created the OpenSear-Worker UI automation system using dAppeteer.</p><p>To read part one and part two of this article series,</p><ul><li><a href="https://betterprogramming.pub/how-i-automated-minting-my-tweets-as-nfts-on-opensea-854c50a44467">How I Automated Minting My Tweets as NFTs on OpenSea</a></li><li><a href="https://medium.com/@niweera/how-i-automated-minting-my-tweets-as-nfts-on-opensea-part-2-662fcb52c45c">How I Automated Minting My Tweets as NFTs on OpenSea — Part 2</a></li></ul><p>In my previous articles, I discussed how I created the OpenSear-API and how to start listening to Twitter events and queue jobs according to our specific scenarios.</p><p>In this article, I discuss how to undertake the queued job, mint, and publish my tweets on the <a href="https://opensea.io">OpenSea.io</a> platform. Since OpenSea does not let us create NFTs programmatically (using their <a href="https://docs.opensea.io/reference/api-overview">API</a>), I used UI automation to tackle this task of minting and publishing NFTs. As I mentioned in the <a href="https://medium.com/@niweera/how-i-automated-minting-my-tweets-as-nfts-on-opensea-854c50a44467">first article</a>, there are nine steps to be automated. So let’s get right to it.</p><ol><li><strong>Obtaining the screenshot of the tweet</strong></li></ol><p>There are so many ways to obtain a screenshot of a tweet. For this task, I use the TweetPik Twitter Screenshot API.</p><p><a href="https://tweetpik.com/twitter-screenshot-api">Twitter Screenshot API - TweetPik</a></p><p>The following gist shows how to use TweetPik API to obtain and store a screenshot of a given tweet.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/947d8f71731b09d1d0542d465ec19dff/href">https://medium.com/media/947d8f71731b09d1d0542d465ec19dff/href</a></iframe><p>For this, we need to provide four values.</p><p>i. The ID of the tweet</p><p>We need to set the ID of the tweet that we need to take the screenshot. Here we set tweetId body value. For example,</p><pre><a href="https://twitter.com/Niweera/status/1497455677131739137">https://twitter.com/Niweera/status/1497455677131739137</a></pre><pre>tweetId = `<a href="https://twitter.com/Niweera/status/1497455677131739137">1497455677131739137</a>`</pre><p>ii. The theme ID</p><p>For screenshot customization, we can set a theme by providing a TweetPik theme ID. Here we set themeId body value to TWEETPIK_THEME_ID.</p><p>iii. The timezone</p><p>We can provide a timezone for the tweet screenshot. The timezone value should be provided as in the following Wiki page.</p><p><a href="https://en.wikipedia.org/wiki/List_of_tz_database_time_zones">List of tz database time zones - Wikipedia</a></p><p>I live in Sri Lanka, so my timezone would be Asia/Colombo. Here we set timezone body value to Asia/Colombo.</p><p>iv. The TweetPik API key</p><p>To authorize the API request, we need to set the authorization header value to the TWEETPIK_API_KEY. The TweetPik API key can be obtained for a logged-in user from the <a href="https://tweetpik.com/dashboard">TweetPik dashboard</a>.</p><p>Now we can obtain the screenshot for the given tweet ID. However, we need some touch-ups for the screenshot. Let’s discuss it next.</p><p>2. <strong>Add some touch-ups to the tweet screenshot</strong></p><p>When I say, touch-ups, I meant enhancing the image etc. For that, I used Jimp, a JavaScript Image Manipulation Program.</p><p><a href="https://github.com/oliver-moran/jimp">GitHub - oliver-moran/jimp: An image processing library written entirely in JavaScript for Node, with zero external or native dependencies.</a></p><p>3.<strong> Log in to OpenSea.io and go to add assets page</strong></p><p><a href="https://opensea.io">OpenSea.io</a> is not a usual <em>web2</em> application where we can create an account using our email and password. For OpenSea.io, we need to connect our <a href="https://metamask.io/">MetaMask</a> (or any other <a href="https://support.opensea.io/hc/en-us/articles/1500007978402-What-crypto-wallets-can-I-use-with-OpenSea-">supported wallet provider</a>) wallet to the OpenSea platform. This is a very tedious task to be automated. However, to our help, the @chainsafe/dappeteer<em> </em>package comes as a knight in shining armor.</p><p><a href="https://github.com/ChainSafe/dappeteer">GitHub - ChainSafe/dappeteer: 🏌🏼‍E2E testing for dApps using Puppeteer + MetaMask</a></p><p>By using the dappeteer<em> </em>package, we can automate the login using MetaMask wallet. Under the hood, the <em>dappeteer</em> package uses the puppeteer<em> </em>package.</p><p><a href="https://github.com/puppeteer/puppeteer">GitHub - puppeteer/puppeteer: JavaScript API for Chrome and Firefox</a></p><p>Let’s first spin up a puppeteer Chrome automation instance and connect our MetaMask wallet to the OpenSea platform.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/92cc2b86c93efd1bc1be3d0587d914d7/href">https://medium.com/media/92cc2b86c93efd1bc1be3d0587d914d7/href</a></iframe><p>What we do here is first, we create a puppeteer instance, by giving a metamaskVersion<em>. </em>Currently, @chainsafe/dappeteer v2.4.1 supports MetaMask v10.8.1 extension. Be sure to install them as follows.</p><pre>$ npm i puppeteer @chainsafe/dappeteer@2.4.1</pre><p>I have tested for the above-mentioned versions, so other combinations of versions might not work, beware of that.</p><p>After creating the puppeteer instance, we set up the MetaMask by providing the METAMASK_MNEMONIC_PHRASE which is basically the recovery key you obtained when setting up a MetaMask wallet. If you still haven’t created a MetaMask wallet or do not know how to do it, check out the following video.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FAf_lQ1zUnoM%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DAf_lQ1zUnoM&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FAf_lQ1zUnoM%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/74eaf5bdb52ad1a39bc6566ad1926c48/href">https://medium.com/media/74eaf5bdb52ad1a39bc6566ad1926c48/href</a></iframe><p>The METAMASK_PASSWORD is a string that you can provide as a temporary password. Even if you omit it, it won’t be a problem since the dappeteer package will provide it for you. The weird thing about MetaMask is that it only relies on the METAMASK_MNEMONIC_PHRASE and not on the password. Password can be changed every time you connect to the MetaMask wallet.</p><p>Then I’m configuring the Polygon Mainnet in MetaMask since it is the gas-free way to mint NFTs.</p><ul><li><a href="https://szewong.medium.com/gas-free-trading-of-nfts-on-opensea-with-polygon-16ee6281e875">Gas free trading of NFTs on OpenSea with Polygon</a></li><li><a href="https://docs.polygon.technology/docs/develop/metamask/config-polygon-on-metamask/">Add Polygon Network | Polygon Technology | Documentation</a></li></ul><p>After that, I’m using the dappeteer to sign in to the OpenSea platform. Now we can go to the add asset page and start working on our NFT minting tasks.</p><p>4. <strong>Upload the screenshot</strong></p><p>As mentioned in the following list, we upload the image to OpenSea.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/8d03b25288c4ccdfb7252453e47533f8/href">https://medium.com/media/8d03b25288c4ccdfb7252453e47533f8/href</a></iframe><p>Here we need to input two parameters, page and filepath. page is the reference to the puppeteer page and filepath is the path to the screenshot.</p><p>5. <strong>Add a unique name to the NFT</strong></p><p>This is very essential since NFTs should have unique names.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b6572a8c9f965cce27a94e784c8d065b/href">https://medium.com/media/b6572a8c9f965cce27a94e784c8d065b/href</a></iframe><p>6. <strong>Add an external link</strong></p><p>Here, I add an external link to the original tweet which the screenshot is taken of.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/88f49bf962d903a219e2f24eb4f2e847/href">https://medium.com/media/88f49bf962d903a219e2f24eb4f2e847/href</a></iframe><p>7. <strong>Add a description</strong></p><p>Here I add a small description of the tweet that is taken as a screenshot.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b4a47cad489ac0caa5293d47453e4934/href">https://medium.com/media/b4a47cad489ac0caa5293d47453e4934/href</a></iframe><p>8. <strong>Add metadata related to the asset</strong></p><p>Here I do three things. First I add the tries I used up for finishing the Wordle guessing game as a level (as [tries]/6). Then I count each colored box out of 30 and add them as numeric statistics for the NFT. Both of these steps are done just for fun. Finally, I check all the level metadata and statistics metadata are inserted accurately. If all the metadata is inserted accurately I move on to minting the NFT.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/eff9b1253d77bf3897c8cdba7a134668/href">https://medium.com/media/eff9b1253d77bf3897c8cdba7a134668/href</a></iframe><p>In the fillLevels function, I input the tries parameter where it is the number of tries out of 6. In the fillStats function, I input the statistics parameter where it is an object as follows. (As per the example below).</p><p>⬛⬛⬛⬛⬛<br>⬛⬛⬛🟩🟨<br>🟩⬛🟩🟩🟩<br>🟩🟩🟩🟩🟩</p><pre>tries = &quot;4&quot;</pre><pre>statistics = {<br>               &quot;blackBlocks&quot; : &quot;9&quot;,<br>               &quot;greenBlocks&quot; : &quot;10&quot;,<br>               &quot;yellowBlocks&quot;: &quot;1&quot;<br>             }</pre><p>In the checkNumericTraits function, I input both tries and statistics so that I can check them against the previously entered values. If any of them mismatches, the program will notify that.</p><p>If all the metadata is entered correctly, the program will move on to the final step where it will mint the NFT. When I say mint the NFT, I mean the program will click <em>Create </em>button.</p><p>9. <strong>Mint the NFT</strong></p><p>The last step is to mint the NFT by clicking the <em>Create </em>button. It is as simple as that.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/07afe3578ea2f2ea8c98ff98711137c8/href">https://medium.com/media/07afe3578ea2f2ea8c98ff98711137c8/href</a></iframe><p>Here I add two parameters, page is the puppeteer page and browser is the puppeteer browser. After clicking on the create button, the browser is closed and the program is exited with returning the asset URL of the NFT we minted. Now, all we have to do is create a reply tweet to the original tweet mentioning this asset URL. We will add a reply where it says, <em>here the above tweet is for sale on OpenSea, and check it out via this asset URL</em>.</p><p><strong>Replying to the tweet with the asset URL</strong></p><p>For this, we use the <a href="https://github.com/PLhery/node-twitter-api-v2">Twitter API v2 client for NodeJS</a>.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/732ef6e37d4ec014bbd2937b29293cbb/href">https://medium.com/media/732ef6e37d4ec014bbd2937b29293cbb/href</a></iframe><p>Here we input the tweetID and the assetURL we obtained from the previous step. TwitterApi is constructed as mentioned in <a href="https://medium.com/@niweera/how-i-automated-minting-my-tweets-as-nfts-on-opensea-854c50a44467">article one</a>. After this, all of our steps are completed and the OpenSear-Worker has completed its job.</p><p><strong>But,</strong></p><p>There is a simple caveat where you cannot run a Chrome extension on a headless browser. So we cannot deploy this worker into a server (let’s say an Ubuntu machine). So we need to have a workaround for this. To tackle this, I used the following package to create a virtual display and trick the puppeteer browser into believing the server has a display.</p><p><a href="https://github.com/Rob--W/node-xvfb">GitHub - Rob--W/node-xvfb</a></p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/c234e08a87fc909d560d3b09d57a3db3/href">https://medium.com/media/c234e08a87fc909d560d3b09d57a3db3/href</a></iframe><p>By using xvfb I was able to overcome the above-mentioned issue and any code that runs between xvfb.startSync() and xvfb.stopSync() will believe the system has a display, so the problem is solved.</p><h3><strong>Closing remarks</strong></h3><p>So, this is the end of my story <strong>How I Automated Minting My Tweets as NFTs on OpenSea. </strong>There are so many wonderful people I have to thank. I would like to thank <a href="https://medium.com/u/a717bb422529">Andre Rabold</a> for his <a href="https://levelup.gitconnected.com/how-to-mint-100-000-nfts-for-free-62d83888ff6">wonderful article</a> and his <a href="https://github.com/arabold/opensea-uploader">code</a> for being my greatest inspiration. I would also like to acknowledge the inspiration I took from <a href="https://dev.to/alexluong/comprehensive-guide-to-twitter-webhook-1cd3">this article</a>.</p><p>Last but not least, I would like to acknowledge the <a href="https://stackoverflow.com/">StackOverflow</a> community, without their questions and answers (so that I can read and get inspired), this OpenSear system would still be a crazy dream that I once had.</p><p>If you want to take a look at the final product, the complete source code for the OpenSear system is hosted on GitHub (viva la open-source) below:</p><p><a href="https://github.com/Niweera/opensear">GitHub - Niweera/opensear: OpenSear is a system that helps me to mint and list my NFTs on opensea.io marketplace.</a></p><p>This is the OpenSear system in action.</p><ol><li>On <a href="https://opensea.io/assets/matic/0x2953399124f0cbb46d2cbacd8a89cf0599974963/3174924704537354725776608230933781217533108654819823768175757884922791985153">OpenSea.io marketplace</a></li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*02Lc85QOb5EuEq6qdmYYtQ.jpeg" /><figcaption><a href="https://github.com/Niweera/opensear/blob/main/assets/nft.jpg">https://github.com/Niweera/opensear/blob/main/assets/nft.jpg</a></figcaption></figure><p>2. On <a href="https://twitter.com/Niweera/status/1493156992588062721">Twitter</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/542/1*PsULI1cK6ZkurPqoDQQWUg.jpeg" /><figcaption><a href="https://github.com/Niweera/opensear/blob/main/assets/tweet.jpg">https://github.com/Niweera/opensear/blob/main/assets/tweet.jpg</a></figcaption></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9464d2652cc0" width="1" height="1" alt=""><hr><p><a href="https://medium.com/better-programming/how-i-automated-minting-my-tweets-as-nfts-on-opensea-part-3-9464d2652cc0">How I Automated Minting My Tweets as NFTs on OpenSea —Part 3</a> was originally published in <a href="https://betterprogramming.pub">Better Programming</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How I Automated Minting My Tweets as NFTs on OpenSea — Listening to Twitter Events and Queuing Jobs]]></title>
            <link>https://medium.com/better-programming/how-i-automated-minting-my-tweets-as-nfts-on-opensea-part-2-662fcb52c45c?source=rss-b6269cc7ba50------2</link>
            <guid isPermaLink="false">https://medium.com/p/662fcb52c45c</guid>
            <category><![CDATA[blockchain]]></category>
            <category><![CDATA[nft]]></category>
            <category><![CDATA[nodejs]]></category>
            <category><![CDATA[web3]]></category>
            <category><![CDATA[ethereum]]></category>
            <dc:creator><![CDATA[Nipuna Weerasekara]]></dc:creator>
            <pubDate>Sun, 27 Feb 2022 05:02:22 GMT</pubDate>
            <atom:updated>2022-12-13T09:09:47.163Z</atom:updated>
            <content:encoded><![CDATA[<h4>Customizing my OpenSea system with Webhooks— part 2</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*iqwvF7BVqgsKVLIh" /><figcaption>Photo by <a href="https://unsplash.com/@theshubhamdhage?utm_source=medium&amp;utm_medium=referral">Shubham Dhage</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>This is part two of “How I Automated Minting My Tweets as NFTs on OpenSea article” series. In this article, I discuss how I created the OpenSear-API webhook listener using NodeJS and ExpressJS.</p><p>To read part one of this article series check out the following link:</p><p><a href="https://betterprogramming.pub/how-i-automated-minting-my-tweets-as-nfts-on-opensea-854c50a44467">How I Automated Minting My Tweets as NFTs on OpenSea</a></p><p>In my first article, (I did not want this to become an article series, but my story is too big to tell in a single piece) I discussed how I created Twitter Account Activity API webhooks. In this article, I discuss how I created the OpenSear-API which is essentially a webhook listener.</p><p>First of all, for this API, we need to create two endpoints.</p><pre>GET /callback</pre><p>This endpoint is used to register and authenticate the listener endpoint with the Twitter Account Activity API and to re-authenticate when needed.</p><pre>POST /callback</pre><p>This endpoint is used by Twitter Account Activity API to send webhook events.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/11120223054b864f935c2626de38044f/href">https://medium.com/media/11120223054b864f935c2626de38044f/href</a></iframe><p>The above gist is an example JSON payload that is sent to POST /callback endpoint. You can find more from here.</p><p><a href="https://developer.twitter.com/en/docs/twitter-api/premium/account-activity-api/guides/account-activity-data-objects">Account Activity data objects</a></p><p>So, let’s start building OpenSear-API. For this, let’s use NodeJS and ExpressJS because it’s cool to use <a href="https://medium.com/swlh/javascript-in-space-7855a7ecf81b">JavaScript everywhere</a>. However, if I go on and on about how to create an API using Node and Express, this will become extremely long not to mention boring. To learn how to create an API easily you can follow <a href="https://medium.com/swlh/node-is-simple-part-1-b87a8dc390c7">my guide on Node is Simple article series</a>.</p><p>So long story short, let’s create this folder structure.</p><pre>.<br>├── controllers<br>│   └── index.js<br>├── errors<br>│   └── index.js<br>├── keys<br>│   └── index.js<br>├── middleware<br>│   └── index.js<br>├── models<br>│   └── index.js<br>├── services<br>│   └── index.js<br>├── utilities<br>│   ├── async-wrapper.d.ts<br>│   └── async-wrapper.js<br>└── index.js</pre><p>The /index.js file is the entry point to our Express API. Let’s create it as follows.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/39a1750930dd80bd229de6fa327724a6/href">https://medium.com/media/39a1750930dd80bd229de6fa327724a6/href</a></iframe><p>Now we need to create controllers and services which will be handling the API requests.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/6839d1ec60e21cd6e554bcf8dd5bdfe6/href">https://medium.com/media/6839d1ec60e21cd6e554bcf8dd5bdfe6/href</a></iframe><p>In this controllers file, we define the API endpoints GET /callback and POST /callback and the related services will be created in a service file. asyncWrapper is used to create a wrapper to catch errors and use our own error handling middleware to handle those errors.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/926ec764934b07425aca20d273c5aa3a/href">https://medium.com/media/926ec764934b07425aca20d273c5aa3a/href</a></iframe><p>To ease the development, we can use a data type declaration.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/6e34cc2cfb8fdcf301ee9ff58f23c1e5/href">https://medium.com/media/6e34cc2cfb8fdcf301ee9ff58f23c1e5/href</a></iframe><p>Now let’s create the validator middleware.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/77a52a3a1edef423e39a2600e0bca4ee/href">https://medium.com/media/77a52a3a1edef423e39a2600e0bca4ee/href</a></iframe><p>And let’s create the models file as well.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/7939552eb8211c59b123aa907b7abdda/href">https://medium.com/media/7939552eb8211c59b123aa907b7abdda/href</a></iframe><p>For model validation, we use <a href="https://joi.dev/">the most powerful schema description language and data validator for JavaScript</a> 💪. We know from <a href="https://developer.twitter.com/en/docs/twitter-api/premium/account-activity-api/guides/account-activity-data-objects">the documentation</a>, that every payload that Twitter Account Activity API sends has a for_user_id key. So we check it as the validation. (This is not necessary, but I add this just so that I can use the joi package.) Also, I will add a layer of security to these endpoints using custom-made middleware. Now let’s create the middleware functions that will handle the security and other essential Express features.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/c1d8a8f1d9fb868c8433273b5bf2183a/href">https://medium.com/media/c1d8a8f1d9fb868c8433273b5bf2183a/href</a></iframe><p><strong>Middleware; can’t live with them, can’t live without them.</strong></p><p>Let’s discuss the middleware used here.</p><ol><li>body-parser — Body parser middleware is used to parse the request body and attach it as req.body.</li></ol><p>2. morgan — Morgan (as in <strong><em>Dexter Morgan</em></strong>) is an HTTP request logger middleware for NodeJS.</p><p>3. cors — CORS is a package to use when you need to specify which resources can access this API. Which is also known as Cross-Origin Resource Sharing.</p><p><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS">Cross-Origin Resource Sharing (CORS) - HTTP | MDN</a></p><p>4. helmet — Helmet is used to secure the Express apps by setting various HTTP headers.</p><p>Now let’s move on to the most important middleware of ’em all. The TwitterValidator.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/0175c90e21de18b951304d17e1f52415/href">https://medium.com/media/0175c90e21de18b951304d17e1f52415/href</a></iframe><p>In this TwitterValidator we check if the GET /callback endpoint is hit by the real Twitter API. To help us with that, Twitter API provides us with the x-twitter-webhooks-signature header where we can check against the received data.</p><p>Security; what about it?</p><ol><li>Securing the GET /callback endpoint</li></ol><p>As mentioned in <a href="https://developer.twitter.com/en/docs/twitter-api/enterprise/account-activity-api/guides/securing-webhooks">the official guide</a>, all we have to do is get the values for crc_token query parameter and nonce query parameter and then create the following string.</p><pre>`crc_token=${token}&amp;nonce=${nonce}`</pre><p>Then we need to create the hash using the HMAC SHA-256 algorithm with the above string and TWITTER_CONSUMER_SECRET. Then we compare this hash with the value from the twitter-webhooks-signature header. (Here I do not make the hash comparison time attack safe. Theoretically, a timing attack could reveal information about the types and lengths of both hashes but not their real values.) If it matches, then we can say the GET request has been sent by Twitter. If not, someone is trying to abuse your API!</p><p>2. Securing the POST /callback endpoint</p><p>This is the trickiest part where I struggled a lot. No one ever told me (I mean <a href="https://developer.twitter.com/en/docs/twitter-api/enterprise/account-activity-api/guides/securing-webhooks">the official guide</a>) that I should use the raw buffer body to obtain the hash to check against the twitter-webhooks-signature value. I tried and tried to check this against the JSON.stringify() (stringified) body but to no avail. So later I found out that I have to use the raw body for this before bodyParser.json() middleware is being applied. So I created the verifyPostRequest custom verifier for bodyParser middleware. What it does is create the hash of the raw body and TWITTER_CONSUMER_SECRET using the HMAC SHA-256 algorithm and check the hash against the twitter-webhooks-signature value. If it matches, then we can say the POST request has been sent by Twitter. If not, someone is trying to abuse your API 😂!</p><p>Since all the other middleware is being dealt with, let’s move on to another very important middleware. Let’s build the ErrorHandling middleware.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/5750fd7eec1929223d77a07d7d774666/href">https://medium.com/media/5750fd7eec1929223d77a07d7d774666/href</a></iframe><p>In error-handling middleware, we check if the errors that are captured by our asyncWrapper are the ones we created in errors/index.js , or if not we handle it using a default error handler. The following is the errors/index.js file where we define our custom error classes.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/d49b286403e719051a6ca1922bed8f2c/href">https://medium.com/media/d49b286403e719051a6ca1922bed8f2c/href</a></iframe><p>Now that we have completed almost all the basic parts of the OpenSear-API Express app, let’s move on to creating the services that are used in the controllers we defined earlier.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/172a1d9bd34df3ca0f4c226352baef86/href">https://medium.com/media/172a1d9bd34df3ca0f4c226352baef86/href</a></iframe><p>In our services file, we handle two endpoints.</p><ol><li>GET /callback endpoint — We create services.getHandler() function so that we can create and send the CRC token back to the Twitter API. For that, we create an object,</li></ol><pre>{<br>  response_token: `sha256=${hmac}`<br>}</pre><p>where hmac is created using the TWITTER_CONSUMER_SECRET and the crc_token provided by the query string from Twitter API and creating a hash as we did before. The Twitter Account Activity API will hit our GET /callback endpoint like the following.</p><pre>/callback?crc_token=$token&amp;nonce=$nonce</pre><p>So if the Twitter API accepts our response_token as valid, then we are in business. We can listen to Twitter events via our webhook listener.</p><p>2. POST /callback endpoint — We create services.postHandler() function to check if the Twitter event we want to focus on has actually happened. In my example, I check if the Twitter event is a tweet that I created and it contains the word wordle in the tweet text body. If the Twitter event is what I am looking for, I will queue for a job here. I have omitted the job queueing functionality here because it is up to your imagination to queue the job. For this task, I’d recommend, reading through the following.</p><p><a href="https://openbase.com/categories/js/best-nodejs-job-queues-libraries">10 Best Node.js Job Queues Libraries in 2022 | Openbase</a></p><p>Now we have completed implementing the OpenSear-API. We can check this by running the following commands as mentioned in <a href="https://medium.com/@niweera/how-i-automated-minting-my-tweets-as-nfts-on-opensea-854c50a44467">my previous article</a>.</p><pre>$ node --experimental-specifier-resolution=node twitter-cli.js create-webhook</pre><pre>$ node --experimental-specifier-resolution=node twitter-cli.js create-susbscription</pre><p>First, we created the webhook, then we added the subscription for our Twitter user. Then we can create a tweet that contains the word wordle in the text. So as for the OpenSear system, we have now completed the first half. Now we can queue a job based on a tweet using OpenSear-API.</p><p>But we need to have a worker to undertake that job and mint and publish the NFT. I’ll keep it for the part 3 of the series. So let’s continue our discussion in the next article where I will discuss how I created the OpenSear-Worker which is used to handle the job.</p><p><a href="https://github.com/Niweera/opensear">GitHub - Niweera/opensear: OpenSear is a system that helps me to mint and list my NFTs on opensea.io marketplace.</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=662fcb52c45c" width="1" height="1" alt=""><hr><p><a href="https://medium.com/better-programming/how-i-automated-minting-my-tweets-as-nfts-on-opensea-part-2-662fcb52c45c">How I Automated Minting My Tweets as NFTs on OpenSea — Listening to Twitter Events and Queuing Jobs</a> was originally published in <a href="https://betterprogramming.pub">Better Programming</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How I Automated Minting My Tweets as NFTs on OpenSea]]></title>
            <link>https://medium.com/better-programming/how-i-automated-minting-my-tweets-as-nfts-on-opensea-854c50a44467?source=rss-b6269cc7ba50------2</link>
            <guid isPermaLink="false">https://medium.com/p/854c50a44467</guid>
            <category><![CDATA[nft]]></category>
            <category><![CDATA[ethereum]]></category>
            <category><![CDATA[web3]]></category>
            <category><![CDATA[nodejs]]></category>
            <category><![CDATA[blockchain]]></category>
            <dc:creator><![CDATA[Nipuna Weerasekara]]></dc:creator>
            <pubDate>Sun, 27 Feb 2022 04:33:04 GMT</pubDate>
            <atom:updated>2022-02-28T17:11:00.840Z</atom:updated>
            <content:encoded><![CDATA[<h4>Here’s how I created my own system for listing and minting NFTs</h4><figure><img alt="Ethereum" src="https://cdn-images-1.medium.com/max/1024/1*etPGVVXMGRogJyUDuFbDFg.jpeg" /><figcaption>Photo by <a href="https://unsplash.com/@theshubhamdhage?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Shubham Dhage</a> on <a href="https://unsplash.com/s/photos/ethereum?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></figcaption></figure><p>In this article, I discuss how I turned my crazy idea into a reality through some trials and tribulations of UI automation of decentralized web apps (<a href="https://ethereum.org/en/dapps/#what-are-dapps">dApps</a>).</p><p>It all started one day when I came across a <a href="https://www.youtube.com/watch?v=meTpMP0J5E8">YouTube video</a> about <a href="https://web3.foundation/"><strong>Web3</strong></a><strong> </strong>apps and minting <a href="https://en.wikipedia.org/wiki/Non-fungible_token"><strong>NFTs</strong></a>. I got curious about what are NFTs and why should I even bother about Web3 (a little late to the party, I know). I checked out the video by <a href="https://medium.com/u/be68cb994bb8">Jeff Delaney</a> (<a href="https://fireship.io">Fireship.io</a>) and got to know about NFTs and what are Web3 and dApps.</p><p>Now I have a big question; what should I mint as NFTs? Since I am not an artist, I had no clue about what to do. So here comes the thinking part.</p><p>I got to know about a site where you can mint and sell your tweets called <a href="https://v.cent.co/">Valuables</a> (what they are doing is taking the screenshot of the tweet and minting an NFT token based on it). Naturally, I minted and published <a href="https://v.cent.co/tweet/1488592456253800450">my first NFT</a> on that platform. Surprisingly, I learned that I do not need the Valuables platform to mint my tweets as NFTs, and I can mint my tweets as NFTs using the <a href="https://opensea.io/">OpenSea.io</a> platform.</p><p>OpenSea is the largest NFT marketplace which is a decentralized web application running on the Ethereum blockchain. After <a href="https://support.opensea.io/hc/en-us/articles/1500003113761-What-are-the-key-terms-to-know-before-I-get-started-">learning how to do the deed</a>, I created <a href="https://opensea.io/collection/wordle-keeps-sadness-away">my first NFT collection</a> in OpenSea and listed it on the marketplace.</p><figure><img alt="Screenshot from https://opensea.io/collection/wordle-keeps-sadness-away" src="https://cdn-images-1.medium.com/max/1024/1*FVKNsZ0wWoYWEBhexWkp_Q.jpeg" /><figcaption>From: <a href="https://opensea.io/collection/wordle-keeps-sadness-away">https://opensea.io/collection/wordle-keeps-sadness-away</a></figcaption></figure><p>So as I might have mentioned earlier, this NFT minting is a tedious task to do manually. If you have thousands of images, still you need to upload them by hand and provide the necessary metadata manually and mint the token. So I thought, hmm if I can do it, a computer can definitely do it.</p><p>Then I looked for a <a href="https://docs.opensea.io/reference/api-overview">developer API</a> for the OpenSea platform. Unfortunately, you can only view assets not mint them using the API. So I turned on to UI automation which is to follow all the manual steps of minting an NFT but by using an automated browser. The following are the steps that I followed to mint an NFT manually on OpenSea.</p><ol><li>First of all, I complete a <a href="https://www.nytimes.com/games/wordle/index.html">Wordle</a> puzzle and share the results in a tweet.</li><li>Take a screenshot of the tweet using the <a href="https://tweetpik.com/#app">TweetPik app</a>.</li><li>Add some touch-ups to the screenshot (if you know what I mean).</li><li>Go to <a href="https://opensea.io">OpenSea.io</a> marketplace and go to <a href="https://opensea.io/collection/wordle-keeps-sadness-away/assets/create">add assets page</a>.</li><li>Upload the screenshot.</li><li>Add a unique name (this is very essential).</li><li>Add an external link (this link can be anything related to the asset, but here I put the tweet URL of the screenshot).</li><li>Add a description.</li><li>Add metadata related to the asset (as metadata I add levels and stats where levels are the tries I took to complete the Wordle [try/6] and as stats, I add the number of black, green, and yellow blocks from the Wordle).</li><li>Finally, I create the NFT by clicking the <em>Create</em> button.</li></ol><p>This is the process that I’m going to automate. However, there is one last step where you can list the NFT in the marketplace by quoting a selling price.</p><p>But I am not going to automate it because that is something I should not automate. <em>No one should let a machine quote the price for you</em>. It is bad for everyone because the price for any asset is based on the perceived value of that asset.</p><p>And a machine cannot perceive anything because it is not human. For more info on how anything is valued, check out this great <a href="https://www.youtube.com/watch?v=451V-lBLfuo&amp;t=222s">video documentary</a> by <a href="https://medium.com/u/959596deae41">Cleo Abram</a>.</p><p><strong>Enough with the storytelling; show me the code!</strong></p><p>Well, finally I am moving on to the coding. First of all, we need to get a high-level understanding of what we are trying to build. Let me show a high-level architecture diagram of the system that I am building. Let’s call it the OpenSear System (ok, that name might’ve been taken by others but at that time I thought about a witty name, I didn’t think of that). Actually, the name doesn&#39;t matter, what it does matters right?</p><figure><img alt="The high-level architecture diagram of OpenSear System" src="https://cdn-images-1.medium.com/max/1000/1*JT4RaTGPtSOGvWKApyjsVw.jpeg" /><figcaption>Created using <a href="https://pixlr.com/">https://pixlr.com/</a> All logos are used with the compliance of “fair use”.</figcaption></figure><p><strong>How did I use Twitter Account Activity API to listen to my tweets in real-time?</strong></p><p>The first task that I have to automate is to listen whenever I post a Wordle tweet and create a new job and queue it in a job queue. For this, I used the <a href="https://developer.twitter.com/en/docs/twitter-api/premium/account-activity-api/overview">Twitter Account Activity API</a> which provides a webhook functionality where it hits a given API endpoint once I tweet something. All I have to do is set up a webhook API where it can be hit by Twitter Account Activity API.</p><p>First, let’s set up the Twitter Account Activity API so that it can send a payload to my webhook API.</p><ol><li>Get a Twitter Developer API key if you do not have one.</li><li>Follow <a href="https://developer.twitter.com/en/docs/twitter-api/premium/account-activity-api/guides/getting-started-with-webhooks">the official guide</a> provided by Twitter on how to set up the webhooks.</li></ol><p>The following is the article I followed to set up the webhooks.</p><p><a href="https://dev.to/alexluong/comprehensive-guide-to-twitter-webhook-1cd3">Comprehensive Guide to Twitter Webhook</a></p><p>We need to create six functions to set up Twitter webhooks. For this, I used the <a href="https://github.com/PLhery/node-twitter-api-v2">Twitter API v2 client for NodeJS</a>.</p><p>First, let’s create the base class for this task. Let’s call it TwitterCLIService because it will be called via a terminal.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b8e600837cfee268f5cf9de44e88dc39/href">https://medium.com/media/b8e600837cfee268f5cf9de44e88dc39/href</a></iframe><p>To create the TwitterCLIService base class, we need four keys. As you might know, we need the following Twitter API keys.</p><ol><li>TWITTER_CONSUMER_KEY</li><li>TWITTER_CONSUMER_SECRET</li><li>TWITTER_ACCESS_TOKEN</li><li>TWITTER_ACCESS_TOKEN_SECRET</li></ol><p>These tokens <a href="https://developer.twitter.com/en/docs/twitter-api/getting-started/getting-access-to-the-twitter-api">can be taken</a> from the <a href="https://developer.twitter.com/en/portal/dashboard">Twitter Developer Dashboard</a>. Watch the following video to learn how to create a Twitter App and obtain the above API tokens.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FV7LEihbOv3Y&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DV7LEihbOv3Y&amp;image=http%3A%2F%2Fi.ytimg.com%2Fvi%2FV7LEihbOv3Y%2Fhqdefault.jpg&amp;key=d04bfffea46d4aeda930ec88cc64b87c&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/f2c781086c8a6768d3d40adcef9a3a37/href">https://medium.com/media/f2c781086c8a6768d3d40adcef9a3a37/href</a></iframe><p>In the above gist, this.actions are the constants that we will call our functions related to the TwitterCLI we are building. First, we will create the endpoint to get existing webhooks. For a free account, <a href="https://developer.twitter.com/en/pricing/aaa-all">Twitter allows</a> one webhook and 15 subscribers (15 different Twitter accounts that can have the access to that one webhook).</p><p>For this function, we need to obtain a Twitter Bearer Token. It can be obtained from the Twitter Developer Dashboard just as other API tokens. Follow <a href="https://communalytic.com/video-tutorials/how-to-request-twitter-bearer-token/">this article</a> to learn how.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/0b2d56e438b27f98615a71c448eaa2d3/href">https://medium.com/media/0b2d56e438b27f98615a71c448eaa2d3/href</a></iframe><p>If we have an existing webhook configured, when we call TwitterCLIService.getWebhooks() function, the following output will be provided.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/adf1ca03c39a0c4062a4f9065880fc9b/href">https://medium.com/media/adf1ca03c39a0c4062a4f9065880fc9b/href</a></iframe><p>Now let’s add the create webhook function.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/6c16c0302fbe11d6a8ed69a0f2557df8/href">https://medium.com/media/6c16c0302fbe11d6a8ed69a0f2557df8/href</a></iframe><p>Before creating a webhook, you need to set up a dev environment for Account Activity API/Sandbox from <a href="https://developer.twitter.com/en/account/environments">Twitter Developer Dashboard</a>. It is pretty simple and straightforward. Now, remember the name you set for your environment (For free accounts, Twitter only allows one environment). I’ll save this name as TWITTER_WEBHOOK_ENV.</p><p>Remember, Twitter only accepts <strong>HTTPS </strong>webhook URLs with no PORTs. So when you are in developing and testing you can use <a href="https://ngrok.com/">ngrok </a>to set up a temporary HTTPS endpoint. Upon successfully creating a webhook (registering a webhook URL in the Twitter API), the following result will be provided.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/ee6c65e8a44ad2f92ab8fb3acd93cda8/href">https://medium.com/media/ee6c65e8a44ad2f92ab8fb3acd93cda8/href</a></iframe><p>Now let’s add the delete webhook endpoint.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/cef88340546169f29f943d98ef72210c/href">https://medium.com/media/cef88340546169f29f943d98ef72210c/href</a></iframe><p>In deleteWebhook() function, we are doing two things. First, we check for existing webhooks and then we delete if there are any. If the webhook deletion is successful, we don&#39;t get any results from the Twitter API, only an HTTP 204 (No Content) response.</p><p>Now let’s move on to the functions that relate to subscription. First, we created webhooks. Now the Twitter API knows which API endpoints to send their notifications to. But still, the Twitter API does not know which users to listen to. So we will create functions that will tell the Twitter API which users to listen to.</p><p>First, let’s create getSubscriptions() function.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/ac8963315ea0c0534e159ed098283ee7/href">https://medium.com/media/ac8963315ea0c0534e159ed098283ee7/href</a></iframe><p>When we call TwitterCLIService.getSubscriptions() function, if there are existing subscriptions, it will show the following results.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/69602f2e8e36f6f965f695b2747b2ac4/href">https://medium.com/media/69602f2e8e36f6f965f695b2747b2ac4/href</a></iframe><p>Now let’s add the create subscription function.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/9b4226cfff1f316434c0e5655105567d/href">https://medium.com/media/9b4226cfff1f316434c0e5655105567d/href</a></iframe><p>If the subscription creation is successful, we don’t get any results from the Twitter API, only an HTTP 204 (No Content) response. However, it is vital to note that in my example, we only subscribe to ourselves.</p><p>Now the Twitter Accounts Activity API listens to our tweets (the one who owns the Twitter Developer Account) and if there is activity, it will send a payload to the registered webhook.</p><p>Now if we need to unsubscribe from the Twitter Account Activity API, we need to implement the following function.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/6ad0705adcd30fd79b760a6f7c178a2c/href">https://medium.com/media/6ad0705adcd30fd79b760a6f7c178a2c/href</a></iframe><p>In this function, we do two things. First, we obtain the Twitter user ID. This is not the Twitter username. Twitter user ID is a unique value that every Twitter account has. You can find your own Twitter user ID from the following website.</p><p><a href="https://tweeterid.com/">TweeterID - Twitter ID and username converter</a></p><p>After obtaining the Twitter user ID, we delete the subscription for that specific user. If the subscription deletion is successful, we don’t get any results from the Twitter API, only an HTTP 204 (No Content) response.</p><p>Now we can move on to creating the CLI script. but before that take a look at the final output that we’ve been developing.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/7b33a3c9272d6768fb0687f2f4a0b841/href">https://medium.com/media/7b33a3c9272d6768fb0687f2f4a0b841/href</a></iframe><p>The above file can also view from the <a href="https://github.com/Niweera/opensear/blob/main/services/twitter-cli-service.js">source code</a> hosted on GitHub.</p><p>Now let’s create the CLI script to call the functions we created in TwitterCLIService. The following file can also be viewed from the <a href="https://github.com/Niweera/opensear/blob/main/services/twitter-cli.js">source code</a> hosted on GitHub.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/cfae9aca95e5ce791d90bf88ea923839/href">https://medium.com/media/cfae9aca95e5ce791d90bf88ea923839/href</a></iframe><p>Calling this CLI script is pretty simple. If you are unfamiliar with using ES6 syntax in NodeJS, do not fear, they are just the same as you would use in React or any other front-end JS framework.</p><p>But the only thing we need to remember is that out of the box, NodeJS does not support this and we need to pass a parameter to activate this powerful feature. For testing purposes, I used NodeJS v16.14.0 and NPM v8.3.1 in my development environment.</p><p>To call the CLI script we need the issue the following commands.</p><ol><li>GET Webhooks</li></ol><pre>$ node --experimental-specifier-resolution=node twitter-cli.js get-webhooks </pre><p>2. CREATE Webhook</p><pre>$ node --experimental-specifier-resolution=node twitter-cli.js create-webhook</pre><p>3. DELETE Webhook</p><pre>$ node --experimental-specifier-resolution=node twitter-cli.js delete-webhook</pre><p>4. GET Subscriptions</p><pre>$ node --experimental-specifier-resolution=node twitter-cli.js get-susbscriptions</pre><p>5. CREATE Subscription</p><pre>$ node --experimental-specifier-resolution=node twitter-cli.js create-susbscription</pre><p>6. DELETE Subscription</p><pre>$ node --experimental-specifier-resolution=node twitter-cli.js delete-susbscription</pre><p>Now we have completed a tiny first step into building OpenSear System. But still, we cannot test these commands since we do not have our OpenSear-API up and running.</p><p>So we have to start working on our OpenSear-API which we need to set up so that it can listen to Twitter Account Activity API webhook events. But if I keep on writing, this article will be extremely large and no one would read it (I know how hard it is to read, so I have a sympathetic ear).</p><p>I will stop for now in this article and in the next article, I’ll discuss how to create the OpenSear-API and how to start listening for Twitter Account Activity API webhook events and respond to them accordingly.</p><p>This is the GitHub repo for this article and I added this just in case you are curious to see the end product:</p><p><a href="https://github.com/Niweera/opensear">GitHub - Niweera/opensear: OpenSear is a system that helps me to mint and list my NFTs on opensea.io marketplace.</a></p><p>So until we meet again with part two of this article, happy coding, and happy minting…</p><pre><strong>Want to Connect?</strong></pre><pre>Shoot me on queries <a href="https://niweera.gq">here</a>.</pre><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=854c50a44467" width="1" height="1" alt=""><hr><p><a href="https://medium.com/better-programming/how-i-automated-minting-my-tweets-as-nfts-on-opensea-854c50a44467">How I Automated Minting My Tweets as NFTs on OpenSea</a> was originally published in <a href="https://betterprogramming.pub">Better Programming</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[GSoC 2021 with SCoRe Lab — Week 12]]></title>
            <link>https://medium.com/scorelab/gsoc-2021-with-score-lab-week-12-713f0ded9315?source=rss-b6269cc7ba50------2</link>
            <guid isPermaLink="false">https://medium.com/p/713f0ded9315</guid>
            <category><![CDATA[flask]]></category>
            <category><![CDATA[gsoc2021]]></category>
            <category><![CDATA[scorelab]]></category>
            <category><![CDATA[gsoc]]></category>
            <category><![CDATA[python]]></category>
            <dc:creator><![CDATA[Nipuna Weerasekara]]></dc:creator>
            <pubDate>Sun, 15 Aug 2021 07:51:08 GMT</pubDate>
            <atom:updated>2021-08-15T07:51:08.986Z</atom:updated>
            <content:encoded><![CDATA[<h3>GSoC 2021 with SCoRe Lab — Week 12</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*Z8xQIH2nLTmppMZA" /><figcaption>Photo by <a href="https://unsplash.com/@ikasalovic?utm_source=medium&amp;utm_medium=referral">Igor Kasalovic</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p><strong><em>tl;dr</em></strong> — <em>This is the thirteenth article of my journey into the Google Summer of Code 2021 with SCoRe Lab. Here I discuss week twelve (9nd of August to 15th of August) of my GSoC experience.</em></p><p><strong>’Tis the final week of the GSoC 2021!</strong></p><p>So, this is the final week of the Google Summer of Code program where we have to complete all of our development works and submit our final code for the final evaluations. I think it kind of saddens me to see the end of this wonderful program for this year. I know I have gained a lot of knowledge through the course of GSoC 2021. And I am proud to say that I have progressed myself in coding, software engineering best practices, and software security best practices from the experience I gained via the GSoC program.</p><p>As I have mentioned in the <a href="https://medium.com/scorelab/gsoc-2021-with-score-lab-week-11-fb70657f45a6">previous article</a>, in this week, all I had left to do was to create the wiki for the DNSTool-Middleware-API project. So I documented all the endpoints and their usage instructions in the wiki file. The following is the link for the wiki of DNSTool-Middleware-API.</p><p><a href="https://github.com/scorelab/DNSTool-Middleware-API/blob/main/api-gateway/WIKI.md">DNSTool-Middleware-API/WIKI.md at main · scorelab/DNSTool-Middleware-API</a></p><p>To create this wiki, I followed the Medium API Documentation and got some good insights from it.</p><p><a href="https://github.com/Medium/medium-api-docs">GitHub - Medium/medium-api-docs: Documentation for Medium&#39;s OAuth2 API</a></p><p>So as the final notes, I would like to extend my gratitude to all my wonderful mentors, my awesome fellow developers, and our awesome organization,</p><p><a href="https://www.scorelab.org/">Score Labs Home Page</a></p><p><strong><em>SCoRe Lab </em></strong>👏.</p><p>Last but not least, I would like to thank our wonderful benefactor <strong>Google</strong> for providing us with this wonderful opportunity and I hope <strong>Google </strong>will keep doing the good work for the foreseeable future 🙏. So until we meet again, happy coding…</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=713f0ded9315" width="1" height="1" alt=""><hr><p><a href="https://medium.com/scorelab/gsoc-2021-with-score-lab-week-12-713f0ded9315">GSoC 2021 with SCoRe Lab — Week 12</a> was originally published in <a href="https://medium.com/scorelab">SCoRe Lab</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[GSoC 2021 with SCoRe Lab — Week 11]]></title>
            <link>https://medium.com/scorelab/gsoc-2021-with-score-lab-week-11-fb70657f45a6?source=rss-b6269cc7ba50------2</link>
            <guid isPermaLink="false">https://medium.com/p/fb70657f45a6</guid>
            <category><![CDATA[flask]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[gsoc2021]]></category>
            <category><![CDATA[scorelab]]></category>
            <category><![CDATA[gsoc]]></category>
            <dc:creator><![CDATA[Nipuna Weerasekara]]></dc:creator>
            <pubDate>Mon, 09 Aug 2021 17:08:27 GMT</pubDate>
            <atom:updated>2021-08-09T17:08:27.592Z</atom:updated>
            <content:encoded><![CDATA[<h3>GSoC 2021 with SCoRe Lab — Week 11</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*ttyeGPQmAUjfc63R" /><figcaption>Photo by <a href="https://unsplash.com/@hendrimotography?utm_source=medium&amp;utm_medium=referral">Hendri Sabri</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p><strong><em>tl;dr</em></strong> — <em>This is the twelfth article of my journey into the Google Summer of Code 2021 with SCoRe Lab. Here I discuss week eleven (2nd of August to 8th of August) of my GSoC experience.</em></p><p><strong>The end is nigh!</strong></p><p>So, the end of the Google Summer of Code 2021 program is around the corner and I am so excited to complete my milestones and publish our DNSTool-Middleware-API to the world. Currently, in the API, a user can register, submit a scan, update the state of the scan and the user can download the scan results. The user can use the DNSTool-CLI to download the scan results and the DNSTool-CLI will be directly communicating with the Middleware API. For the authentication part in DNSTool-CLI, we use a custom generated service account (just like in the Google Cloud Platform), and then by using that service account, the included private key is used to sign a JWT token which provides several important (but not secret) information to the API about the scans to be downloaded. Then the user can send the JWT token to list the scan results files and by using that JWT token the user verification is done and the relevant files are returned to the user. Of course, the user does not have to do any of these tasks by themselves, that’s is why we provide DNSTool-CLI. Behind the scenes, DNSTool-CLI does all of these tasks on behalf of the user and the user only has to do is to provide the obtained service account JSON file to the CLI. For example,</p><pre>$ dnstool download --service-account /path/to/service/account.json</pre><p>See, how cool is that? 😁</p><p>Since there is only one week (9th August to 15th August) left to work on the DNSTool-Middleware-API, there are only a few things left to do 🙏. This week, I wrote test cases for the API using <a href="https://flask.palletsprojects.com/en/2.0.x/testing/">Flask Test Client</a> and it was awesome to see all the test cases are passing with flying colors.</p><p>So in the coming week, I will complete the API documentation and Wiki for the DNSTool-Middleware-API. Until we meet again, happy coding…</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=fb70657f45a6" width="1" height="1" alt=""><hr><p><a href="https://medium.com/scorelab/gsoc-2021-with-score-lab-week-11-fb70657f45a6">GSoC 2021 with SCoRe Lab — Week 11</a> was originally published in <a href="https://medium.com/scorelab">SCoRe Lab</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[GSoC 2021 with SCoRe Lab — Week 10]]></title>
            <link>https://medium.com/scorelab/gsoc-2021-with-score-lab-week-10-56b0d48abf47?source=rss-b6269cc7ba50------2</link>
            <guid isPermaLink="false">https://medium.com/p/56b0d48abf47</guid>
            <category><![CDATA[scorelab]]></category>
            <category><![CDATA[gsoc2021]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[gsoc]]></category>
            <category><![CDATA[flask]]></category>
            <dc:creator><![CDATA[Nipuna Weerasekara]]></dc:creator>
            <pubDate>Wed, 04 Aug 2021 14:08:18 GMT</pubDate>
            <atom:updated>2021-08-04T14:08:18.632Z</atom:updated>
            <content:encoded><![CDATA[<h3>GSoC 2021 with SCoRe Lab — Week 10</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*en2ZQAqnXf2R7xpK" /><figcaption>Photo by <a href="https://unsplash.com/@eddrobertson?utm_source=medium&amp;utm_medium=referral">Ed Robertson</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p><strong><em>tl;dr</em></strong> — <em>This is the eleventh article of my journey into the Google Summer of Code 2021 with SCoRe Lab. Here I discuss week ten (26th of July to 1st of August) of my GSoC experience.</em></p><p><strong>So what happened this week?</strong></p><p>In this week, my task was to create the endpoint to handle scans results file downloading. In our DNSTool system, a user can submit scans and the system will do scans for the provided resources. The results of the scans are stored in Google Cloud Storage. The data is stored in buckets and it is not accessible to the general public. So my task was to provide the user the ability to download their specific scan results without directly interacting with Google Cloud Storage. For this situation, as I mentioned in my <a href="https://medium.com/scorelab/gsoc-2021-with-score-lab-week-9-3f4c7a7474f8">previous blog post</a>, I created a custom service account file where I generated a private key for the specific user and for their specific scan and stored the relevant public key in our Firebase RealTime Database.</p><p>For this task, in our DNSTool-CLI, the user can provide their service account JSON file and download their respective scan files. To support this feature, I had to implement the following two endpoints.</p><pre>GET /list-downloads<br>GET /download/&lt;path:path&gt;</pre><p>Suppose there are 5 files in the scan results in Google Cloud Storage, now the user provides their service account JSON file and they can download all the scan results. However, there is a huge problem with this method. According to the RFCs of HTTP implementation, it can respond with one and only one file per request.</p><ul><li><a href="https://stackoverflow.com/questions/57297060/issue-returning-multiple-downloads-from-one-flask-route-using-multipartencoder?noredirect=1&amp;lq=1">Issue returning multiple downloads from one Flask route using MultipartEncoder</a></li><li><a href="https://stackoverflow.com/questions/1806228/browser-support-of-multipart-responses">Browser support of multipart responses</a></li></ul><p>The above mentioned Stackoverflow questions provide a comprehensive idea about why we need to zip all the files before sending the response. However, there is also a problem with zipping all the files into one file and sending a single file as the response, because we can’t make assumptions about the size of the scan results. If the scan results are few Kilobytes, then we can easily compress them in the memory and return the zipped file to the user. However, if the files are few Gigabytes large, then we can’t do this in the memory (for that we need a really good server 😁). So to resolve this situation, I implemented a middle route GET /list-downloads and now the CLI can see which files are required by the user and it can provide the file names in the GET /download/&lt;path:path&gt; route and easily download them. However, there is another issue I faced when implementing the GET /download/&lt;path:path&gt; route. Since this is directly involved with downloading an unknown size file from Google Cloud Storage and I had to return it to the user. Luckily, Google Cloud Storage Python documentation provides an easy method to download chunks of data from the Cloud Storage bucket.</p><p><a href="https://googleapis.dev/python/google-resumable-media/latest/resumable_media/requests.html#chunked-downloads">google-resumable-media</a></p><p>By using this method and <a href="https://flask.palletsprojects.com/en/2.0.x/patterns/streaming/">Flask’s Streaming Contents API</a>, I was able to implement the above route. Now the user can download a results file which can be small as few Kilobytes and large as few Gigabytes 😁.</p><p>So with these implementations, I submitted a PR,</p><p><a href="https://github.com/scorelab/DNSTool-Middleware-API/pull/15">Implement scan results file download API endpoint in DNSTool-Middleware-API[API-GATEWAY] by Niweera · Pull Request #15 · scorelab/DNSTool-Middleware-API</a></p><p>and it got merged into the main repository.</p><p>So in the coming two weeks, I will be working further on the DNSTool-Middleware-API[API-GATEWAY], and until we meet again, happy coding…</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=56b0d48abf47" width="1" height="1" alt=""><hr><p><a href="https://medium.com/scorelab/gsoc-2021-with-score-lab-week-10-56b0d48abf47">GSoC 2021 with SCoRe Lab — Week 10</a> was originally published in <a href="https://medium.com/scorelab">SCoRe Lab</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[GSoC 2021 with SCoRe Lab — Week 9]]></title>
            <link>https://medium.com/scorelab/gsoc-2021-with-score-lab-week-9-3f4c7a7474f8?source=rss-b6269cc7ba50------2</link>
            <guid isPermaLink="false">https://medium.com/p/3f4c7a7474f8</guid>
            <category><![CDATA[python]]></category>
            <category><![CDATA[flask]]></category>
            <category><![CDATA[gsoc]]></category>
            <category><![CDATA[gsoc2021]]></category>
            <category><![CDATA[jwt]]></category>
            <dc:creator><![CDATA[Nipuna Weerasekara]]></dc:creator>
            <pubDate>Tue, 27 Jul 2021 08:43:09 GMT</pubDate>
            <atom:updated>2021-07-27T08:43:09.001Z</atom:updated>
            <content:encoded><![CDATA[<h3>GSoC 2021 with SCoRe Lab — Week 9</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*J0rl-VVxpNSEHlLK" /><figcaption>Photo by <a href="https://unsplash.com/@kazuend?utm_source=medium&amp;utm_medium=referral">kazuend</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p><strong><em>tl;dr</em></strong> — <em>This is the tenth article of my journey into the Google Summer of Code 2021 with SCoRe Lab. Here I discuss week nine (19th of July to 25th of July) of my GSoC experience.</em></p><p><strong>Life after the evaluations,</strong></p><p>After the evaluations week, I resumed my work on the DNS-Tool-Middleware project. As I mentioned in my <a href="https://medium.com/scorelab/gsoc-2021-with-score-lab-week-8-48a4a21c71af">previous post</a>, my mentor and I discussed the authentication flow on how to grant authorization to the users who need to download resources from Google Cloud Storage. So according to our user authentication flow, first, we need to generate a unique service account for that specific user and for that specific scan. To create this service account I decided to follow the best practices used by Google itself. The following article is a good read if you need to learn about how the Google Service Account JSON files work.</p><p><a href="https://binx.io/blog/2021/03/07/how-to-create-your-own-google-service-account-key-file/">How to create your own Google service account key file</a></p><p>The following is a sample Google service account JSON file.</p><pre>{   <br>  <strong>&quot;type&quot;</strong>: &quot;service_account&quot;,   <br>  <strong>&quot;project_id&quot;</strong>: &quot;demo-project&quot;,       <br>  <strong>&quot;private_key_id&quot;</strong>: &quot;f871b60d0617be19393bb66ea142887fc9621360&quot;,<br>  <strong>&quot;private_key&quot;</strong>: &quot;-----BEGIN RSA PRIVATE KEY-----.....&quot;,<br>  <strong>&quot;client_email&quot;</strong>: &quot;look-no-keys@demo-project.iam.gserviceaccount.com&quot;,   <br>  <strong>&quot;client_id&quot;</strong>: &quot;102234449335144000000&quot;,   <br>  <strong>&quot;auth_uri&quot;</strong>: &quot;https://accounts.google.com/o/oauth2/auth&quot;,<br>  <strong>&quot;token_uri&quot;</strong>: &quot;https://oauth2.googleapis.com/token&quot;,<br>  <strong>&quot;auth_provider_x509_cert_url&quot;</strong>: &quot;https://www.googleapis.com/oauth2/v1/certs&quot;,<br>  <strong>&quot;client_x509_cert_url&quot;</strong>: &quot;https://www.googleapis.com/robot/v1/metadata/x509/look-no-keys%40demo-project.iam.gserviceaccount.com&quot; <br>}</pre><p>The keys in the above JSON object has the following meaning,</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/653/1*qWBNkM0DVfwUZXRDyzJe2g.png" /><figcaption>From <a href="https://binx.io/blog/2021/03/07/how-to-create-your-own-google-service-account-key-file/">https://binx.io/blog/2021/03/07/how-to-create-your-own-google-service-account-key-file/</a></figcaption></figure><p>So, what I did was, I created my own service account file with the same above fields 😁.</p><p>The following is a sample service account JSON file of DNS-Tool-Middleware.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/6f6652b7300d837fbcf9f8d7b6d0e229/href">https://medium.com/media/6f6652b7300d837fbcf9f8d7b6d0e229/href</a></iframe><p>The <strong>private_key_id</strong> is a special unique key to identify a specific service account JSON file and it resembles the functionality of a Google service account JSON file. The private_key_id is used to check if the certain service account file is used by the user and it is also used to check if the authentication token sent by the user is valid. The private_key_id is actually a string with random 16 bytes hashed using the SHA256 hashing algorithm. So it remains considerably unique 😂. I used the following wonderful Python library to generate this random bytes string.</p><p><a href="https://pycryptodome.readthedocs.io/">Welcome to PyCryptodome&#39;s documentation - PyCryptodome 3.9.9 documentation</a></p><p>The <strong>private_key </strong>is a 2048 bit RSA private key in PEM encoded format. It is generated using the above mentioned Python library. The <strong>client_email </strong>is the email of the user who obtained the service key, the <strong>client_id </strong>is the Firebase UID of the user who obtained the service key, the <strong>scan_id </strong>is the ID of the scan that the user wants to download resources from Google Cloud Storage, and the <strong>scans </strong>is the scans related to the specific scan_id.</p><p>By using this service account file, the user requests can be authenticated and checked against the server for validity. Special thanks to the following article for opening my mind 🙏.</p><p><a href="https://jryancanty.medium.com/stop-downloading-google-cloud-service-account-keys-1811d44a97d9">Stop downloading Google Cloud service account keys!</a></p><p>With these implementations, I submitted a PR,</p><p><a href="https://github.com/scorelab/DNSTool-Middleware-API/pull/13">Add endpoint to obtain service account for download authorizations by Niweera · Pull Request #13 · scorelab/DNSTool-Middleware-API</a></p><p>and it got merged into the main repository.</p><p>So in the coming weeks, I will be working further on the authentication workflow, and until we meet again, happy coding…</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3f4c7a7474f8" width="1" height="1" alt=""><hr><p><a href="https://medium.com/scorelab/gsoc-2021-with-score-lab-week-9-3f4c7a7474f8">GSoC 2021 with SCoRe Lab — Week 9</a> was originally published in <a href="https://medium.com/scorelab">SCoRe Lab</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[GSoC 2021 with SCoRe Lab — Week 8]]></title>
            <link>https://medium.com/scorelab/gsoc-2021-with-score-lab-week-8-48a4a21c71af?source=rss-b6269cc7ba50------2</link>
            <guid isPermaLink="false">https://medium.com/p/48a4a21c71af</guid>
            <category><![CDATA[gsoc2021]]></category>
            <category><![CDATA[gsoc]]></category>
            <category><![CDATA[authentication]]></category>
            <category><![CDATA[gcp]]></category>
            <category><![CDATA[python]]></category>
            <dc:creator><![CDATA[Nipuna Weerasekara]]></dc:creator>
            <pubDate>Thu, 22 Jul 2021 19:43:20 GMT</pubDate>
            <atom:updated>2021-07-22T19:43:20.839Z</atom:updated>
            <content:encoded><![CDATA[<h3>GSoC 2021 with SCoRe Lab — Week 8</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*xeZDueiXfI9S_LWQ" /><figcaption>Photo by <a href="https://unsplash.com/@tegan?utm_source=medium&amp;utm_medium=referral">Tegan Mierle</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p><strong><em>tl;dr</em></strong> — <em>This is the ninth article of my journey into the Google Summer of Code 2021 with SCoRe Lab. Here I discuss week eight (12th of July to 18th of July) of my GSoC experience.</em></p><p><strong>Evaluation week!!!</strong></p><p>After weeks of designing developing and learning about developing, finally, the evaluation week arrived. As I discussed in my <a href="https://medium.com/scorelab/gsoc-2021-with-score-lab-week-7-fad037bec8de">previous blog</a>, it was a little bit of a tight schedule that I had to finish up my first evaluation milestones before the evaluation week came. So finally I was able to finish up my targets before the deadline.</p><p>So to recap all of the things I developed before the evaluations, I had to develop API endpoints to support user registration, obtain domain zones list, obtain GCP zones list, check whether the provided email is a valid organization email, creating scans, and updating scans. In addition to these tasks, I created the documentation of the API endpoints using OpenAPI v3 specification Swagger UI. Since some of the API endpoints provide static outputs which can be easily cached, I integrated the Flask Caching to the Flask App in DNSTool-Middleware-API[API-GATEWAY]. In addition, to these tasks, some of the features provided by the API are strictly for human interaction only. Such as new user registration, we need to check if the requester is actually a human. For this situation, I integrated the Google reCAPTCHA v3 to check whether the request in fact came from a human user, not from a Bot 😁.</p><p>After the evaluation week, the coding starts again and <em>we are in the endgame now. </em>In the next <em>phase,</em> I have to develop the authentication mechanism for the user to download the required resources from Google Cloud Storage. My mentor and I had a discussion about the most suitable and (<em>SECURE!</em>) way to do the authentication for the user. At first, we were discussing creating separate user accounts for the users but there was a slight hiccup,</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/898/1*TY4q4AxnBnbz0hO2E6cDug.png" /><figcaption>From <a href="https://cloud.google.com/iam/docs/creating-managing-service-accounts">https://cloud.google.com/iam/docs/creating-managing-service-accounts</a></figcaption></figure><p>so much for that plan 😥. So we brainstormed our ideas on a suitable alternative method and we came to a conclusion on a superb idea. The following is the architecture of the user authentication flow.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*KNQS5HiQgX-KkZD_nrYtzw.png" /><figcaption>User authentication flow to download resources from GCP Cloud Storage</figcaption></figure><p>So we decided to <em>go with the flow </em>😎.</p><p>So in the coming weeks, I will be implementing this authentication workflow in the DNSTool-Middleware-API system. So until we meet again, happy coding. (Oh, one thing I forgot to mention, I passed the evaluations. <em>YAY! </em>😁)</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=48a4a21c71af" width="1" height="1" alt=""><hr><p><a href="https://medium.com/scorelab/gsoc-2021-with-score-lab-week-8-48a4a21c71af">GSoC 2021 with SCoRe Lab — Week 8</a> was originally published in <a href="https://medium.com/scorelab">SCoRe Lab</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[GSoC 2021 with SCoRe Lab — Week 7]]></title>
            <link>https://medium.com/scorelab/gsoc-2021-with-score-lab-week-7-fad037bec8de?source=rss-b6269cc7ba50------2</link>
            <guid isPermaLink="false">https://medium.com/p/fad037bec8de</guid>
            <category><![CDATA[recaptcha]]></category>
            <category><![CDATA[scorelab]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[gsoc]]></category>
            <category><![CDATA[gsoc2021]]></category>
            <dc:creator><![CDATA[Nipuna Weerasekara]]></dc:creator>
            <pubDate>Fri, 16 Jul 2021 10:49:29 GMT</pubDate>
            <atom:updated>2021-07-16T10:49:29.944Z</atom:updated>
            <content:encoded><![CDATA[<h3>GSoC 2021 with SCoRe Lab — Week 7</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*A_Rd3a42X0hs09pi" /><figcaption>Photo by <a href="https://unsplash.com/@athulca?utm_source=medium&amp;utm_medium=referral">Athul Cyriac Ajay</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p><strong><em>tl;dr</em></strong> — <em>This is the eighth article of my journey into the Google Summer of Code 2021 with SCoRe Lab. Here I discuss week seven (5th of July to 11th of July) of my GSoC experience.</em></p><p><strong>The first evaluation is nigh!</strong></p><p>As the first evaluation is almost near, I was on a tight schedule since I needed to complete my first evaluation milestones. As for the first evaluation milestones, I had to complete all the supporting endpoints which are needed for DNSTool-Middleware-API[API-GATEWAY] to function properly. The supporting endpoints are as follows.</p><pre>GET /zones/&lt;query&gt;<br>POST /register<br>POST /check-email<br>GET /gcp-zones/&lt;query&gt;<br>GET /scans<br>POST /scans<br>PATCH /scans/&lt;id&gt;<br>DELETE /scans/&lt;id&gt;</pre><p>After developing all these endpoints, now in DNSTool-Middleware-API[API-Gateway] a user can register in our system, create a new scan, list all the existing scans, update the scan status of a specific scan and they can also delete a specific scan from our database if they want.</p><p>As we discussed in our online meeting, one of the tasks I was assigned was protecting the POST /register endpoint. The big reason behind protecting the POST /register endpoint is that it needed human interaction. If someone knows the endpoint, then they can utilize a BOT 😁 to create fake accounts and this will overutilize the Google Authentication and this will be an issue for real human users. So my task was to allow only human users to create accounts in our system.</p><p><strong>Google reCAPTCHA v3 comes to the rescue 🙏</strong></p><p><a href="https://developers.google.com/recaptcha">reCAPTCHA | Google Developers</a></p><p>Google reCAPTCHA is a free service that protects our resources from spam and abuse. It uses advanced risk analysis techniques to differentiate between humans and bots. You must have seen this reCAPTCHA prompt from time to time when visiting a website.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/616/1*GmtGvbap0r7toziy-rV9rw.gif" /><figcaption>From <a href="https://developers.google.com/recaptcha">https://developers.google.com/recaptcha</a></figcaption></figure><p>I know sometimes it is a real pain, but it is really important that we need to safeguard our resources from spam and abuse. Sometimes you might fail the reCAPTCHA check a few times and feel like this,</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/960/1*PJMEPi9WqGdN4I7EKeTdRw.jpeg" /><figcaption>From <a href="https://www.reddit.com/r/memes/comments/fexhho/maybe_i_am/">https://www.reddit.com/r/memes/comments/fexhho/maybe_i_am/</a></figcaption></figure><p>So anyway, the Google reCAPTCHA v3 removes all these hassles and it silently checks your interaction with the browser and checks whether if you are a real human user or a bot. You do not need to click on anything like those annoying fire extinguisher images or mind-bending images of traffic lights anymore 😁. The documentation of Google reCAPTCHA v3 is pretty cool and straightforward.</p><p><a href="https://developers.google.com/recaptcha/docs/v3">reCAPTCHA v3 | Google for Developers</a></p><p>So after adding the Google reCAPTCHA v3 to our system, I submitted a PR,</p><p><a href="https://github.com/scorelab/DNSTool-Middleware-API/pull/11">Add Google reCAPTCHA v3 verification to `/register` endpoint by Niweera · Pull Request #11 · scorelab/DNSTool-Middleware-API</a></p><p>and it got merged into the main repository.</p><p>So with this concluded my tasks before the evaluation one 😎. There is more to do with our DNSTool-Middleware-API system and I will be working on them in the coming weeks. Until we meet again, happy coding…</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=fad037bec8de" width="1" height="1" alt=""><hr><p><a href="https://medium.com/scorelab/gsoc-2021-with-score-lab-week-7-fad037bec8de">GSoC 2021 with SCoRe Lab — Week 7</a> was originally published in <a href="https://medium.com/scorelab">SCoRe Lab</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[GSoC 2021 with SCoRe Lab — Week 6]]></title>
            <link>https://medium.com/scorelab/gsoc-2021-with-score-lab-week-6-410e34dbcb5c?source=rss-b6269cc7ba50------2</link>
            <guid isPermaLink="false">https://medium.com/p/410e34dbcb5c</guid>
            <category><![CDATA[flask]]></category>
            <category><![CDATA[gsoc]]></category>
            <category><![CDATA[scorelab]]></category>
            <category><![CDATA[gsoc2021]]></category>
            <category><![CDATA[python]]></category>
            <dc:creator><![CDATA[Nipuna Weerasekara]]></dc:creator>
            <pubDate>Thu, 08 Jul 2021 10:18:21 GMT</pubDate>
            <atom:updated>2021-07-08T10:18:21.640Z</atom:updated>
            <content:encoded><![CDATA[<h3>GSoC 2021 with SCoRe Lab — Week 6</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*e_rOYhuyWIlBXZBo" /><figcaption>Photo by <a href="https://unsplash.com/@itfeelslikefilm?utm_source=medium&amp;utm_medium=referral">🇸🇮 Janko Ferlič</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p><strong><em>tl;dr</em></strong> — <em>This is the seventh article of my journey into the Google Summer of Code 2021 with SCoRe Lab. Here I discuss week six (28th of June to 4th of July) of my GSoC experience.</em></p><p><strong>So what happened in week six?</strong></p><p>As my project mentors and I discussed in our online meeting, I was assigned to develop the endpoints related to making updates to the scan records stored in the database. The development of these endpoints was pretty much straightforward and I utilized the power of the all-mighty, Flask RESTful library.</p><p><a href="https://flask-restful.readthedocs.io/">Flask-RESTful - Flask-RESTful 0.3.8 documentation</a></p><p>In the Flask RESTful library, you can easily define a single controller for a route and define several HTTP methods. For example,</p><pre>PATCH  /scans/&lt;id&gt;<br>DELETE /scans/&lt;id&gt;</pre><p>these above two HTTP methods utilize the same endpoint /scans/&lt;id&gt; and if the user hits this endpoint via a PATCH method, we need to respond to it as a PATCH request and if the user hits this endpoint via a DELETE method, we need to respond to it as a DELETE request. In hindsight, we need a mechanism to capture the HTTP request method and respond to it accordingly. In plain Flask, we’ll do this as follows,</p><pre><strong>from</strong> flask <strong>import</strong> request<br><br>@app.route<strong>(</strong>&#39;/scans/&lt;id&gt;&#39;<strong>,</strong> methods=<strong>[</strong>&#39;PATCH&#39;<strong>,</strong> &#39;DELETE&#39;<strong>])</strong><br><strong>def</strong> scan_controller<strong>(id:str):</strong><br>    <strong>if</strong> request.method == &#39;PATCH&#39;<strong>:</strong><br>        <strong>return</strong> update_scan_record<strong>(id)</strong><br>    <strong>else:</strong><br>        <strong>return</strong> delete_scan_records<strong>(id)</strong></pre><p>(from <a href="https://flask.palletsprojects.com/en/1.1.x/quickstart/#http-methods">https://flask.palletsprojects.com/en/1.1.x/quickstart/#http-methods</a>)</p><p>But we can utilize the Flask RESTful in the following way (in a more elegant way indeed 😁).</p><pre><strong>from</strong> <strong>flask_restful</strong> <strong>import</strong> Resource</pre><pre><strong>class</strong> <strong>ScanController</strong>(Resource):<br>    method_decorators: Dict[str, List[Callable]] = dict(patch=[validator, authenticate])    <br>    model: str = &quot;UpdateScan&quot;</pre><pre>    <strong>def</strong> patch(self,id:str):<br>        <strong>return</strong> update_scan_records<strong>()</strong><br><br>    <strong>def</strong> delete(self,id:str):<br>        <strong>return</strong> delete_scan_record<strong>()</strong></pre><p>(from <a href="https://flask-restful.readthedocs.io/en/latest/quickstart.html#full-example">https://flask-restful.readthedocs.io/en/latest/quickstart.html#full-example</a>)</p><p>So by using this example, I was able to create endpoint handlers for PATCH and DELETE methods. Of course, I utilized other libraries such as <a href="https://marshmallow.readthedocs.io/">Marshmallow</a> and <a href="https://firebase.google.com/docs/reference/admin/python">Firebase Admin</a> to fulfill my tasks. After completing these tasks, I updated the <a href="https://swagger.io/tools/swagger-ui/">Swagger-UI</a> to reflect the newly added endpoints, because, documentation is your biggest ally 💪.</p><p>So after all of these, I submitted a PR,</p><p><a href="https://github.com/scorelab/DNSTool-Middleware-API/pull/9">Add Suspend and Delete Scans Endpoint DNSTool-Middleware-API[API-Gateway] by Niweera · Pull Request #9 · scorelab/DNSTool-Middleware-API</a></p><p>and it got merged into the main repository.</p><p>So in the coming week, I will work on enhancing the features of our DNSTool-Middleware-API[API-Gateway] component 🤞. Until we meet again, happy coding…</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=410e34dbcb5c" width="1" height="1" alt=""><hr><p><a href="https://medium.com/scorelab/gsoc-2021-with-score-lab-week-6-410e34dbcb5c">GSoC 2021 with SCoRe Lab — Week 6</a> was originally published in <a href="https://medium.com/scorelab">SCoRe Lab</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>