How to send the right file with discord - python

I am building a bot that sends a txt file based on a command.
Problem is that when two people send the same command at the same time, the bot only sends one of the two twice, which is something I should avoid at all costs.
Do you have any solutions?
I tried to come up with a solution but it didn't work: I tried to delete the file right after it has been sent, but as I said, it didn't work.
NOTE: the bot successfully sends two embeds with the correct information, but then sends two identical files (always the one generated by the second request)
Here's the code of the command:
#bot.command(name='sendfile', help='Sends a file', pass_context=True)
async def quick(ctx, *args):
try:
arg1 = str(args[0])
arg2 = args[1].capitalize()
arg3 = args[2].upper()
quantity = args[3]
generate_file(order_id, arg2, quantity)
except:
embed = Embed(title="FILE GENERATION", description="An error has occurred. Please retry", color=16711702)
fields = [("Error ID", "1", False)]
for name, value, inline in fields:
embed.add_field(name=name, value=value, inline=inline)
print(f"{bcolors.FAIL} [{datetime.now()}] - ERROR {bcolors.ENDC}")
raise TypeError
embed = Embed(title="FILE GENERATION", description="FILE DETAILS", color=15859711)
fields = [("ARG 1", f"{arg1}", False), ("Arg2", f"{arg2}", False), ("arg3", f'{arg3}',False), ("Quantity", f"{quantity}", False)]
for name, value, inline in fields:
embed.add_field(name=name, value=value, inline=inline)
await ctx.send(embed=embed)
print(f"{bcolors.OKCYAN} [{datetime.now()}] - Command successfully executed! {bcolors.ENDC}")
with open("generated_file.txt", "rb") as file:
await ctx.send(file=discord.File(file, "generated_file.txt"))
Thanks for your help :)

The solution I came up with it solves the problem but it's not the best, but it works.
The idea is basically that I generate an alphanumeric string and include that in the file name.
e.g.: 'file_fu56d.txt'
After creation, I send it over to discord and then delete it.
Here's some code:
list= []
random_string = get_random_string(5)
for p in range(0, int(quantity)):
to_append = f'{arg1}-something-{arg2}-soimething_else-{arg2}\n'
list.append(to_append)
# writes the txt file assigning each element of the list to each line
with open(f'file_{random_string}.txt', 'w') as file:
file.writelines(list)
print(f'[{datetime.now()}] - {quantity} lines successfully generated.')
# opens the file and sends it to discord
with open(f"file_{random_string}.txt", "rb") as file:
await ctx.send(file=discord.File(file, f"file_{random_string}.txt"))
print(f"[{datetime.now()}] - File successfully sent! ")
# deletes the file
os.remove(f'file_{random_string}.txt')
print(f"[{datetime.now()}] - 'file_{random_string}.txt' successfully deleted!")

Related

Attached PDF to MS Teams chatbot

I am trying to attach a pdf file in a MS Teams bot.
I get the following error " [on_turn_error] unhandled error: (BadArgument) Unknown attachment type". Would anyone know why it might not work?
The following is a portion of my code that concerns the error... unfortunately since it is a chatbot it is not appropriate to put the full code here.
Thank you for your advice.
class MyBot(ActivityHandler):
async def on_message_activity(self, turn_context: TurnContext):
elif str(turn_context.activity.text).upper() in {'PDF'}:
reply = Activity(type=ActivityTypes.message)
reply.text = "This is the pdf file."
reply.attachments = [self._get_inline_attachment()]
await turn_context.send_activity(reply)
#truncated#
def _get_inline_attachment(self) -> Attachment:
file_path = os.path.join(os.getcwd(), "TEST.pdf")
with open(file_path, "rb") as pdf_file:
dencoded_string = base64.b64encode(pdf_file.read()).decode()
return Attachment(
name="TEST.pdf",
content_type="application/pdf",
content_url=f"data:application/pdf;base64,{dencoded_string}",
)
#Nivedipa-MSFT indicated that the sample code for file sharing on MS Teams is avail here: https://github.com/microsoft/BotBuilder-Samples/blob/22fcff680a3e11006eb09b81ac5ed4de345933e2/archive/samples/python/56.teams-file-upload/bots/teams_file_bot.py
If I may add, "supportsFiles" has to be enabled in the manifest for this to work: https://learn.microsoft.com/en-us/microsoftteams/platform/resources/schema/manifest-schema#bots and https://learn.microsoft.com/en-us/microsoftteams/platform/resources/bot-v3/bots-files#configure-your-bot-to-support-files

Handle files in media group using aiogram

