discord.py invalid form of body - python

code:
#bot.command(name="whois")
async def whois(ctx,user:nextcord.Member=None):
if user==None:
user=ctx.author
rlist = []
for role in user.roles:
if role.name != "#everyone":
rlist.append(role.mention)
b = ", ".join(rlist)
embed = nextcord.Embed(colour=user.color,timestamp=ctx.message.created_at)
embed.set_author(name=f"User Info - {user}"),
embed.set_thumbnail(url=user.avatar.url),
embed.set_footer(text=f'Requested by - {ctx.author},
icon_url=ctx.author.avatar.url)
embed.add_field(name='ID:',value=user.id,inline=False)
embed.add_field(name='Name:',value=user.display_name,inline=False)
embed.add_field(name='Created at:',value=user.created_at,inline=False)
embed.add_field(name='Joined at:',value=user.joined_at,inline=False)
embed.add_field(name='Bot?',value=user.bot,inline=False)
embed.add_field(name=f'Roles:({len(rlist)})',value=''.join([b]),inline=False)
embed.add_field(name='Top Role:',value=user.top_role.mention,inline=False)
await ctx.send(embed=embed)
error:
nextcord.ext.commands.errors.CommandInvokeError: Command raised an exception: HTTPException: 400 Bad Request (error code: 50035): Invalid Form Body
In embeds.0.fields.5.value: Must be 1024 or fewer in length
how do I fix? because my code is shorter than 2000

I assume it's an issue with you putting too much text into the embed's field. How many roles are in rlist? If it's more than 47 (46.54 ~ 1024 / 22), then it's going to get mad.
You'll need to put a cap on the number of characters/roles that can appear in that field.
edit: Also, why do you cast b to a list and then join it? You're just turning a string into a list, then back into the exact same string.

In set_footer you forgot to add a second ' so it makes the footer way to long for discord to handle

Related

Can I use an f string to design a url for my discord.py bot's embeds' images?

