How can I get a self variable from another class? - python

My problem is that I need to get the self.tracker variable from the gift_bot.py file inside the cogs/invite_moduly.py file. But sadly, I don't know a way to do that. I tried a few methods like self.bot.tracker, tracker and more, but none worked.
How can I access this variable in invite_module.py?
File gift_bot.py
class GiftBot(commands.Bot):
def __init__(self):
self.tracker = InviteTracker(self)
super().__init__(command_prefix="*", intents=intents, case_insensitive=True)
async def on_ready(self):
try:
await self.tracker.cache_invites()
except:
pass
cogs/invite_module.py:
class InviteModule(commands.Cog):
def __init__(self, bot: commands.Bot):
self.bot = bot
self.last_member: list = []
#commands.Cog.listener("on_member_join")
async def on_member_join(self, member: discord.Member):
invite_info = await get_inviter(self.tracker, member)
With this code, I get:
Unresolved attribute reference 'tracker' for class 'InviteModule'

Assuming that you instantiate InviteModule with bot being a reference to an instance of GiftBot, it would be self.bot.tracker.
self is used to reference the instance of the class where's that method (by convention, it's not a keyword, but it's discouraged to use something other than self). So, inside InviteModule, self is an object of the class InviteModule, and inside GiftBot, it's an object the class GiftBot.
If you want to reference a property of GiftBot inside InviteModule, you must pass an instance of GiftBot to InviteModule¹, which I assume you're doing as the bot property. So, to access it inside InviteModule use self.bot, and hence, the tracker would be self.bot.tracker.
¹ Or, instead of passing the whole bot, you could pass the property itself. For instance, you could do:
class InviteModule(commands.Cog):
def __init__(self, tracker):
self.tracker = tracker
# rest of the code here
And use as:
giftBot = GiftBot()
inviteModule = InviteModule(giftBot.tracker)
I assume you want the first option, but I'm adding this for completeness.
I can be wrong, but I believe the error mentioned in the comments is actually from the IDE.
Per your comment, I suppose class Bot does not have the field tracker. So, you could either:
Change InviteModule init to accept GiftBot
Before trying to access the field tracker, check if the element is an instance of GiftBot.
If there's nothing usable in InviteModule without the field tracker, the first option is probably better:
class InviteModule(commands.Cog):
def __init__(self, bot: GiftBot): # or gift_bot.GiftBot, depending on how you're importing/connecting those files
self.bot = bot
# rest of the code
The second option would go something like:
class InviteModule(commands.Cog):
def __init__(self, bot: commands.Bot):
self.bot = bot
# rest of init
#commands.Cog.listener("on_member_join")
async def on_member_join(self, member: discord.Member):
if instanceof(self.bot, GiftBot):
invite_info = await get_inviter(self.bot.tracker, member)
# if not instance, define what's desired: ignore, log only, throw an error

Related

Is it possible to access a class attribute in a python decorator?

Is it possible to access a class attribute in a python decorator?
Code:
class ButtonView(discord.ui.View):
def __init__(self, allPages, author, *, timeout = 180):
super().__init__(timeout=timeout)
self.allPages = allPages
#discord.ui.button(label=self.allPages, style=discord.ButtonStyle.secondary, custom_id="current_page_button")
async def current_page_button_callback(self, button, interaction):
...
I get a warning on self.allPages in #discord.ui.button():
self.allPages is not defined

AttributeError: "Class" object has no attribute 'user' discord.py

When I do my discord.py project, I found out that the following codes work well in the first time but return an attribute error for the second time (in a single run).
class Test2(commands.Cog):
def __init__(self, dc_bot):
self.bot = dc_bot
self.ui = ""
#commands.command(description="get avatar")
async def getavatar(self, ctx, *, content: str):
avatar = self.bot.user.avatar_url
self.ui += content
await ctx.send(content)
await ctx.send(avatar)
self.__init__(self) # reset the values
First time it works well.
Second time it will say: AttributeError: 'Test2' has no attribute 'user'
I guess because I want to reset self.ui for the next run. And if there are a lot of "self" in my init function that I need to use, I thought (before) that it is a good idea to just called the init function. But running again self.ui = dc_bot will cause this problem I think. Could you explain why this would happen please?
There's a mistake when you re-calls the init method. self.__init__(self) is actually calling Test2. __init__(self, self) . Which overrides self.bot = self thus the attribute error when you run the command a second time. Instead you want:
self.__init__(self.bot)
But this isn't a good solution, instead you should have a "reset" helper method that does the resetting,and you call the helper method instead of init. Since normally, once the class is initiated, you don't want to call the init method again.
You are making the code way too complex try this out!
#commands.command(description = "get avatar")
async def getavatar(self, ctx, *, avamember: discord.Member = None):
userAvatarUrl = avamember.avatar_url
await ctx.send(userAvatarUrl)

