I own a discord.py bot. I made a few commands that can create a file with a few simple functions. I have a command who let me read the file, but, I want to improve it. I want that the bot read a specific line of the file. Like if i say +file read test txt 9, the bot will read the file test.txt at line 9.
I use a command group in a cog (cogs/owner.py). The command group called "file" who has a False invoke_without_command() (#commmands.group(invoke_without_command=False))
I tried first to just put the "line" variable in the await ctx.send(f.read(line)), but it didn’t worked.
This is the MRE (minimum reproducible example) of the code:
cogs/owner.py
from discord.ext import commands
import discord
class Owner(commands.Cog):
# the init function
#commands.group(invoke_without_command=False)
async def file(self, ctx):
print=" "
#file.command()
async def read(self, ctx, filename, extension, line=None):
if line == None:
f = open(f"{filename}.{extension}", "r")
await ctx.send(f.read())
else:
f = open(f"{filename}.{extension}", "r")
await ctx.send(f.read(line))
"""
also tried this:
await ctx.send(f.read(type(Line)))
""""
#setup function
The read function doesn't understand line numbers. You want something like this:
await ctx.send(f.readlines()[line-1])
... the - 1 is because Python arrays like the array of lines returned by readlines start counting from 0, but text file line numbers usually start at 1.
Though that will blow up with an exception if the file doesn't have that many lines, so you may want to add an except clause to handle that case.
Related
I have defined a variable but I am getting a "name not defined" error regardless of where I place the code using the variable or where I place the variable definition code.
In this instance, the defining code is as follows:
def get_prefix(client, message):
with open('prefixes.json', 'r') as f:
prefixes = json.load(f)
return prefixes[str(message.guild.id)]
The line that is throwing an error is:
client = commands.Bot(command_prefix = get_prefix)
If I were to use the following code instead as an example:
client = commands.Bot(command_prefix = '$')
Then I have no errors, but that's not very dynamic and not what I'm looking for.
Regardless of where these blocks of code are placed, the same error is thrown and I don't understand what exactly the problem is.
I'd managed to solve the issue by running
from cogs.Prefixes import get_prefix in my main file.
I'm trying to get the thumbnail of the currently playing media on windows, and thanks to this answer (https://stackoverflow.com/a/66037406/15491505) i got quite far, however I'm facing a strange issue where in get_thumbnail() the variable byte_buffer always ends up being 0 in length after the first run... as in the first time I call it I get back the thumbnail perfectly, but all further calls end up failing...
This is what I have so far:
from winrt.windows.media.control import GlobalSystemMediaTransportControlsSessionManager as MediaManager
from winrt.windows.storage.streams import DataReader, Buffer, InputStreamOptions
from io import BytesIO
from PIL import Image
import asyncio
async def get_thumbnail():
sessions = await MediaManager.request_async()
current_session = sessions.get_current_session()
if current_session:
properties = await current_session.try_get_media_properties_async()
media_info = {song_attr: properties.__getattribute__(song_attr) for song_attr in dir(properties) if song_attr[0] != '_'}
if media_info.get('thumbnail'):
thumb_stream_ref = media_info['thumbnail']
thumb_read_buffer = Buffer(5000000)
readable_stream = await thumb_stream_ref.open_read_async()
readable_stream.read_async(thumb_read_buffer, thumb_read_buffer.capacity, InputStreamOptions.READ_AHEAD)
buffer_reader = DataReader.from_buffer(thumb_read_buffer)
byte_buffer = buffer_reader.read_bytes(thumb_read_buffer.length)
binary = BytesIO()
binary.write(bytearray(byte_buffer))
binary.seek(0)
print(len(bytearray(byte_buffer)))
img = Image.open(binary)
return img
thumbnail = asyncio.run(get_thumbnail())
thumbnail.show()
# This will work
thumbnail2 = asyncio.run(get_thumbnail())
thumbnail2.show()
# This will not
Example output:
C:\Users\willy\Desktop>test.py
117672
0
Traceback (most recent call last):
File "C:\Users\willy\Desktop\test.py", line 39, in <module>
thumbnail2 = asyncio.run(get_thumbnail())
File "C:\Python38\lib\asyncio\runners.py", line 44, in run
return loop.run_until_complete(main)
File "C:\Python38\lib\asyncio\base_events.py", line 616, in run_until_complete
return future.result()
File "C:\Users\willy\Desktop\test.py", line 31, in get_thumbnail
img = Image.open(binary)
File "C:\Python38\lib\site-packages\PIL\Image.py", line 2930, in open
raise UnidentifiedImageError(
PIL.UnidentifiedImageError: cannot identify image file <_io.BytesIO object at 0x00000278D2FB3B30>
Solution
Simply await the result of the readable_stream.read_async(...) call:
...
readable_stream = await thumb_stream_ref.open_read_async()
await readable_stream.read_async(thumb_read_buffer, thumb_read_buffer.capacity, InputStreamOptions.READ_AHEAD)
buffer_reader = DataReader.from_buffer(thumb_read_buffer)
...
The thumbnail should now be successfully displayed each time.
Debugging process
(for anyone interested and for similar bugs in the future)
After break-pointing your code, it appeared that, on the second call of get_thumbnail(), byte_buffer was left empty. This indicated that the thumb_read_buffer was not being populated correctly from the stream.
Interestingly, when single-stepping the code instead, the image displayed both times. This suggested to me that maybe an asynchronous function call wasn't being awaited.
Turns out .read_async() (as the function name suggests) is an asynchronous operation in winrt (see IInputStream.ReadAsync on learn.microsoft.com). Hence, awaiting its execution fixed the problem of the empty thumb_read_buffer.
Hi i have a simple bot that takes a command:
#bot.command(name='repeat', help='help me to understand bots')
async def test(ctx, *lines):
print("repeating")
coloured_lines=[]
for line in lines:
coloured_lines.append("```css \n"+ line + "```")
await asyncio.gather(*[ctx.send(line) for line in coloured_lines])
You write to it a command like $repeat "green text is cool", and you should get an output like:
instead you get . My intuition is that this is because something is happening with the ` character - although i am not sure what, or how to fix it.
coloured_lines.append("```css \n"+ line + "```") In markdown space's count and css is not a valid file format and css is. coloured_lines.append("```css\n"+ line + "```")
What you expect and what you got
What you did wrong
Some background - I am creating a bot for Discord. This command, claim, will be used to claim a "job", or essentially tie their discord username to the job. The command will use the line number as the identifier for the job.
The problem I am having is that I can only access the first line in the job.txt file using this 'for' function. When I try to access the other lines, I get the 'This job does not exist!' error.
#bot.command()
async def claim(ctx, *, message=None):
with open('job.txt', 'r+') as f:
for num, line in enumerate(f, 1):
if num == int(message):
print(line)
break
else:
print('This job does not exist!')
break
When I run the code like this...
#bot.command()
async def claim(ctx, *, message=None):
with open('job.txt', 'r+') as f:
for num, line in enumerate(f, 1):
if num == int(message):
print(line)
break
I can access each line which doesn't really make sense to me. Unfortunately, I need to be able to prevent someone from claiming a job that doesn't exist.
In your first example you have a break on the if and on the else. As a result, the loop always terminates after reading the first line from the file. I suspect you want to go through all lines to find the matching message.
I would suggest keeping track of whether you found the job and handling that after the loop:
#bot.command()
async def claim(ctx, *, message=None):
found_job = False
with open('job.txt', 'r+') as f:
for num, line in enumerate(f, 1):
if num == int(message):
print(line)
found_job = True
break
if found_job:
# do your work here
else:
print("Uh-oh...didn't find the job")
I am trying to write a raffle command for my bot using discord.py and want it so the user can do the following command to start a raffle:
!raffle time winners title EG: !raffle 60 1 Pie
The issue I am having is creating validation to check that the first two inputs are numbers and that the title isn't blank. Currently this is the code I have for the command:
#bot.command(pass_context=True)
async def raffle(ctx, time, winners, title):
if time != int or winners != int or title != "":
await bot.say("{} raffle has been started for {} seconds and there will be {} winner(s)!".format(title, time, winners))
else:
await bot.say("Seems you went wrong! Raffle format is: !raffle time winners title")
return
However I am having no luck and am getting the following error:
Ignoring exception in command raffle
Traceback (most recent call last):
File "C:\Users\kairj\AppData\Local\Programs\Python\Python36-32\lib\site-packages\discord\ext\commands\bot.py", line 846, in process_commands
yield from command.invoke(ctx)
File "C:\Users\kairj\AppData\Local\Programs\Python\Python36-32\lib\site-packages\discord\ext\commands\core.py", line 367, in invoke
yield from self.prepare(ctx)
File "C:\Users\kairj\AppData\Local\Programs\Python\Python36-32\lib\site-packages\discord\ext\commands\core.py", line 345, in prepare
yield from self._parse_arguments(ctx)
File "C:\Users\kairj\AppData\Local\Programs\Python\Python36-32\lib\site-packages\discord\ext\commands\core.py", line 304, in _parse_arguments
transformed = yield from self.transform(ctx, param)
File "C:\Users\kairj\AppData\Local\Programs\Python\Python36-32\lib\site-packages\discord\ext\commands\core.py", line 212, in transform
raise MissingRequiredArgument('{0.name} is a required argument that is missing.'.format(param))
discord.ext.commands.errors.MissingRequiredArgument: time is a required argument that is missing.
Any help would be great as I am sure its a simple mistake somewhere!
Thanks in advance
What you are defining is actually two checks. The first is that you want to ensure there are 3 arguments to your command, and the second is to ensure that the first two are ints.
The first one is actually handled by ext.commands internally. To catch it, you will need to define an on_command_error event method.
#bot.event
def on_command_error(exc, ctx):
if isinstance(exc, commands.errors.MissingRequiredArgument):
# Send a message here
return
# If nothing is caught, reraise the error so that it goes to console.
raise exc
The second is checking the ints, which as #Luke McPuke said is simply
if not isinstance(time, int)
Try using isinstance instead:
if not isinstance(time, int)