Giphy API not responding in discord.py - discord.py

I made a discord bot that shows you gifs when you type a certain command but the problem is that it works fine in the first half but takes a long time to show the gifs when not used.
Basically it doesn't show the gifs instantly when not used.
Here's the code that I've written:
#client.command()
async def gif(ctx, *, q = 'dance'):
api_key = 'Some Key here'
api_instanc = giphy_client.DefaultApi()
try:
api_responce = api_instanc.gifs_search_get(api_key, q, limit = 7,rating = 'r')
lst = list(api_responce.data)
giff = random.choice(lst)
emb = discord.Embed(title = f"Requested by {ctx.author} " + q )
emb.set_image(url= f'https://media.giphy.com/media/{giff.id}/giphy.gif')
await ctx.channel.send(embed = emb)
except ApiException as e:
await ctx.channel.send("API EXCEPTION")
It doesn't show any errors but doesn't work after the long time.
Any re-write of the code with aiohttp will be appreciated because I am learning that.

I think the module you are using is not asynchronous which leads to blocking read more.
Default in the command is search = None you can use that with an if statement to check.
After that is the request for the api to get the image.
Here is the code edited to use aiohttp
# import aiohttp
# import random
#bot.command()
async def giphy(ctx, search: str = None):
api_key = ""
embed = discord.Embed(title=f"Requested by {ctx.author}")
async with aiohttp.ClientSession() as session:
# search
if search:
embed.description = search
async with session.get(f'http://api.giphy.com/v1/gifs/search?q={search}&api_key={api_key}&limit=10') as response:
data = await response.json()
gif_choice = random.randint(0, 9)
embed.set_image(url=data['data'][gif_choice]['images']['original']['url'])
# radnom
else:
async with session.get(f'http://api.giphy.com/v1/gifs/random?api_key={api_key}&limit=10') as response:
data = await response.json()
embed.set_image(url=data['data']['images']['original']['url'])
await ctx.send(embed=embed)

Related

Discord.py create an embed message command

I was trying to make a embed command with time limit, só i started doing it, and at my first try to make it timed it didn't work.
So i searched here, found one sample and tried to do like it (as you can see at my code below), but it didn't work too.
Then i tried to copy that code and pasted at my bot.
Guess...it didn't work -_-, now i'm here, asking gods to help.
client.command(aliases=["createEmbed", "embedCreate"])
async def embed(ctx,):
Questions = ["Titulo?", "Cor? (Hex sem #)", "Descrição?"]
Responses = []
def check(m):
return m.author == ctx.author and m.channel == ctx.channel
#try:
await ctx.send(Questions[0])
title = await client.wait_for('message',timeout=2.0,check=check)
#except asyncio.TimeoutError(*title):
#await ctx.send("Demorou muito")
#return
#else:
Responses.append(title.content)
await ctx.send(Questions[1])
cor = await client.wait_for('message',timeout=25.0,check=check)
Responses.append(cor.content)
await ctx.send(Questions[2])
descrição = await client.wait_for('message',timeout=25.0,check=check)
Responses.append(descrição.content)
cor2 = Responses[1]
corHex = int(cor2, 16)
embedVar = discord.Embed(title=Responses[0], description=Responses[2], color=corHex)
await ctx.send(embed=embedVar)
At my code you can see the piece with #, that is the piece that i tried to test the time command.
If i remove the time command, it works.
It is better to loop through questions and get the date after that make the embed. Below is an example of how to get the Title and Description. You can modify it how you like
#client.command(aliases=["createEmbed", "embedCreate"])
async def embed(ctx):
questions = ["Title?", "Description?"]
responses = []
def check(m):
return m.author == ctx.author and m.channel == ctx.channel
for question in questions:
try:
await ctx.send(question)
message = await client.wait_for('message', timeout=15, check=check)
except asyncio.TimeoutError:
await ctx.send("Timeout")
return
else:
responses.append(message.content)
embedVar = discord.Embed(title=responses[0], description=responses[1])
await ctx.send(embed=embedVar)

News command discord.py

