Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Running Flask & a Discord bot in the same application

I am building a Discord bot in Python and would like to receive HTTP requests from Twitch.tv's API (See Webhooks Guide & Webhooks Reference) (To subscribe to events like; X streamer has gone live) and based on the content of the HTTP (POST or GET) request received from Twitch, do something on the Discord bot, e.g: Output a message on a text channel.

I am using the discord.py Python Discord API/Library.

I've looked into the matter and found that Flask seemed like a good minimalist choice for a webserver to receive these requests on.

I should preface this by saying I'm very new to Python and I've never used Flask before.

Now. The problem is I can't seem to figure out a way to run the Flask server inside of my discord bot.

I've tried adding this simple code into my discord.py script:

from flask import Flask, request
app = Flask(__name__)
@app.route('/posts', methods=['POST'])
def result():
    print(request.form['sched'])
    # Send a message to a discord text channel etc...
    return 'Received !'

When I run my discord.py script which looks something like this: (Stripped away some commands and features for the sake of keeping this shorter)

import discord
import asyncio

from flask import Flask, request
app = Flask(__name__)
@app.route('/posts', methods=['POST'])
def result():
    print(request.form['sched'])
    # Send a message to a discord text channel etc...
    return 'Received !'

client = discord.Client()

@client.event
async def on_ready():
    print('Logged in as')
    print(client.user.name)
    print(client.user.id)
    print('------')

@client.event
async def on_message(message):

    if message.author == client.user:
        return

    content = message.content
    fullUser = message.author.name+'#'+message.author.discriminator
    print(str(message.timestamp)+" #"+message.channel.name+" "+fullUser+": "+str(content.encode('ascii', 'ignore').decode('ascii')))
    if content.startswith('!'):

        content = content[1:]
        if content.startswith('test'):
            counter = 0
            tmp = await client.send_message(message.channel, 'Calculating messages...')
            async for log in client.logs_from(message.channel, limit=100):
                if log.author == message.author:
                    counter += 1

            await client.edit_message(tmp, 'You have {} messages.'.format(counter))

client.run('MyTokenHere')

It seems like if I point flask to discord.py (the above) and run it, it'll start the code, get to the "client.run('MyTokenHere')" part for discord, and just stop at that and run the discord bot. It's not until I exit out of the bot by doing Ctrl+C that the actual Flask server starts, but now the discord bot is disconnected and no longer does any processing.

The same problem persists if I were to for example add "app.run()" somewhere in my code (before calling "client.run()" which starts the Discord bot part) to launch the Flask server; It'll just run the flask, get stuck on that until I Ctrl+C out of the Flask server, then it'll proceed to start the Discord bot. Ultimately, I need to use the Discord API and I need to be connected to the Discord API gateway and all that good jazz to actually send messages to a channel with the bot, so I don't really know what to do here.

So. I think I've tried my best to explain what I'm ultimately trying to achieve here, and hopefully someone can help me find a way to either make this work with Flask, or if there's a better and easier way, provide a different solution.

like image 371
Haxo201 Avatar asked Feb 08 '18 19:02

Haxo201


2 Answers

As the kind commenters informed me; threading seems like the way to go. Thanks guys!

like image 171
Haxo201 Avatar answered Sep 28 '22 02:09

Haxo201


This is cog example in discord.py

I made this thing for dbl (Discord Bot Lists) you can implement, the thing you need.

Note: run your heroku app with webprocess

example :
web: python main.py

Then go on https://uptimerobot.com/ and setup to ping your web app after every 5 minutes

from aiohttp import web
from discord.ext import commands, tasks
import discord
import os
import aiohttp

app = web.Application()
routes = web.RouteTableDef()


def setup(bot):
    bot.add_cog(Webserver(bot))


class Webserver(commands.Cog):
    def __init__(self, bot):
        self.bot = bot
        self.web_server.start()

        @routes.get('/')
        async def welcome(request):
            return web.Response(text="Hello, world")

        @routes.post('/dbl')
        async def dblwebhook(request):
            if request.headers.get('authorization') == '3mErTJMYFt':
                data = await request.json()
                user = self.bot.get_user(data['user']) or await self.bot.fetch_user(data['user'])
                if user is None:
                    return
                _type = f'Tested!' if data['type'] == 'test' else f'Voted!'
                upvoted_bot = f'<@{data["bot"]}>'
                embed = discord.Embed(title=_type, colour=discord.Color.blurple())
                embed.description = f'**Upvoter :** {user.mention} Just {_type}' + f'\n**Upvoted Bot :** {upvoted_bot}'
                embed.set_thumbnail(url=user.avatar_url)
                channel = self.bot.get_channel(5645646545142312312)
                await channel.send(embed=embed)
            return 200

        self.webserver_port = os.environ.get('PORT', 5000)
        app.add_routes(routes)

    @tasks.loop()
    async def web_server(self):
        runner = web.AppRunner(app)
        await runner.setup()
        site = web.TCPSite(runner, host='0.0.0.0', port=self.webserver_port)
        await site.start()

    @web_server.before_loop
    async def web_server_before_loop(self):
        await self.bot.wait_until_ready()
like image 40
WiperR Avatar answered Sep 28 '22 02:09

WiperR