Use variables of imported files - python

So I have this file with the following code
async def check_server_restriction(ctx):
restriction = await get_restriction()
global check
if restriction[str(ctx.channel.id)]["Server"] == 1:
await ctx.send("This command is restricted. You can use it in the #bot-commands channel")
check = True
And I have another file that looks like this:
from channel_restrictions_functions import test_187, new_restriction, get_restriction, check_server_restriction, check
class whois(commands.Cog):
def __init__(self, client):
self.client = client
#commands.command()
async def whois(self, ctx, member:discord.Member = None):
await test_187()
await new_restriction(ctx)
await check_server_restriction(ctx)
#print(check)
if check == True:
print("Nicht perint")
return
So basically I import the function and try to import the variable too. The problem is now when the imported function gets activated and turns check (the variable) True (in theory) nothing happens. It should work like this when the thing is in the list it should send "This command is restricted" and set check true so that in my command the if statement works and it will return.
I dont get whats the mistake. I think it says check = False every time but it should be true
I hope you can understand my problem and you know how to fix it
thx for the help

Even global variables are not truly global, they are global only within the scope of their own module. There are ways to get around this restriction and share a variable between modules, if you absolutely have to, see e.g. here. It is recommended to put all global variables into a separate module and import from there, however, see the Python docs for more on this.
That said, why would you want to do this with a global variable? It's simpler and less error prone (in the sense that the restriction status would be shared between commands if global, which might lead to commands being falsely restricted) to just have check_server_restriction return True or False and then check the return value of the function something like this:
async def check_server_restriction(ctx):
restriction = await get_restriction()
if restriction[str(ctx.channel.id)]["Server"] == 1:
await ctx.send("This command is restricted. You can use it in the #bot-commands channel")
return True
return False
In your code, you'd then use this modified function like so:
if await check_server_restriction(ctx): # the == True can be omitted
print("Nicht perint")
return

Related

Discord.Py Assigning values to variables

#commands.command()
async def reroll(self, ctx, channel: discord.TextChannel, id_ : int):
try:
new_gaw_msg = await ctx.channel.fetch_message(id_)
except:
await ctx.send("Failed to parse command. ID was incorrect.")
return
winners = int(winners.replace("W",""))
users_mention = []
for i in range(winners):
users = await new_gaw_msg.reactions[0].users().flatten()
users.pop(users.index(self.client.user))
winner = random.choice(users)
users_mention.append(winner.mention)
users.remove(winner)
displayed_winners = ",".join(users_mention)
await ctx.send(f"Congragulations {displayed_winners}! You have won the **{prize}**.\n{gaw_msg.jump_url}")
variable winners is referenced before assignment. I gave value to winners before using it so idk why it doesnt work. Any help is appreciated :D
You are trying to perform an operation on the winners variable before it was created.
There is a thing called name "namespace". Basically, variables created globally (without indent) and variables created locally (inside functions or loops) are different variables.
If you want to specifically use a global variable inside the function, you should first declare that the variable will be global by writing global variable_name inside the function. And only then you can perform operations with it.
There are some hidden dangers with the usage of global variables, so I suggest you read more about python namespaces and global keyword by yourself.

How to stop on_message() in discord.py by using another function?

