Toggling command access - Discord.py - python

im pretty new to python so i thought a good learning project would be to create a discord bot for a personal server, i have a few commands that only i as the owner of the bot can access but i would like to be able to toggle that access with a command so that my friends can also use them but i have a problem, this is the piece of the code giving me an error:
MyID = '<#xxxxxxxxxxxxxxxx>'
FFA = False
class TestCommands(commands.Cog):
def __init__(self, client):
self.client = client
#commands.Cog.listener()
async def on_ready(self):
def IDCheck(ctx):
return ctx.message.author.id == xxxxxxxxxxxxxxxxxxxxxx
#commands.command()
async def ToggleFFA(ctx):
if FFA == False:
FFA = True
print (FFA)
await message.channel.send('All user can now use owner locked commands')
if FFA == True:
FFA = False
print (FFA)
await message.channel.send('All user can no longer use owner locked commands')
###########################################################################
#commands.command()
if FFA == False:
#commands.check(IDCheck)
async def FFATest(self, ctx, *, question):
loopnumber = 0
while spamnumber < int(question):
await ctx.send('Test' + MyID);
await asyncio.sleep(1)
loopnumber += 1
print ({loopnumber})
if FFA == True:
async def LoopTest(self, ctx, *, question):
loopnumber = 0
while loopnumber < int(question):
await ctx.send('Test' + MyID);
await asyncio.sleep(1)
loopnumber+= 1
print ({loopnumber})
###########################################################################
i get an invalid syntax error within the highlighted piece of code. If anyone knows a simpler way of toggling the access or a way that i can correct the error i would really appreciate it.
thanks in advance.

You can specify a bot_check_once method that will be used as a check on all commands from the bot. We can then have an attribute of the cog that controls which mode we are in:
class MyCog(commands.Cog):
def __init__(self, bot):
self.bot = bot
self.owner_only = True
async def bot_check_once(self, ctx):
app = await self.bot.application_info()
if self.owner_only:
return ctx.author == app.owner
else:
return True
#commands.command()
async def all_users(self, ctx):
self.owner_only = False
#commands.command()
async def just_owner(self, ctx):
self.owner_only = True

Well you could add a check method outside. Here is an example.
def FFA_Enabled(ctx):
global FFA
if commands.is_owner():
return True
else:
return FFA
#commands.check(FFA_enabled):
async def SomeCommand(self, ctx:Context):
ctx.send("Message")
This Should work
if you don't know what ctx:Context means
It derives ctx from the type Context(i use it for autofill) if you wanna you is i suggest you type this:
from discord.ext.commands import Context

You can use a LIST for this, Inside that you can store the USER ID and Status ID.
Note: This is a just snippet to give you an idea, The ID Will reset when the script is restarted, I recommend you to save it in a file and load it from there.
You can also use a function to return True/False based on the USER ID instead of writing a bunch of code in each command.
users = []
status = 'Global'
#commands.is_owner()
#commands.command()
async def add_user(self,ctx,user:discord.User):
global users
id = user.id
await ctx.send(f'{user.name} has been added into the mod list.')
return users.append(id)
#commands.is_owner()
#commands.command()
async def change_status(self,ctx):
global status
if status == 'Global':
status = 'Local'
elif status == 'Local':
status = 'Global'
await ctx.send(f'Status has been changed to {status}')
return status
#commands.command()
async def test_command(self,ctx):
global status
global users
#IF Status is Local
if status == 'Local':
if ctx.user.id in users:
#ALLOW THE USERS' WHOS' ID IS IN THE LIST
pass
else:
#EXIT THE FUNCTION IF USER NOT IN THE LIST
return
#IF The status is Global
else:
#ALLOW THE COMMAND IF IT'S GLOBAL
pass

Related

Command does not work during discord bot creation