I've made a news command in discord.py using newsapi, I'm having a problem where it sends the top 10 headlines one by one, I wanna make it so that it sends the headline in one embed.
Here's the code :
#commands.command()
async def news(self, ctx):
open_bbc_page = requests.get(main_url).json()
article = open_bbc_page["articles"]
results = []
for ar in article:
results.append(ar["title"])
for i in range(len(results)):
ree = (i + 1, results[i])
em = discord.Embed(title="Here's the trending news", description = f"{ree}",color = 0xa3a3ff)
await ctx.send(embed=em)[enter image description here][1]
[1]: https://i.stack.imgur.com/Fel1n.jpg
You could put them all into the description of the embed like this:
for ar in article:
results.append(ar["title"])
embeddescription = '/n'.join(results)
em = discord.Embed(title="Here's the trending news", description = embeddescription,color = 0xa3a3ff)
await ctx.send(embed=em)
Just need to make sure they arent longer than 2048 characters combined, this is the limit for embed descriptions.
def set_news(ctx, content)
with open('news.json', 'r') as f:
msg = json.load(f)
msg[str("news")] = content
with open('news.json', 'r') as f:
json.dump(msg, f)
def check_news()
with open('news.json', 'r') as f:
msg = json.load(f)
return msg
#bot.command()
async def news_set(ctx, content):
set_news(ctx, content)
await ctx.send("I set news to {content}!")
#bot.command()
async def news(ctx):
news = check_news()
await ctx.send(f"News: {news}")

My giveaway cog command isn't working for some reason

So I made a giveaway command in a cog, and for some weird reason it's not working. I'm not getting any errors or anything. Since it's "interactive", the first two questions work fine but once it asks for the prize, it just freezes and doesn't do anything after that. My friend is also working on a different bot and gave me his code which worked perfectly on his end, but not on mine. Here is my code:
import discord
from discord.ext import commands
from discord.ext.commands import BucketType, cooldown, CommandOnCooldown
import random
import json
import datetime
import asyncio
class Giveaway(commands.Cog):
"""Giveaway commands. You must have manage message permissions to use these"""
def __init__(self, bot):
self.bot = bot
# Helper functions
def convert(self, time):
pos = ["s", "m", "h", "d"]
time_dict = {"s": 1, "m": 60, "h": 3600, "d": 2600*24}
unit = time[-1]
if unit not in pos:
return -1
try:
val = int(time[:-1])
except:
return -2
return val * time_dict[unit]
# Bot Events
# Bot Commands
#commands.command()
#commands.has_permissions(kick_members=True)
async def giveaway(self, ctx, host: discord.Member):
await ctx.send("Let's start with this giveaway! Answer these questions within 15 seconds!")
questions = ["Which channel should it be hosted in?",
"What should be the duration of the giveaway? (s|m|h|d)",
"What is the prize of the giveaway?"]
answers = [f"{ctx.channel.mention}"]
def check(m):
return m.author == ctx.author and m.channel == ctx.channel
for i in questions:
await ctx.send(i)
try:
msg = await self.bot.wait_for('message', timeout=15.0, check=check)
except asyncio.TimeoutError:
await ctx.send('You didn\'t answer in time, please be quicker next time!')
return
else:
answers.append(msg.content)
try:
c_id = int(answers[0][2:-1])
except:
await ctx.send(f"You didn't mention a channel properly smh")
return
channel = self.bot.get_channel(c_id)
time = convert(answers[2])
prize = answers[3]
await ctx.send(f"The giveaway for {prize} will be in {channel.mention} and will last {answers[2]}!! Hosted by {host.mention}")
embed = discord.Embed(title="Giveaway!", description=f"{prize}", color=discord.Colour.dark_purple())
embed.add_field(name="Hosted by:", value=f"{host}")
embed.set_footer(text=f"Ends {answers[2]} from now!")
my_msg = await channel.send(embed=embed)
await my_msg.add_reaction("🎉")
await asyncio.sleep(time)
new_msg = await channel.fetch_message(my_msg.id)
users = await new_msg.reactions[0].users().flatten()
users.pop(users.index(self.bot.user))
winner = random.choice(users)
await channel.send(f"Congratulations! {winner.mention} won the prize: {prize} from {host.mention}!!")
def setup(bot):
bot.add_cog(Giveaway(bot))
Please let me know what I should do to fix it!!

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--
Moved the error logging to my Events.py file
Moved the API Keys to my .env file
Removed the tenor_api env since it won't be getting used and has nothing to do with this question
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
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)

Python Youtube ffmpeg Session Has Been Invalidated