I need a sustainable method to handle files wrapped in media group.
My function handle_files is waiting for media files. When user uploads media file it goes through the series of different checks. If it passes all tests (size restriction, formats restriction) the media file is downloaded and processed.
It looks like this:
async def handle_files(message: types.Message, state: FSMContext):
user_data = await state.get_data()
locale = user_data['locale']
list_of_files = user_data['list_of_files']
try:
file = message.document
file_name = file['file_name']
except Exception as e:
await message.answer('Error while downloading file')
return None
file_name = unidecode(file_name)
file_size = file['file_size']
if file_size >= 20971520:
await message.answer('File is too big')
return None
invalid_format, formats = check_invalid_format(file_name, function)
if invalid_format:
await message.answer(file_name + 'has unsupported format. Supported formats: ' + ', '.join(formats))
return None
output_folder = os.path.join('temp', str(message.from_user.id))
if not os.path.exists(output_folder):
os.makedirs(output_folder, exist_ok=True)
file_path = os.path.join(output_folder, file_name)
await file.download(destination_file=file_path)
list_of_files.append(file_path)
await state.update_data(list_of_files=list_of_files)
await message.answer('Added files: '.format('\n'.join(list_of_files)))
Working with separate files looks fine. After downloading files user gets a list of added files one by one.
But the problem is that when user uploads files in media group one file overrides another. It looks like that.
So, only one file is appending to the list list_of_files that prevents me from processing both files.
I tried to solve the problem by initializing user_data dictionary one more time:
...
await file.download(destination_file=file_path)
user_data = await state.get_data()
list_of_files = user_data['list_of_files']
list_of_files.append(file_path)
await state.update_data(list_of_files=list_of_files)
...
It solved one part of my problem but this solution is not elegant and, supposedly, not quite sustainable. The message is duplicated.
I need that after uploading media group user gets one message containing the list of all files from this media group.
I suppose that the problem here is linked with asyncio. But I've spent already a lot of time but the solution haven't been found. Looking for you help.
check this link.
Here you can find the middleware, which will return list of messages from media group, and then you could use for loop to handle them.
import asyncio
from typing import List, Union
from aiogram import Bot, Dispatcher, executor, types
from aiogram.dispatcher.handler import CancelHandler
from aiogram.dispatcher.middlewares import BaseMiddleware
bot = Bot(token="TOKEN_HERE") # Place your token here
dp = Dispatcher(bot)
class AlbumMiddleware(BaseMiddleware):
"""This middleware is for capturing media groups."""
album_data: dict = {}
def __init__(self, latency: Union[int, float] = 0.01):
"""
You can provide custom latency to make sure
albums are handled properly in highload.
"""
self.latency = latency
super().__init__()
async def on_process_message(self, message: types.Message, data: dict):
if not message.media_group_id:
return
try:
self.album_data[message.media_group_id].append(message)
raise CancelHandler() # Tell aiogram to cancel handler for this group element
except KeyError:
self.album_data[message.media_group_id] = [message]
await asyncio.sleep(self.latency)
message.conf["is_last"] = True
data["album"] = self.album_data[message.media_group_id]
async def on_post_process_message(self, message: types.Message, result: dict, data: dict):
"""Clean up after handling our album."""
if message.media_group_id and message.conf.get("is_last"):
del self.album_data[message.media_group_id]
#dp.message_handler(is_media_group=True, content_types=types.ContentType.ANY)
async def handle_albums(message: types.Message, album: List[types.Message]):
"""This handler will receive a complete album of any type."""
media_group = types.MediaGroup()
for obj in album:
if obj.photo:
file_id = obj.photo[-1].file_id
else:
file_id = obj[obj.content_type].file_id
try:
# We can also add a caption to each file by specifying `"caption": "text"`
media_group.attach({"media": file_id, "type": obj.content_type})
except ValueError:
return await message.answer("This type of album is not supported by aiogram.")
await message.answer_media_group(media_group)
if __name__ == "__main__":
dp.middleware.setup(AlbumMiddleware())
executor.start_polling(dp, skip_updates=True)

discord.py Using custom checks for blacklist

What I'm trying to do: I had some people abuse my bot's commands, so I wanted to put a stop to this. Therefore, after reviewing multiple sources of code, I got as far as inserting member ids into a .txt document.
My problem: My 'custom check' has been having errors. All the custom check examples that I had found were in cogs or were not related to what I was trying to do, therefore I haven't gotten mine to work as expected yet. Note that I am not using cogs, either.
Code:
def blacklist(ctx): # this is the def used for the check, it reads the .txt file for user
file = open("blacklist.txt", "r")
members_banned = file.readlines()
if str(ctx.author.id) not in members_banned:
return ctx.author.id not in members_banned
#client.command() # this command is used for adding people to the blacklist. This is most likely not the problem..
#commands.is_owner()
async def blacklist(ctx, member: discord.Member):
file = open("blacklist.txt", "r")
anything = False
while anything == False:
content = file.read()
things = content.split("\n")
for line in things:
if str(line) == str(member.id):
await ctx.send(f"{member.name} is already in blacklist, please remove manually!")
anything = True
else:
pass
if anything == False:
thing = open("blacklist.txt", "a")
thing.write(f"\n{member.id}")
thing.close()
anything = True
await ctx.send(f"{ctx.author.mention}: {member.name} has been blacklisted, hopefully")
file.close()
break
#client.command()
#commands.check(blacklist) # This is the code I used to test the custom check for the blacklist
async def bltest(ctx):
await ctx.send("Cool, you're not blacklisted")
Errors: (via this code)
return await self.callback(*args, **kwargs)
TypeError: blacklist() missing 1 required positional argument: 'member'
The above error doesn't make any sense to me.
Others:
The .txt file
Other code I have looked at:
Code found after looking through previous questions asked in the discord.py server
Code found while trying to look for check examples, this is in a cog
The discord.py documentation
Both of your functions have the same name. The command is named blacklist with the ctx and member parameter, while the function's name is blacklist with just ctx as a parameter. This means if the wrong one is chosen, you're calling a function that expects 2 arguments & only giving it 1.
Give the first function (the one that reads the file) a different name, like "check_blacklist" or "blacklisted" or whatever suits you best.
As it says in the error in the first line of code add member as an argument to the function blacklist. You need it because in this function you are dealing with member of the discord server.