I typed !mycommand2 [name] [role] because it didn't come out even when I typed command !캐릭터생성 [name] [role], but it's still the same. Why? And description's role(Is it like an annotation? Explain to the developer what command this is without a role?) and...I also wonder about command hidden.
char = I want to mack a instance.....char.py has class char.
import discord, asyncio
import char
from discord.ext import commands
intents=discord.Intents.all()
client = discord.Client(intents=intents)
bot = commands.Bot(command_prefix='!',intents=intents,help_command=None)
#client.event
async def on_ready():
await client.change_presence(status=discord.Status.online, activity=discord.Game("언성듀엣!"))
#bot.command(name="테스트", description="테스트용 함수", hidden=False) #set hidden to True to hide it in the help
async def mycommand1(ctx, argument1, argument2):
await ctx.channel.send ("{} | {}, Hello".format(ctx.author, ctx.author.mention))
await ctx.author.send ("{} | {}, User, Hello".format(ctx.author, ctx.author.mention))
char_num = 1
#bot.command(name="캐릭터생성", description="테스트용 함수", hidden=False) #set hidden to True to hide it in the help
async def mycommand2(ctx, context1, context2):
global char_num
globals()['char_{}'.format(char_num)]=char(name=context1,Sffter=context2,username=ctx.author.name)
char_num+=1
await ctx.message.channel.send ("done", context1,"!")
client.run('-')
Change the function name to the command name
async def urcommandname(ctx,arg1,arg2):

discord.py - edit the interaction message after a timeout in discord.ui.Select