I get the following error while I'm playing YouTube audio with my bot
[tls # 0000024ef8c4d480] Error in the pull function.
[matroska,webm # 0000024ef8c4a400] Read error
[tls # 0000024ef8c4d480] The specified session has been invalidated for some reason.
Last message repeated 1 times
It seems like YouTube links expire? I don't really know but I need to fix this issue. This is my code:
class YTDLSource(discord.PCMVolumeTransformer):
def __init__(self, source, *, data, requester):
super().__init__(source)
self.requester = requester
self.title = data['title']
self.description = data['description']
self.uploader = data['uploader']
self.duration = data['duration']
self.web_url = data['webpage_url']
self.thumbnail = data['thumbnail']
def __getitem__(self, item: str):
return self.__getattribute__(item)
#classmethod
async def create_source(cls, ctx, player, search: str, *, loop, download=True):
async with ctx.typing():
loop = loop or asyncio.get_event_loop()
to_run = partial(ytdl.extract_info, url=search, download=download)
raw_data = await loop.run_in_executor(None, to_run)
if 'entries' in raw_data:
# take first item from a playlist
if len(raw_data['entries']) == 1:
data = raw_data['entries'][0]
else:
data = raw_data['entries']
#loops entries to grab each video_url
total_duration = 0
try:
for i in data:
webpage = i['webpage_url']
title = i['title']
description = i['description']
uploader = i['uploader']
duration = i['duration']
thumbnail = i['thumbnail']
total_duration += duration
if download:
source = ytdl.prepare_filename(i)
source = cls(discord.FFmpegPCMAudio(source), data=i, requester=ctx.author)
else:
source = {'webpage_url': webpage, 'requester': ctx.author, 'title': title, 'uploader': uploader, 'description': description, 'duration': duration, 'thumbnail': thumbnail}
player.queue.append(source)
except Exception as e:
print(e)
return
embed=discord.Embed(title="Playlist", description="Queued", color=0x30a4fb, timestamp=datetime.now(timezone.utc))
embed.set_author(name=ctx.author.display_name, icon_url=ctx.author.avatar_url)
embed.set_thumbnail(url=data[0]['thumbnail'])
embed.add_field(name=raw_data['title'], value=f"{len(data)} videos queued.", inline=True)
embed.set_footer(text=raw_data["uploader"] + ' - ' + '{0[0]}m {0[1]}s'.format(divmod(total_duration, 60)))
await ctx.send(embed=embed)
return
embed=discord.Embed(title="Playlist", description="Queued", color=0x30a4fb, timestamp=datetime.now(timezone.utc))
embed.set_author(name=ctx.author.display_name, icon_url=ctx.author.avatar_url)
embed.set_thumbnail(url=data['thumbnail'])
embed.add_field(name=data['title'], value=(data["description"][:72] + (data["description"][72:] and '...')), inline=True)
embed.set_footer(text=data["uploader"] + ' - ' + '{0[0]}m {0[1]}s'.format(divmod(data["duration"], 60)))
await ctx.send(embed=embed)
if download:
source = ytdl.prepare_filename(data)
else:
source = {'webpage_url': data['webpage_url'], 'requester': ctx.author, 'title': data['title'], 'uploader': data['uploader'], 'description': data['description'], 'duration': data['duration'], 'thumbnail': data['thumbnail']}
player.queue.append(source)
return
source = cls(discord.FFmpegPCMAudio(source), data=data, requester=ctx.author)
player.queue.append(source)
#classmethod
async def regather_stream(cls, data, *, loop):
loop = loop or asyncio.get_event_loop()
requester = data['requester']
to_run = partial(ytdl.extract_info, url=data['webpage_url'], download=True)
data = await loop.run_in_executor(None, to_run)
return(cls(discord.FFmpegPCMAudio(data['url']), data=data, requester=requester))
I'm using the rewrite branch of discord.py for the bot.
I'm not sure if I need to provide more details? Please let me know, I really need to get this fixed...
In fact it isn't really a problem with your code (and many people complain of this error).
This is just a possible issue when streaming a video. If you absolutely want to stream it, you have to accept this as a potential issue. Note how (almost) every music bots set limitations for the video/music you want to listen to.
If you need to ensure you do not get this issue, you have to fully download the music. (Which will also make the bot loading longer before playing).
Would you be able to post all your code? i may have a solution for you if i was able to see the whole code.
the solution i would recomend is to download the soong and then delete it after.
You could set your download to true and then add this in your player_loop
try:
# We are no longer playing this song...so, lets delete it!
with YoutubeDL(ytdlopts) as ydl:
info = ydl.extract_info(source.web_url, download=False)
filename = ydl.prepare_filename(info)
try:
if os.path.exists(filename):
os.remove(filename)
else:
pass
except Exception as E:
print(E)
await self.np.delete()
except discord.HTTPException:
pass
bit botched but could be cleaned up, this was the best solution i have found for me.

Resources