Omar Moustafa

Connect MetaMask Wallet with Flask and Discord.py

Created October 1, 2022

Introduction

Today we will build a Discord.py and use Flask to allow to integrate the Ethereum wallet addresses with the wallet, you use the Flask frontend to connect the wallet while it's logged in the Flask system and when you want to check if synced, the Discord.py bot sends a JSON POST request to read.

SO LET'S GOOOOO 🔥🔥🔥

Setup the Flask Page

Start by creating a new directory, and in that directory, create a folder for the Flask page:

mkdir metamask-flask-discord
cd metamask-flask-discord
mkdir flask
cd flask

Now, create an app.py file and a log.txt and paste this into the app.py:

from flask import Flask, render_template, Response, request
app = Flask(__name__)

@app.route('/<discord_id>')
def index(discord_id):
    return render_template('index.html', discordId = discord_id)

@app.route('/sync', methods=['POST'])
def sync():
    discord_id = request.json['discordId']
    wallet_address = request.json['walletAddress']
    with open('log.txt', 'r+') as f:
        file = f.read()
        if discord_id in file:
            if wallet_address in file:
                Pass
            else:
                f.write(' ' + wallet_address)
        else:
            f.write('\n' + discord_id + ' ' + wallet_address)
    f.close()

@app.route('/read', methods=['POST'])
def read():
    discord_id = request.json['discordId']
    with open('log.txt', 'r') as f:
        file = f.read()
        if discord_id in file:
            wallet_addresses = file.split(discord_id)[1].split(' ')
            wallet_addresses.pop(0)
            return Response(str(wallet_addresses), mimetype='application/json')
        else:
            return Response('No wallet address found', mimetype='application/json')

This is our main flask file, what we are doing here, we are setting 3 routes:

And for sync and read we will receive a JSON POST request, POST is more safer, that's why I personally prefer it.

Also, in both sync and read we open up the log.txt either to read wallet addresses for a specific user, or write new wallet addresses for a new user.

Now, let's create the landing page where you can connect your MetaMask wallet:

mkdir templates
touch index.html

Now in your index.html, paste this:

<!DOCTYPE html>
<html>
    <head>
        <title>MetaMask Connection</title>
    </head>
    <body>
        <center>
        <h1>Connect Your MetaMask Wallet Here To Connect With Discord!</h1>
        <button id="connectWallet" onclick="">Connect Wallet</button>
        <button id="syncDiscord" onclick="">Sync Discord</button>

        <p id="walletAddress"></p>

        <script type="text/javascript">
            var discordId = "{{ discordId }}";
            window.walletAddress = null
            const syncDiscord = document.getElementById('syncDiscord');
            const connectWallet = document.getElementById('connectWallet')
            const walletAddress = document.getElementById('walletAddress')


            function checkInstalled() {
                if (typeof window.ethereum == 'undefined') {
                    walletAddress.innerText = 'Please install MetaMask to continue.'
                    return false
                }
                connectWallet.addEventListener('click', connectWalletwithMetaMask)
            }

            async function connectWalletwithMetaMask() {
                const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' })
                .catch((e) => {
                    console.error(e.message)
                    return
                })
                if (!accounts) { return }

                window.walletAddress = accounts[0]
                walletAddress.innerText = window.walletAddress
                

                connectWallet.innerText = 'Sign Out'
                connectWallet.removeEventListener('click', connectWalletwithMetaMask)
                setTimeout(() => {
                    connectWallet.addEventListener('click', signOutOfMetaMask)
                    syncDiscord.addEventListener('click', syncDiscordWithWallet)
                }, 200 )
            }

            function signOutOfMetaMask() {
                window.walletAddress = null
                walletAddress.innerText = ''
                connectWallet.innerText = 'Connect Wallet'
                
                connectWallet.removeEventListener('click', signOutOfMetaMask)
                syncDiscord.removeEventListener('click', syncDiscordWithWallet)

                setTimeout(() => {
                    connectWallet.addEventListener('click', connectWalletwithMetaMask)
                }, 200 )
            }

            window.addEventListener('DOMContentLoaded', () => {
                checkInstalled()
            })

            async function syncDiscordWithWallet() {
                alert("If the wallet is already in the system, nothing would happen, if not, it would add it to the bot system.")
                const response = await fetch('/sync', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify({
                        walletAddress: window.walletAddress,
                        discordId: discordId
                    })
                })
            }

        </script>
        </center>   
    </body>
</html>

So here we make few buttons, and make a few event listeners which run certain functions, and for example, when the wallet is connected, we replace the event listener to listen to the sign out function, why would the user sign in again if he is already signed in, hahah!

And for syncing here, we send a JSON POST request with walletAddress and discordId to /sync to save it!

Working on our Discord.py bot

Start by creating a directory and creating a .env & bot.py:

mkdir discordbot
cd discordbot
touch .env
touch bot.py

Now, open up your .env file where you save your token just like this:

TOKEN=<your-discord-bot-token-here>

Let's work on the actual bot now:

import discord
from discord.ext import commands
import requests
import os
from dotenv import load_dotenv
load_dotenv()
token = os.getenv('TOKEN')


intents = discord.Intents.all()
bot = commands.Bot(command_prefix='$', intents=intents)

@bot.event
async def on_ready():
    print("Bot is ready")
    print("Username: {}".format(bot.user.name))
    print("ID: {}".format(bot.user.id))
    print("Discord.py Version: {}".format(discord.__version__))
    print("------")

@bot.command()
async def linkwallet(ctx):
    await ctx.author.send("Click on this link to connect your MetaMask Wallet")
    await ctx.author.send("Link your wallet here: http://127.0.0.1:5000/{}".format(ctx.author.id))

@bot.command()
async def readuser(ctx, userid: str):
    r = requests.post('http://127.0.0.1:5000/read', json={'discordId': userid})
    await ctx.send(r.text)




bot.run(token)

So what we are doing is simple, first linkwallet command which actually sending a DM with a link like this to connect: http://127.0.0.1:5000/<discord-id>

And readuser gets a userid and send a JSON request to read and returns the JSON response back to the user!

Try it out!

Let's run the flask app:

cd flask
flask run

And let's run the bot:

cd discordbot
python bot.py

Video Demo:

Important Links 🔗

Thanks for reading!!!

Thanks for reading this article 🚀

Hope you learned something useful !!!