retrieve variables from cog class

class Music(commands.Cog):
def __init__(self, client):
self.players = {}
async def destroy_player(self,ctx):
try:
self.players.pop(ctx.guild.id)
print(self.players)
except:
pass
def setup(client):
client.add_cog(Music(client))
this is a part of my code. how can I use self.players outside the class?
You can define a variable to store the class object before using it, like this:
def setup(client):
c = Music(client)
client.add_cog(c)
print(c.players)
Simple, use the get_cog method. Assuming you are using this in another cog
async def get_something(self, ctx): #this would be a cog method (command or any function inside a cog class)
Music = self.bot.get_cog('Music')
print(Music.players)
or if you are just using bot
async def get_something(ctx):
Music = bot.get_cog('Music') #or ctx.bot.get_cog('Music')
this will work if you have added the cog Music with add_cog(Music(bot)
References:
get_cog
Context
Examples:
You can see the discord.py's docs example here
You can either create the instance of a class (create instance outside of a function to use it outside of a function or in a function with return statement) which is a much simpler approach
Or if you want to avoid creating the instance of a class do sth like this:
class Music:
players = {}
#classmethod
def get_players(self):
return self.players
Music.get_players()
This will also return the attribute without creating the class instance, but the attribute can be within init statement.
If you want to learn more read about getter and setter in Python

Python Split Class to multiple files when access parent via decorator

I have this Discord.py Cog object that contains a bunch of methods that I would like to split into multiple files to browse easier. (most stuff cut out for brevity)
class VoteManager(commands.Cog):
def __init__(self, bot):
self.bot = bot
#commands.group(pass_context=True, invoke_without_command=True)
async def vote(self, ctx):
await ctx.send(f'Command not found. Use `{BOT_CALL_PREFIX}vote help` for commands.')
def new(self, channel_id):
pass # Actual logic for new_command, separated so it could be called without context
#vote.command(name='new')
async def new_command(self):
pass # Really long function, etc.
What I want is to be able to split definitions like new and new_command into their own file new.py.
The VoteManager class is in the __init__.py file of discordbot.voting module. So when used, I use bot.add_cog(discordbot.voting.VoteManager(bot)) to add it to the registry of commands for the bot.
The goal is to be able to just do something like from .voting.new import * in the VoteManager class and have it read the functions to be added.

Importing function from another cog with discord.py

I would like to import some functions from another cog so they can be used for several cogs in different .py files. How would I go about doing that? Here's what the documentation says:
class Economy(commands.Cog):
...
async def withdraw_money(self, member, money):
# implementation here
...
async def deposit_money(self, member, money):
# implementation here
...
class Gambling(commands.Cog):
def __init__(self, bot):
self.bot = bot
def coinflip(self):
return random.randint(0, 1)
#commands.command()
async def gamble(self, ctx, money: int):
"""Gambles some money."""
economy = self.bot.get_cog('Economy')
if economy is not None:
await economy.withdraw_money(ctx.author, money)
if self.coinflip() == 1:
await economy.deposit_money(ctx.author, money * 1.5)
as an example, but that means I have to define economy every time if I would like to call on it. Is there a more efficient method for calling a function in another cog?
If withdraw_money and deposit_money don't use any of Economy's attributes or methods, you could make them static methods and import Economy to use them or just make them functions outside the class/cog and import them directly.
Otherwise, you can look for a way to refactor those methods so that they don't rely on Economy so that you can make them static methods or independent functions.
If that's not possible, this is already the best way to do it.
Bot.get_cog is O(1) anyway, so there's very minimal impact efficiency-wise.

Categories