Search code examples
discord.pygiphy-api

Discord.py Rewrite Giphy Cog Error: Unclosed Connector


I'm trying to build a Giphy cog for my discord.py bot and it's throwing the following error

Unclosed client session
client_session: <aiohttp.client.ClientSession object at 0x000001CA509CDA60>
Unclosed connector
connections: ['[(<aiohttp.client_proto.ResponseHandler object at 0x000001CA51897BE0>, 77055.671)]']
connector: <aiohttp.connector.TCPConnector object at 0x000001CA509CDA30>

Does anyone know what is causing this and how I would fix it?

Here is my code:

import os
import aiohttp
import random
import discord
from discord.ext import commands
from dotenv import load_dotenv

load_dotenv()

prefix = os.getenv("CLIENT_PREFIX")
giphy_api = os.getenv("GIPHY_API_KEY")

command_attrs = {'hidden': False}


class FunCog(commands.Cog, name='Fun Commands', command_attrs=command_attrs):
    def __init__(self, client):
        self.client = client

    @commands.command(name='gif')
    async def _gif(self, ctx, *, search, json=None):
        embed = discord.Embed(colour=discord.Colour.blue())
        session = aiohttp.ClientSession()

        if search == '':
            response = await session.get('https://api.giphy.com/v1/gifs/random?api_key=' + giphy_api)
            data = json.loads(await response.text())
            embed.set_image(url=data['data']['images']['original']['url'])
        else:
            search.replace(' ', '+')
            response = await session.get(
                'http://api.giphy.com/v1/gifs/search?q=' + search + '&api_key=' + giphy_api + '&limit=10')
            data = json.loads(await response.text())
            gif_choice = random.randint(0, 9)
            embed.set_image(url=data['data'][gif_choice]['images']['original']['url'])
            await session.close()
            await ctx.send(embed=embed)


def setup(client):
    client.add_cog(FunCog(client))

I omitted my Giphy API Key for security reasons. I'm using discord.py rewrite and python 3.8.6if it helps.

Basically I want to be able to search a gif with it by tag and it will respond with a random gif from giphy for the specified tag.

--EDIT--

  1. Moved the error logging to my Events.py file
  2. Moved the API Keys to my .env file
  3. Removed the tenor_api env since it won't be getting used and has nothing to do with this question
  4. Updated the code a bit to resolve a few missing parameters.

---EDIT---

Thanks to Fixator10 for the response that fixed the issue I was having in this post.

Here is my working code if anyone wants to use it:

import os
import aiohttp
import random
import discord
import json
from discord.ext import commands
from dotenv import load_dotenv

load_dotenv()

prefix = os.getenv("CLIENT_PREFIX")
giphy_api = os.getenv("GIPHY_API_KEY")

command_attrs = {'hidden': False}


class FunCog(commands.Cog, name='Fun Commands', command_attrs=command_attrs):
    def __init__(self, client):
        self.client = client
        self.session = aiohttp.ClientSession()

    def cog_unload(self):
        self.client.loop.create_task(self.session.close())

    @commands.command(name='gif')
    async def _gif(self, ctx, *, search):
        session = self.session
        embed = discord.Embed(colour=discord.Color.dark_gold())

        if search == '':
            response = await session.get('https://api.giphy.com/v1/gifs/random?api_key=' + giphy_api)
            data = json.loads(await response.text())
            embed.set_image(url=data['data']['images']['original']['url'])
        else:
            search.replace(' ', '+')
            response = await session.get(
                'http://api.giphy.com/v1/gifs/search?q=' + search + '&api_key=' + giphy_api + '&limit=10')
            data = json.loads(await response.text())
            gif_choice = random.randint(0, 9)
            embed.set_image(url=data['data'][gif_choice]['images']['original']['url'])
            await ctx.send(embed=embed)


def setup(client):
    client.add_cog(FunCog(client))

It's probably not the best since it only uses a single tag but I'll probably improve that at another time xD


Solution

  • This is caused by your session behavior. You dont close session if search is empty. In two words: you should close session after usage.

    The most simple solution - is to use context manager:

        # https://docs.aiohttp.org/en/stable/#client-example
        async with aiohttp.ClientSession() as session:
            async with session.get('http://python.org') as response:
    

    Otherwise, you can create a session for your Cog and close it on unload:

    class MyCog(commands.Cog):
        def __init__(client):
            self.client = client
            self.session = aiohttp.ClientSession()
    
        # https://discordpy.readthedocs.io/en/stable/ext/commands/api.html#discord.ext.commands.Cog.cog_unload
        def cog_unload(self):
            # since close is coroutine, 
            # and we need to call it in sync func
            # lets create asyncio task
            self.client.loop.create_task(self.session.close()) 
            # or, we can use detach:
            # self.session.detach()
    
        @commands.command()
        async def mycmd(ctx):
            async with self.session.get("https://example.com") as response:
                await ctx.send(response.status)