How can I access the interaction message and edit it?
discord.ui.Select
class SearchMenu(discord.ui.Select):
def __init__(self, ctx, bot, data):
self.ctx = ctx
self.bot = bot
self.data = data
self.player = Player
values = []
for index, track in enumerate(self.data[:9]):
values.append(
discord.SelectOption(
label=track.title,
value=index + 1,
description=track.author,
emoji=f"{index + 1}\U0000fe0f\U000020e3"
)
)
values.append(discord.SelectOption(label='Cancel', description='Exit the search menu.', emoji="🔒"))
super().__init__(placeholder='Click on the Dropdown.', min_values=1, max_values=1, options=values)
async def callback(self, interaction: discord.Interaction):
if self.values[0] == "Cancel":
embed = Embed(emoji=self.ctx.emoji.whitecheck, description="This interaction has been deleted.")
return await interaction.message.edit(embed=embed, view=None)
discord.ui.View
class SearchMenuView(discord.ui.View):
def __init__(self, options, ctx, bot):
super().__init__(timeout=60.0)
self.ctx = ctx
self.add_item(SearchMenu(ctx, bot, options))
async def interaction_check(self, interaction: discord.Interaction):
if interaction.user != self.ctx.author:
embed = Embed(description=f"Sorry, but this interaction can only be used by {self.ctx.author.name}.")
await interaction.response.send_message(embed=embed, ephemeral=True)
return False
else:
return True
async def on_timeout(self):
embed = Embed(emoji=self.ctx.emoji.whitecross, description="Interaction has timed out. Please try again.")
await self.message.edit(embed=embed, view=None)
If I try to edit the interaction like this I am getting
-> AttributeError: 'SearchMenuView' object has no attribute 'message'
After 60 seconds the original message should be replaced with the embed in the timeout.
You're trying to ask the View to send a message, which is not a method in discord.ui.View.
You could defer the response and don't let it timeout and allow the user to try again?
async def interaction_check(self, interaction: discord.Interaction):
if interaction.user != self.ctx.author:
embed = Embed(description=f"Sorry, but this interaction can only be used by {self.ctx.author.name}.")
await interaction.channel.send(embed=embed, delete_after=60)
await interaction.response.defer()
return True
view = MyView()
view.message = await channel.send('...', view=view)
After that you can use self.message in on_timeout (or somewhere else you don't have access to interaction.message) to edit it.
Source: https://discord.com/channels/336642139381301249/669155775700271126/860883838657495040

discord.py Passing context and self to function in class

I am trying to create a bot, which uses discord.py's VoiceClient.
The problem is, when you name a variable in a function it doesn't get passed on to the other functions. So the variable voicebot doesn't get passed on to the other functions (which are for making the bot leave or talk)
intents = discord.Intents.all()
bot = commands.Bot(command_prefix=commands.when_mentioned_or("~"),intents=intents, owner_id=587573816360960022)
...Other functions...
#bot.command(name='join', help='Joins the voice channel you are in')
async def JoinVoiceofAuthor(ctx):
vchannel = ctx.message.author.voice
if vchannel != None:
if "voicebot" not in globals():
global voicebot
voicebot = await vchannel.channel.connect()
else:
if voicebot.is_connected():
return
if vchannel != None:
voicebot = await vchannel.channel.connect()
await ctx.send('Joining you, '+str(ctx.message.author.mention)+', in voice channel "'+str(vchannel.channel)+'"')
else:
await ctx.send('Sorry,'+str(ctx.message.author.mention)+', you are not in a voice channel')
...Other functions...
I tried making voicebot a global variable, but found out, that you can't change global varibales from a function.
So my next idea was to make the bot a class:
class TheBot(commands.Bot):
def __init__(self):
intents = discord.Intents.all()
super().__init__(intents=intents, command_prefix='~', owner_id=587573816360960022)
members = inspect.getmembers(self)
for name, member in members:
if isinstance(member, commands.Command):
if member.parent is None:
self.add_command(member)
...Other functions...
#commands.command(name='join', help='Joins the voice channel you are in')
async def JoinVoiceofAuthor(self, ctx):
vchannel = ctx.message.author.voice
if vchannel != None:
if "voicebot" not in globals():
self.voicebot = await vchannel.channel.connect()
else:
if self.voicebot.is_connected():
return
if vchannel != None:
self.voicebot = await vchannel.channel.connect()
await ctx.send('Joining you, '+str(ctx.message.author.mention)+', in voice channel "'+str(vchannel.channel)+'"')
else:
await ctx.send('Sorry,'+str(ctx.message.author.mention)+', you are not in a voice channel')
...Other functions...
But that also didn't work, because the Context was also passed on to self and not to ctx
Does someone have a solution for this?
As Parasol Kirby said, you need to use the global keyword. To adapt this to your case we need to make some changes.
To make the voicebot variable global just add global voicebot at the top of the command. That way you can use this variable from every function.
Then to check if the bot is already connected to a voice channel, we do not need to use the voicebot variable. You can create a function that returns True if the bot is in a voice channel like this:
def is_connected(ctx):
voice_client = discord.utils.get(ctx.bot.voice_clients, guild=ctx.guild)
return voice_client and voice_client.is_connected()
Now to check if the bot is not connected to a voice channel from within the command, just use the is_connected function and "pass in" the context.
if not is_connected(ctx):
So the full code looks like this:
def is_connected(ctx):
voice_client = discord.utils.get(ctx.bot.voice_clients, guild=ctx.guild)
return voice_client and voice_client.is_connected()
#bot.command(name='join', help='Joins the voice channel you are in')
async def JoinVoiceofAuthor(ctx):
global voicebot
vchannel = ctx.message.author.voice
if vchannel != None:
if not is_connected(ctx):
voicebot = await vchannel.channel.connect()
await ctx.send('Joining you, '+str(ctx.message.author.mention)+', in voice channel "'+str(vchannel.channel)+'"')
If you want to change a global variable from a function, you must use the global keyword from inside the function:
x = 50
def add_to_x(num):
global x
x += num
print(x)
add_to_x(10)
print(x)
#The Output is this:
50
60
And an implementation in discord.py would be:
x = 50
#bot.command()
async def add_to_x(ctx,amount):
global x
print(x)
x += int(amount)
print(x)

Discord.py constantly edit message

Ok so I've created a bot with python and have set it so its always up and everything, but I want to make a message that it constantly sets to the current user count in the server.
I have no idea as to how I would do this, and appreciate any help I could get. I have got the message id, the channel id, and the guild id, I just need to know how to edit messages and how to do it every 10 seconds or so.
Thanks for the help!
from discord.ext import commands
from typing import Union
import asyncio
import discord
_CH = Union[discord.TextChannel, discord.VoiceChannel, discord.CategoryChannel]
_C = commands.Context
class StatusCog(commands.Cog):
INTERVAL = 60
TEMPLATE = 'Online: {}, All: {}'
def __init__(self, bot: commands.Bot):
self.bot = bot
self.running = False
self.STATUS_DICT = {'online': 0, 'offline': 0, 'idle': 0, 'dnd': 0}
#commands.group(invoke_without_command=True)
async def count(self, ctx: _C):
pass
#count.command()
async def start(self, ctx: _C, channel: _CH):
self.running = True
while self.running:
status = self.STATUS_DICT.copy()
for member in ctx.guild.members:
s = member.status
for key in status.keys():
if getattr(s, key, False):
status[key] += 1
break
await channel.edit(name=self.TEMPLATE[:].format(list(status.values())))
await asyncio.sleep(self.INTERVAL)
#count.command()
async def stop(self, ctx: _C):
self.running = False
def setup(bot: commands.Bot):
bot.add_cog(Cog(bot))

Missing 1 required positional argument: 'number'

Hi I'm having an issue running a asyncio loop it's asking for a missing 1 required positional argument: 'number'.
Here is what I'm working with:
async def purge_modlog(ctx, number):
tomorrow = datetime.now()+timedelta(days=1)
midnight = datetime(year=tomorrow.year, month=tomorrow.month,
day=tomorrow.day, hour=20, minute=35, second=0)
number = int(number)
server = before.server
db = fileIO(self.direct, "load")
if not server.id in db:
return
channel = db[server.id]["Channel"]
if number > 99 or number < 1:
await ctx.send("I can only delete messages within a range of 1 - 99", delete_after=10)
else:
author = ctx.message.author
authorID = author.id
mgs = []
number = int(number)
channel = modlog
async for x in bot.logs_from((channel), limit = int(number+1)):
mgs.append(x)
await asyncio.sleep((midnight - datetime.now()).seconds)
print("Deleting modlog messages 14 day or older")
await asyncio.sleep(5)
await delete_messages(mgs)
await ctx.send('Success!', delete_after=4)
await asyncio.sleep(86400) # Wait 24 hours
def check_folder():
if not os.path.exists('data/modlogset'):
print('Creating data/modlogset folder...')
os.makedirs('data/modlogset')
def check_file():
f = 'data/modlogset/settings.json'
if not fileIO(f, 'check'):
print('Creating default settings.json...')
fileIO(f, 'save', {})
def setup(bot):
check_folder()
check_file()
q = ModLog(bot)
loop = asyncio.get_event_loop()
loop.create_task(q.purge_modlog())
bot.add_cog(q)
To loop the event under def def setup(bot): You can see
loop = asyncio.get_event_loop()
loop.create_task(q.purge_modlog())
This is supposed to loop the event (q.purge_modlog()) I'm not sure what I'm doing wrong here. I have already tried the follow (q.purge_modlog(ctx, number))
line 686, in setup
loop.create_task(q.purge_modlog(ctx, number))
NameError: name 'ctx' is not defined
If anyone could help me that would be great. To add this is a module.
I have corrected some mistakes here. Try to read through the discord.py documentation on purging messages.
class ModLog:
def __init__(self, bot):
self.bot = bot
async def on_ready(self):
await self.bot.wait_until_ready()
for server in self.bot.servers:
channel = self.bot.get_channel("channel ID here")
if channel:
self.bot.loop.create_task(self.modlog_purge(channel))
async def modlog_purge(self, channel):
while True:
now = datetime.utcnow()
two_weeks_ago = now - timedelta(days=14)
await self.bot.purge_from(channel, before=two_weeks_ago)
await asyncio.sleep(86400)
def setup(bot):
q = ModLog(bot)
bot.add_cog(q)
class ModLog:
def __init__(self, bot):
self.bot = bot
async def on_ready(self):
await self.bot.wait_until_ready()
for guild in self.bot.guilds:
channel = await self.get_channel(guild)
if channel:
self.bot.loop.create_task(self.modlog_purge(channel))
async def get_channel(guild):
# Whatever your logic for getting the Channel for a given guild is
async def modlog_purge(self, channel):
while True:
now = datetime.utcnow()
two_weeks_ago = now - timedelta(days=14)
await channel.purge(before=two_weeks_ago)
await asyncio.sleep(86400)
def setup(bot):
q = ModLog(bot)
bot.add_cog(q)
Here's how I would structure this (I'm not on a computer with discord.py at the moment, so there may be some errors). We have an on_ready event that kicks off background tasks for each server that has a channel we want to maintain (this could instead loop through a list of channels, or something similar).
The actual purge is all taken care of by the TextChannel.purge coroutine. We just pass it a datetime object, and it deletes 100 messages from before that date (this is configurable). It then sleeps for a day and repeats.

Categories

Resources