Why does my json file keep turning into a string of random words? (Python)

So i'm making some scoring system type thing in python with json and discord.py, this is its code:
import json
def getPoints(bot, user):
f = open('points.json', 'r')
points = json.load(f)
name = str(user)
f.close()
return points.get(name)
#later on down the line
#bot.group()
async def pointSystem(ctx):
pass
#pointSystem.command()
async def enable(ctx):
f = open('points.json', 'r')
points = json.load(f)
if points.get(str(ctx.author)) != None:
await ctx.send('Already enabled for this user!')
return
io = await ui.prompt(ctx, 'This command enables the point system\nAre you sure you want to enable this?')
if io == 'yes':
await ctx.send('Ok, enabling...')
points[str(ctx.author)] = 0
f.close()
f = open('points.json', 'w')
json.dump(points, f, indent=4)
f.close()
await ctx.send('Enabled for this user!')
else:
await ctx.send('Alright, stopping command execution...')
f.close()
in points.json i should have:
{}
And that's what i put in there originally, but sometimes, i look into points.json and i see a string with some random word in it. It doesn't give an error of any kind, it just does what i described, which doesn't make much sense, because i don't think i have any code that would set it to a string of any kind. I have no reproduction code, as i don't know the real culprit of this problem, i would point you towards doing something like what i did, which is creating a discord bot with this scoring system thing, but i don't really think even that would reproduce it.
Edit: Okay, this is getting really weird. 3 months later, with completely new code, I still have a file named "points.json" in my root directory, and it contains the random string I mentioned above, that literally makes no sense. Even when I remove it, a couple weeks later it will come back.

How can I remove the {''} in my variable?

I recently made a command that saves the information into a JSON file. So basically, I have 2 commands, first command sets the global variable, and the second command uses the variables provided to add into the JSON file. And once I tested it, it saves the text as a global variable, and then it saved into the JSON file as {'test'}. I don't want the {''}, so is there a way to don't have {''}, only the text test?
Script:
#global variables
namereg = None
cbreg = None #more
bdreg = None
descreg = None
libreg = None
invreg = None
btreg = None
ssreg = None
slugreg = None
#client.command(pass_context=True)
async def namereg(ctx, *, arg):
global namereg
namereg = {arg}
embed = discord.Embed(title='Registed Name.',description=f'Set the name as {arg}',colour=discord.Color.dark_green())
print(f'{arg}')
await ctx.send(embed = embed)
#client.command(pass_context=True)
async def add(ctx):
role_names = [role.name for role in ctx.message.author.roles]
if "Server Moderator" in role_names:
def write_json(data, filename='bots.json'):
with open (filename, "w") as f:
json.dump(data, f, indent=4)
with open ('bots.json') as json_file:
data = json.load(json_file)
temp = data["bots"]
y = {"name": f"{namereg}"}
temp.append(y)
write_json(data)
embed = discord.Embed(title='Added!',description='Successfully added with the following!',timestamp=ctx.message.created_at,colour=discord.Color.dark_green())
await ctx.send(embed = embed)
If there is a way to not have {''}, please reply to this thread! Thank you.
If you're writing it to a JSON file, the quotes will be added every time as part of JSON syntax. If you just need to write a dictionary to a file (which is also readable), you can write it to a normal .txt file.
Issues:
namereg = None
#client.command(pass_context=True)
async def namereg(ctx, *, arg):
global namereg
This is broken. Functions at the top level of your code are global variables, and are in the same namespace. Give it a different name from the storage variable.
namereg = {arg}
This takes the string that came from the user's input, and creates a set with a single element. That is not what you want. You wanted the input string to be the registered name, so just assign it directly.
y = {"name": f"{namereg}"}
I assume you did this fancy formatting because you were getting an error before (because the json will not serialize sets by default, because the JSON data format does not have a direct way to represent them). You should have listened to this error message more closely, by questioning why you had data of the invalid type in the first place. The {} and '' in your output come from the string representation of the set that you stringify using the string formatting. The plain string that you want to use does not require any formatting to convert to string, because it is already a string.

Categories