I'm creating a command for a discord bot and I want it to be able to send me an animated sprite from the Pokemon Showdown website.
#client.command()
async def dex(ctx, args):
args_l = args.lower()
if "," in args_l:
args_split = args_l.split()
pokemon = args_split[1]
url = "https://media2.giphy.com/media/3oEjI6SIIHBdRxXI40/200.gif"
tag = args_split[0]
if tag == "shiny":
url = f"play.pokemonshowdown.com/sprites/ani-shiny/{pokemon}.gif"
elif tag == "mega":
url = f"play.pokemonshowdown.com/sprites/xyani/{pokemon}-mega.gif"
else:
url = f"play.pokemonshowdown.com/sprites/xyani/{pokemon}.gif"
embed = discord.Embed(title="POKEDEX", color=c, timestamp=datetime.datetime.now())
embed.set_footer(text="Pokedex")
embed.description = f"Pokemon: {pokemon}\n\nApplicable Tags: {tag}\n\nThis auction has increments of {increment}\n\nAutobuy: {ab}"
embed.set_image(url=url)
await ctx.send(embed=embed)
When I run this command, dex, my bot does not send an embed (await ctx.send(embed=embed) isn't sending an embed). There is no error message. When I try using a command that isn't added to the bot, it responds with a message, "I do not have this command" (which I set it to do with a CommandNotFound error checker).
I think the problem is either in the urls or the embed.set_image(url=url).
You are trying to send pokemon, tag, ab, and increment variables even though they are not set when there is no ',' in args, also the urls have to have 'https://' at the front for it to work. Something like this seems to work but then again you need to make sure you cover the case where there is no ',' in args.
#client.command()
async def dex(ctx, args):
pokemon = args
args_l = args.lower()
if "," in args_l:
args_split = args_l.split(',')
pokemon = args_split[1]
url = "media2.giphy.com/media/3oEjI6SIIHBdRxXI40/200.gif"
tag = args_split[0]
if tag == "shiny":
url = f"play.pokemonshowdown.com/sprites/ani-shiny/{pokemon}.gif"
elif tag == "mega":
url = f"play.pokemonshowdown.com/sprites/xyani/{pokemon}-mega.gif"
else:
url = f"play.pokemonshowdown.com/sprites/xyani/{pokemon}.gif"
embed = discord.Embed(
title="POKEDEX", color=discord.Colour.random(), timestamp=datetime.datetime.now())
embed.set_footer(text="Pokedex")
embed.description = f"Pokemon: {pokemon}\n\nApplicable Tags: {tag}\n\nThis auction has increments of \n\nAutobuy: "
embed.set_image(url="https://"+url)
await ctx.send(embed=embed)
Without an error message, it is difficult to know for sure, but it looks like this is your problem:
If there is no , in your command, the pokemon variable is unset when you try to use it in url = f"play.pokemonshowdown.com/sprites/xyani/{pokemon}.gif"
It looks like you need to modify the else block to something like the following:
else:
pokemon = args_l
url = f"play.pokemonshowdown.com/sprites/xyani/{pokemon}.gif"
You also need to specify what character you're splitting with earlier on in the function, as str.split() without any argument splits on whitespace rather than a comma which appears to be what you want:
args_split = args_l.split(",")

discord.py Changing file size to add custom emoji

What I'm trying to do: I have an on_message event for a 'global chat' command that I have created. This would send messages to any server that was in the json, currently supporting images, multiple lines of text, and custom emojis (as long as the bot shared a server with said emoji).
My problem: As you can see in the image above, one person, 'S o u p', has their profile picture replaced with a duck emoji. For context's sake, their avatar is not that of a duck emoji. However, if you were to look at the most recent message, the profile picture is shown. I believe that the problem lies in the size of the message.author's avatar. The PIL library may be a good solution, but I do not want to save the image.
Code I have tried:
avatar_bytes = await message.author.avatar_url.read()
guildem = client.get_guild(738285544911405107)
try:
em = await guildem.create_custom_emoji(name=f'{message.author.name}', image=avatar_bytes)
except:
em = '🦆'
The code above is the one I had used in the image example I had provided.
avatar = message.author.avatar_url_as(size=128) # resize avatar
avatar_bytes = await avatar.read() # then read as bytes?
Above code gives exact same results. Avatars are turned into duck emojis.
Others I have looked at:
How to crop an image to a shape for eg. circle using Pillow in Discord.py?: Messages did not send when using the code here. I assumed that using the PIL library would help reduce the file size significantly one way or the other without saving the image.
How to reduce the image file size using PIL: All the answers required saving the image, which I do not want to do. I tried tweaking the code a little bit, but caused all avatars to be turned into duck emojis.
If necessary, I will include the entire on_message event. However, this does not feel relevant nor necessary to include since the emojis are only a small portion of said event.
Edit: Here is the code to the entire on_message event.
#client.event
async def on_message(message):
if message.author == client.user: # stop bot from replying to self
return
else:
if not message.guild: # prevent errors when messaging in bot dm's
return
f = open("global text.json") # existing json
"""
{
"747061937673732097": 765059798131539988,
"724783642546536458": 818707151122989119,
"761524419003809832": 813963773295591485,
"755309786232258630": 760381389685784587,
"738285544911405107": 738285544911405110
}
"""
data = json.load(f)
for i in data:
if str(i) == str(message.guild.id):
if data[str(message.guild.id)] == message.channel.id:
avatar_bytes = await message.author.avatar_url.read()
guildem = client.get_guild(738285544911405107) # guild to upload emojis to
try:
em = await guildem.create_custom_emoji(name=f'{message.author.name}', image=avatar_bytes) # create emoji using avatar_bytes in guildem server
except:
em = '🦆'
### functions ###
def censor(text): # censor words for safety
banned = ["test"] # bad words were here, removed for stackoverflow reasons
text = text.split()
nearly = []
for word in text:
if any(item in word.lower() for item in banned):
word = word.lower()
replaced = word.replace(word, ('#'*(len(word))))
nearly.append(replaced)
else:
nearly.append(word)
message = ' '.join(nearly)
return message
def parag(string): # check for \n
length = [string]
if string in ["", " ", " ", " "]:
return "<:transparent:774136812813811713>"
final_list = []
if '\n' in string:
length = string.split('\n')
for item in length:
thing = censor(item)
final_list.append(thing)
return paragger(final_list)
def paragger(list: list): # returns original message with \n
message = ""
for item in list:
message += f"> {item}\n"
return message
msg = parag(message.content)
### attachment handling ###
embed_list = []
if message.attachments:
if len(message.attachments) == 1:
embed = discord.Embed(title=f"{message.author}'s file", color=0xc39ce6)
for file in message.attachments:
embed.set_image(url=file.url)
embed_list.append(embed)
else:
count = 1
for file in message.attachments:
embed = discord.Embed(title=f"{message.author}'s file {count}")
embed.set_image(url=file.url)
embed_list.append(embed)
for i in data:
if str(i) != str(message.guild.id):
channel = client.get_channel(data[str(i)])
try:
await channel.send(f"{em} {message.author} \n{msg}", allowed_mentions=allowed_mentions)
if len(embed_list) > 0:
for embed in embed_list:
await channel.send(embed=embed)
except:
await channel.send(f"{em} {message.author} \n> {em}", allowed_mentions=allowed_mentions) # only happens if message is larger than 2000 char
if em != '🦆':
await em.delete() # delete the custom emoji to make space for more emojis later on
break
await client.process_commands(message) # process, prevent commands breaking
Your issue is here:
em = await guildem.create_custom_emoji(name=f'{message.author.name}', image=avatar_bytes)
You see, discord.py is looking for an emoji name with alphanumeric characters or underscores. S o a p does not fit this requirement, hence the error you have gotten. The following fixes the issue by replacing said characters with an underscore.
avatar_bytes = await message.author.avatar_url_as(size=128).read() # size=128 makes the size smaller for emojis
name = message.author.name.replace('-', '_').replace(' ', '_') # Replace the erroring characters
try:
em = await guildem.create_custom_emoji(name=f'{name}', image=avatar_bytes)
except:
em = '🦆'
EDIT: After taking another look, the problem wasn't what I thought after all, the issue with the user named S o a p was that it had non alphanumeric characters, I've updated my answer to fix this.
You don't need to save image. This is how you send images to discord without saving it:
import io
import PIL
image = PIL.Image.new("RGB", (200, 200), (255, 255, 255))
bytes_io = io.BytesIO()
image.save(bytes_io, format="png")
bytes_io.seek(0)
file = discord.File(bytes_io, "white.png")
#now you can send this file to discord
await ctx.send(file=file)

Python Split string after 2000 characters

I'm working on a discord bot that can return the summary of Wikipedia articles. but there's an issue, some summaries are longer than 2000 characters, which exceeds discord's character limit. is there a way I can split my string into multiple messages?
The string I want to split is str(wikipedia.search(query)) (the entire thing is in an embed block):
embedVar = discord.Embed(title=str(query)
description=str(wikipedia.search(query)), color=0x9CAFBE)
await message.channel.send(embed=embedVar)
heres a solution:
article = "5j5rtOf8jMePXn7a350fOBKVHoAJ4A2sKqUERWxyc32..." # 4000 character string i used
chunklength = 2000
chunks = [article[i:i+chunklength ] for i in range(0, len(article), chunklength )]
print(len(chunks))
output
2
an extension on how you could use it:
for chunk in chunks:
embedVar = discord.Embed(title="article name",
description=chunk ,
color=0x9CAFBE)
await ctx.send(embed=embedVar)
To expand what Darina commented, splice the string before you post it on discord.
posted_string = str(wikipedia.search(query))[:2000]
embedVar = discord.Embed(title=str(query),
description=posted_string,
color=0x9CAFBE) await message.channel.send(embed=embedVar)
A 'string' is an array of characters. When you assign it to another variable by using [:2000], you're telling the interpreter to put all the characters from the beginning of the array up to, but not including, the 2000th character.
EDIT:
As Ironkey mentions in the comments, hardcoding values isn't viable since we don't know exactly how many characters an article has. Try this untested code instead:
wiki_string = str(wikipedia.search(query))
string_length = len(wiki_string)
if string_len < 2000:
embedVar = discord.Embed(title=str(query),
description=wiki_string,
color=0x9CAFBE)
await message.channel.send(embed=embedVar)
else:
max_index = 2000
index = 0
while index < (string_length - max_index):
posted_string = wiki_string[index:max_index]
embedVar = discord.Embed(title=str(query),
description=posted_string,
color=0x9CAFBE)
await message.channel.send(embed=embedVar)
index = index + max_index
posted_string = wiki_string[index-max_index:]
embedVar = discord.Embed(title=str(query),
description=wiki_string,
color=0x9CAFBE)
await message.channel.send(embed=embedVar)
If this doesn't work, please let me know where it failed. Thanks!

how to get messages by date using TELETHON?

how to get message that posted today using TELETHON
I'm using the below code
date_of_post = datetime.datetime(2019, 12, 24)
with TelegramClient(name, api_id, api_hash) as client:
for message in client.iter_messages(chat , offset_date = date_of_post):
print(message.sender_id, ':', message.text)
offset_date is used to get messages prior to that date. So you should use the day after:
async def get_messages_at_date(chat, date):
result = []
tomorrow = date + datetime.timedelta(days=1)
async for msg in client.iter_messages(chat, offset_date=date):
if msg.date < date:
return result
result.append(msg)
The point made by #Lonami is valid - offset_date is used to get messages prior to that date. However, the docs describe another argument called reverse that you can supply to iter_messages :
reverse (bool, optional):
If set to True, the messages will be returned in reverse order (from oldest to newest, instead of the default newest to oldest). This also means that the meaning of offset_id and offset_date parameters is reversed, although they will still be exclusive.
So, if you use it like this:
for message in client.iter_messages(chat, reverse = True, offset_date = date_of_post):
print(message.sender_id, ':', message.text)
it should work as you expected.

how to automatically delete channels with a discord bot

I'm doing a python bot for discord. it create an delete channel according to player instructions.
I want to create a garbage collector that test all the server.channels and delete the outdated one.
I do :
async def waitTimer():
while True:
await asyncio.sleep(10)
regex = re.compile(r"[0-9]*_[a-z0-9]*-[0-9]*") #nom des channels de raid
for cCurrent in client.get_all_channels():
if regex.match(cCurrent.name):
numRaid = int(cCurrent.name[0])
cRaidCurrent = cRaids[numRaid]
date = datetime.datetime.now()
print (cRaidCurrent.raid.fin.timestamp())
print (date.timestamp())
if cRaidCurrent.raid.fin < date:
if cRaidCurrent.retirerRaid():
cId = cRaidCurrent.com.id
await removeFromListe(cRaidCurrent)
await client.delete_channel(client.get_channel(cId))
cCurrent = 0
Sometimes it pass and sometimes I get this error :
for cCurrent in client.get_all_channels():
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/discord/client.py", line 581,
in get_all_channels
for channel in server.channels:
RuntimeError: dictionary changed size during iteration
If I understand it clearly the client.get_all_channels is a dictionary and I can't remove the channels during the iteration ... So the question is what other possibilities do I have to remove those channel ?
Thanks Patrick Haugh for the answer that work perfectly.
In the end the deleting operation needs to be done in 2 times. Here is the code if anyone needs it :
for cCurrent in client.get_all_channels():
if regex.match(cCurrent.name):
numRaid = getNumChannel(cCurrent.name)
cRaidCurrent = cRaids[numRaid]
now = datetime.datetime.now()
if cRaidCurrent.raid.fin < now:
toDelete.append(cRaidCurrent)
for cRaidCurrent in toDelete:
cId = cRaidCurrent.id
cRaidCurrent.retirerRaid()
await removeCRaid(cRaidCurrent)
del cRaids[cId]

Categories

Resources