I want to stop a function using another function.
def on_message(message):
#DO WHAT I WANT
terminate_function(#if condition met)
def terminate_function():
#terminate_function code
What should I put for #terminate_function code ?
To exit out of a function you can use the return keyword.
def on_message(message):
# do what you want
if "condition met":
return
print("this is not printed if the condition is met")
You can't make a function return from another function. What you could do to solve this is make the terminate function return a bool & check that.
def on_message(message):
#DO WHAT I WANT
if should_terminate():
return
def should_terminate() -> bool:
#DO SOMETHING
return condition_met # True if on_message should terminate, False if not
Alternatively, if you really want the other function to break the first one, an ugly solution is to throw an error.
def on_message(message):
#DO WHAT I WANT
terminate_function()
def terminate_function():
# Raising an exception here will make on_message stop as well
raise RuntimeError()

How can I prevent getting an AttributeError after reloading a cog?

A more unusual question today, but maybe someone can help me.
I am working on a bot, which among other things has to do with music. The following problem:
If I change stuff in a cog and then reload it I always get an AttributeError: 'NoneType' object has no attribute 'XXXX' error for the particular command. Is there a way to fix/prevent this?
The error occurs when the bot is in a voice channel for example and then I reload the cog.
I query state for every command which has to do with music, is it maybe related to that?
state = self.get_state(ctx.guild)
Full function for get_state:
def get_state(self, guild):
"""Gets the state for `guild`, creating it if it does not exist."""
if guild.id in self.states:
return self.states[guild.id]
else:
self.states[guild.id] = GuildState()
return self.states[guild.id]
I tried to solve it with a try/except AttributeError, but of course that didn`t really work/the console still gave me the output.
Here is an example code:
#commands.command()
#commands.guild_only()
#commands.check(audio_playing)
#commands.check(in_voice_channel)
#commands.check(is_audio_requester)
async def loop(self, ctx):
"""Activates/Deactivates the loop for a song."""
state = self.get_state(ctx.guild)
status = state.now_playing.toggle_loop()
if status is None:
return await ctx.send(
embed=discord.Embed(title=":no_entry: Unable to toggle loop.", color=discord.Color.red()))
else:
return await ctx.send(embed=discord.Embed(
title=f":repeat: Loop: {'**Enabled**' if state.now_playing.loop else '**Disabled**'}.",
color=self.bot.get_embed_color(ctx.guild)))
And if I make changes in the cog, reload it and then try to run loop again I get the following error:
In loop:
File "C:\Users\Dominik\PycharmProjects\AlchiReWrite\venv\lib\site-packages\discord\ext\commands\core.py", line 85, in wrapped
ret = await coro(*args, **kwargs)
File "C:\Users\Dominik\PycharmProjects\AlchiReWrite\cogs\music.py", line 220, in loop
status = state.now_playing.toggle_loop()
AttributeError: 'NoneType' object has no attribute 'toggle_loop'
(Same error for all the other commands)
Requested, we have the GuildState class:
class GuildState:
"""Helper class managing per-guild state."""
def __init__(self):
self.volume = 1.0
self.playlist = []
self.message_queue = []
self.skip_votes = set()
self.now_playing = None
self.control_message = None
self.loop = False
self.skipped = False
def is_requester(self, user):
return self.now_playing.requested_by == user
How would I overcome this error?
The bot joins on the command play URL and then I built in the following:
if not state.now_playing:
self._play_song(client, state, video)
_play_song is mainly defined the following:
def _play_song(self, client, state, song):
state.now_playing = song
# Other things are not relevant
When you reload the cog, the states dictionary in your cog will be empty. With state = self.get_state(ctx.guild), a new GuildState object is created. From the __init__ function of the GuildState class, self.now_playing is set to None.
Because of this, status = state.now_playing.toggle_loop() will throw an AttributeError as None has no attributes (in this case, no toggle_loop attribute).
If you want to get rid of these errors, you will need to set self.now_playing correctly to something that does have the needed attributes.
If you want to keep the states dictionary as is, you can save it before reloading your cog and restore it. The below example assumes that the cog class is named TestCog.
#client.command()
async def reload(ctx):
temp = client.get_cog('TestCog').states
client.reload_extension('cog')
client.get_cog('TestCog').states = temp
Note that this may break your cog if you change how GuildState is created, as you are restoring the previous version of states.

How to check if a function ran without generating any errors

I want to check to see if a message() function ran correctly and managed to send the message, the user could be out of reach for a certain time so I want to script to skip over his name it didn't manage to message him, and to mark the name of people it send a message to
I tried to set a bool value
That changes after the function runs but it doesn't seem to work, i'm using a modular to send the message I recive error on the modular not the script itself
user_was_messaged = False
def message_user()
user_was_messaged = True
If your user_was_messaged variable is global, then you need to use the global Python statement to change the value from within your function:
user_was_messaged = False
def message_user():
global user_was_messaged
user_was_messaged = True
However, using a global variable for this purpose would be highly discouraged. Instead, use the return value of the function:
def message_user():
# do things to try to send a message
if cannot send:
return False
# maybe do more things
return True
if message_user():
print("successful")
else:
print("failed")
As you have the variable user_was_messaged before the function, you must declare the variable as global in the function as in like:
user_was_messaged = False
def message_user()
global user_was_messaged
user_was_messaged = True

Python Discord Bot #commands.has_any_role issue

So I want admins to have the ability to add roles to the list 'admin_roles'. My issue is when calling #commands.has_any_role(*admin_roles), it does not seem to look at anything that has been appended to the list.
Code -
admin_roles = []
#client.command()
#commands.has_any_role('Owner')
async def addadminrole(ctx, role):
global admin_roles
admin_roles.append(role)
#client.command()
#commands.has_any_role(*admin_roles)
async def setinfo(ctx, item, description):
global desc
if(item == 'desc'):
desc = description
So when the code is run, I get the error discord.ext.commands.errors.MissingAnyRole: You are missing at least one of the required roles:
No matter how many things get appended to the list, the has_any_role doesn't check for them.
A decorator is invoked once as soon as the module is imported, and then the function that gets called is whatever was provided by the decorator definition. The problem that you are facing is that you modify your global variable in the middle of your runtime, but this does not cause the decorator to be re-applied, and thus it uses the values from when it was invoked at import. Because admin_roles is an empty list at the time of module import, it will be passed as an empty list into commands.has_any_role. To achieve a similar level of functionality (though not the best), you can do something like:
from discord.ext.commands import MissingAnyRole, NoPrivateMessage
from discord.channel import DMChannel
#client.command()
async def setinfo(ctx, item, description):
global admin_roles
if isinstance(ctx.channel, DMChannel): #if this is a private message, roles do not apply.
raise NoPrivateMessage
if not len([role for role in ctx.author.roles if role in admin_roles]): # if the invoking Member has no roles in admin_roles, throw the same error as check_any_role
raise MissingAnyRole
global desc
if(item == 'desc'):
desc = description
I found another way to solve this issue. I understand what was wrong with my initial code since admin_roles is never called before it is accessed. This is the code I used to circumvent my issue -
#client.command()
async def setinfo(ctx, item, description):
global admin_roles
global desc
x = 0
roles = [0]*len(admin_roles)
while x < len(admin_roles):
roles[x] = discord.utils.get(ctx.guild.roles, name=admin_roles[x])
if roles[x] in ctx.author.roles:
if(item == 'desc'):
desc = description
x += 1

Categories