How to make Json database distinct for each server? - discord.py

So i made a code for warns system, in server it works without issue but the warns are being transferred over to each of the server the bot is in, Plz advise how to make warns distinct for each server
#commands.has_permissions(manage_roles=True, ban_members=True)
async def warn(ctx,user:discord.User,*reason:str):
if reason==None:
reason="Defying a Supreme Power"
else:
reason = ' '.join(reason)
for current_user in Warnlog['user']:
if current_user['name'] == user.name:
current_user['reasons'].append(reason)
break
else:
Warnlog['user'].append({
'name':user.name,
'reasons': [reasons,]
})
with open('Warnlog.json','w+') as f:
json.dump(Warnlog,f)
a=discord.Embed(title="Warned",color=0x800000)
a.add_field(name=f"{user.name} has been warned",value=f" **Reason:-** {reason}")
await ctx.channel.send(embed=a)
#Client.command(manage_channels = True)
async def infractions(ctx,user:discord.User):
for current_user in Warnlog['user']:
if user.name == current_user['name']:
Op=discord.Embed(title=f"**Warn list for {user.name}**",inline=True)
Op.add_field(name="No. Of Warns Recived", value=f"{len(current_user['reasons'])}",inline=True)
Op.add_field(name='Warns', value=f"{','.join(current_user['reasons'])}\n",inline=False)
Op.set_thumbnail(url=f'{user.avatar_url}')
await ctx.send(embed=Op)
break
else:
Io=discord.Embed(title="**Warns**",description=f"{user.name} hasn't commited a Warnable action(YET)")
await ctx.send(embed=Io) ```

You have to change the structure of your json from Warnlog[user] to Warnlog[ctx.guild.id][user]. You can just change this in your code everywhere you are using Warnlog[user], but you have to reset the json database.

Related

'AsyncTeleBot' object has no attribute 'register_next_step_handler'

Trying to create simple ask, answer, remember telegram bot with pyTelegramBot.
Everything were normal, when TeleBot were used. TeleBot.register_next_message_handler helped me a lot.
Example:
...
#bot.message_handler(func=lambda msg: msg.text is not None and '/start' in msg.text)
def send_welcome(msg):
global cur_user
cur_user = msg.from_user.id
keyboard = types.InlineKeyboardMarkup()
keyboard.add(types.InlineKeyboardButton('Да', callback_data='old'),
types.InlineKeyboardButton('Нет', callback_data='new'))
keyboard.add(types.InlineKeyboardButton('Пора остановиться', callback_data='stop_bot'))
greet = 'Бла...бла...бла\nМы знакомы?'
bot.send_message(msg.chat.id, greet, reply_markup=keyboard)
...
#bot.callback_query_handler(func=lambda call: True)
def query_processing(call):
global user
global cur_user
if call.data == 'new':
user = dict.fromkeys(user, None)
nxt = bot.send_message(call.message.chat.id, 'Как звать?')
bot.register_next_step_handler(nxt, get_name_ask_goal)
...
Still I need my bot to be asynchronous because of sleep time till message to be send from bot and with simultaneous possibility for user to send messages.
Tried to use AsyncTeleBot, but there is no register_next_step_handler function. I did't find out how to make bot wait user for the name typing and it is almost impossible to me to add register_next_step_handler function into particular files. Although found issue in gitHub and no solution from 2017.
Tried
...
bot = AsyncTeleBot(os.getenv('token2'))
...
async def beep(chat_id) -> None:
"""Send the beep message."""
await bot.send_message(chat_id, text='Beep!')
aioschedule.clear(chat_id)
async def scheduler():
global chat_id
aioschedule.every(5).seconds.do(beep, chat_id).tag(chat_id)
while True:
await aioschedule.run_pending()
await asyncio.sleep(1)
async def main():
await asyncio.gather(scheduler(), bot.polling(non_stop=True))
if __name__ == "__main__":
asyncio.run(main(), debug=True)
...
Result:
TypeError: An asyncio.Future, a coroutine or an awaitable is required
I'm wondering:
is there other appropriate libraries for my task?
is there any simple solution to save user's message?
is it possible to use asyncio with TeleBot that made Thread-style?
Hoping for any help.

Discord.py AttributeError: 'NoneType' object has no attribute 'guild'

I recently got helped for doing a voice time tracking in python for my Discord bot and everything works fine except 1 thing.
Here's my code:
#commands.Cog.listener()
async def on_voice_state_update(self, member, before, after):
if member.bot: #checking this before anything else will reduce unneeded file operations etc
return
with open('data/voice_leaderboard.json', 'r') as file:
voice_data = json.load(file)
new_user = str(member.id)
guild_id = str(member.guild.id)
if new_user not in voice_data[guild_id]: #this adds a new user to the guild dict if they're not in it yet
voice_data[guild_id][new_user] = {
"total_time" : 0,
"join_time" : None}
userdata = voice_data[guild_id][new_user] #this is to make the next code clearer, adding things to this dict also adds them to the voice_data dict, it just make the code "cleaner"
#after making sure the user exists you gotta check if they're joining or leaving a vc(and reject all the other options), plus if they change vc within the same guild it should keep counting. There's multiple ways to do this
if(before.channel == None): #this is when they join a vc (they werent in one before so they gotta have just joined one)
join_time = round(time.time())
userdata["join_time"] = join_time
elif(before.channel.guild == after.channel.guild): #wrote this to only check if they changed vc within the same guild, but then I realised it can also catch all the mute/deafen events yay.
return
elif(str(after.channel.guild.id) != guild_id): #this will check if the channel they're in after the event (we wanna record the time passed if its None or a different guild, both of which will get triggered by this)
if(userdata["join_time"] == None): return #this will catch errors, if they were to happen
userdata = voice_data[guild_id][new_user]
leave_time = time.time()
passed_time = leave_time - userdata["join_time"]
userdata[total_time] += passed_time
userdata["join_time"] = None #preventive measure against errors
with open('data/voice_leaderboard.json', 'w') as update_user_data:
json.dump(voice_data, update_user_data, indent=4)
I get this error when I disconnect:
elif(before.channel.guild == after.channel.guild):
AttributeError: 'NoneType' object has no attribute 'guild'
Anyone know how to fix this?
after.channel is None because the user is not connected to a channel after disconnecting.
You will have to cater for cases like this.
...
elif after.channel is None:
return
elif(before.channel.guild == after.channel.guild):
return
...

Is there a way to record the specific user of a command for future use without a database?

So I'm working on a discord bot using discord.py and I'm trying to create a bot for the moderation team in a server, the bot will swap the 'Moderator' role with a 'Leave of absence' role for when they're not active, however the code I have come up with has a slight loopholing problem that I just can't figure out, the code for the commands is this
...
#client.command()
#commands.has_role('Moderator')
async def sl(ctx, member: discord.Member = None):
if not member:
member = ctx.author
loa = ctx.guild.get_role(848032714715561985)
mod = ctx.guild.get_role(848032880709074944)
await member.add_roles(loa)
await member.remove_roles(mod)
await ctx.send("I have filed your Leave, take care, we look forward to your return!")
#client.command()
async def sr(ctx, member: discord.Member = None):
if not member:
member = ctx.author
mod = ctx.guild.get_role(848032880709074944)
loa = ctx.guild.get_role(848032714715561985)
await member.add_roles(mod)
await member.remove_roles(loa)
await ctx.send("Welcome back!")
...
as you can see anyone could use the second command to just give themselves a moderator role, I can't set the second command to be moderator only use as the moderator will no longer have said role from using the first command, I'm racking my brain to think of a work around i.e. logging the command users id to a whitelist and having only those whitelisted id's be able to use the second command, I've done many googlesearches for this but have come back with no results, any suggestions would be appreciated, please forgive that this question is a bit lengthy and I'm still very new to coding in general so any help at all, even if you don't fully understand what I'm blabbering on about would be very appreciated, thank you.
Check for the loa role in the command (ex):
mod = None
for role in ctx.author.roles:
if role.id == 848032714715561985: mod = True
if mod:
#your code here
So from your question, I'm guessing that you would like to code basically a "storage" file and make sure the person on the leave of absence was previously a moderator.
What you could do is create a csv file, for example records.csv (in the same folder as your main .py file of course), and every time someone calls the sl command, the program will record the user that used it.
import csv
#client.command()
#commands.has_role('Moderator')
async def sl(ctx, member: discord.Member = None):
if not member:
member = ctx.author
loa = ctx.guild.get_role(848032714715561985)
mod = ctx.guild.get_role(848032880709074944)
await member.add_roles(loa)
await member.remove_roles(mod)
file = open("records.csv", "w")
file.writelines(str(ctx.author.id))
file.close()
await ctx.send("I have filed your Leave, take care, we look forward to your return!")
#client.command()
async def sr(ctx, member: discord.Member = None):
if not member:
member = ctx.author
mod = ctx.guild.get_role(848032880709074944)
loa = ctx.guild.get_role(848032714715561985)
found = False
with open('records.csv', 'r') as file:
reader = csv.reader(file)
for row in reader:
if row[0] == str(ctx.author.id):
found = True
break
if found = False:
await member.add_roles(mod)
await member.remove_roles(loa)
await ctx.send("Welcome back!")
else:
await ctx.send("We do not have history of you having a moderator role.")
Are you running the program through an online environment like Repl.it? If so, this may not be the best way to approach this problem since people would have access to your this records.csv file (which you may not care about but just in case). If you are running the program through your desktop file directories, then there should be no privacy concerns.

Check if a user id is in a list of user ids to allow command access - discord.py

im fairly new to python and have been writing a discord bot for a server with me and a few friends as practice. I've added a few commands that could be pretty disruptive if all users had access to them at all times and have been trying to add a method to enable and disable access to them without using discord roles but a python list instead. I added a command that i can use to to add their user id to a list, but when the user tries to use the command they receive a permission error.
the code below is the problematic code from a cog
modlist = []
def ListCheck():
async def IsInList(ctx):
member = ctx.message.author.id
return member in modlist
return commands.check(IsInList)
#commands.command()
async def AddUser(self, ctx, *, question):
modlist.append(question)
#commands.command()
async def ViewModList(self, ctx):
await ctx.send('User ids that have access:')
for i in range(0, len(modlist)):
await ctx.send(modlist[i])
#commands.command()
async def ClearModList(self, ctx):
modlist.clear()
await ctx.send('Modlist cleared')
#commands.command()
#ListCheck()
async def PermTest(self, ctx):
await ctx.send('user is in modlist')
Any help would be hugely appreciated
One way you could do it would be to use commands.check(). The function in the code below checks if the message author, ctx.author, is in the given list, modList. If the author is in this list when the command 'test' is invoked, then the bot will send "You are a mod!", otherwise it will raise a check error.
modList = [394506589350002688, 697725862128386048] # just some ids as examples
def check_Mod(ctx):
if ctx.author.id in modList:
return ctx.author.id in modList
#commands.command()
#commands.check(check_Mod)
async def test(ctx):
await ctx.send("You are a mod!")
The above working is shown in the image below:
To answer is there is a way to add user ids to the list via a command?, you may either use a json or text file. In this case, I will use a text file since it's simpler, and also because my json isn't as fluent as others.
Here's an example of how your text file may look like:
394506589350002688
697725862128386048
First you need to read the file and check whether the author's id is in the file. We will be changing the check_Mod function for this. If you want to know more about reading through a text file, or feel that you may need a more efficient way, you may have a look at this: How to search for a string in text files?
def check_Mod(ctx):
with open('Mod.txt') as f: # do change the 'Mod.txt' to the name that suits you. Ensure that this file is in the same directory as your code!
if str(ctx.author.id) in f.read(): # this is reading the text file and checking if there's a matching string
return ctx.author.id
Now for writing the author id into your text file.
#commands.command()
#commands.check(check_Mod)
async def add_Mod(ctx, user:discord.Member=None):
if user == None:
await ctx.send("Please provide a user to add as a Mod!")
return
# First we'll make some functions for cleaner, more readable code #
def is_Mod(user_id):
## This function will check if the given id is already in the file. True if in file, False if not ##
with open('Mod.txt', 'r') as f:
if str(user_id) in f.read():
return True
else:
return False
def add_Mod(user_id):
## This function will add the given user id into the given text file, Mod.txt ##
with open('Mod.txt', 'a') as f: # 'a' is used for appending, since we don't want to overwrite all the ids already in the file
f.write(f"{str(user_id)}\n")
f.close()
# Now we put those functions to use #
if is_Mod(user.id) == True:
await ctx.send(f"The user {user} is already a Mod!")
else:
add_Mod(user.id)
await ctx.send(f"{user} added as a Mod!")
The above should work as seen in the image below.
p.s. If you have any other questions, please post them as a new question, thank you!

decorator still calls function after failing

I understand why the error check is printed, but I don't understand why despite the fact that the user calling does not have the mod label, it runs the function anyway, maybe I don't really understand decorators (user also has a token role but I commented that part out for this test)
# token check
#bot.command(name='wallet', help='display if a token is available for user')
#commands.has_role('mod') # remove later
# #commands.has_any_role('PBToken 3', 'PBToken 6')
async def token_check(ctx):
name = ctx.author.name
response = name + ', You have a token available!'
await ctx.send(response)
# error message if user has no token currently
#bot.event
async def on_command_error(ctx, error):
if isinstance(error, commands.errors.MissingRole):
name = ctx.author.name
# await ctx.send(name + ', you currently do not have a token, keep leveling up!')
await ctx.send('error check')
Here is the output:
Intead of using the mod role, why don't you run it using the check for whether the user has server administrator. To do that, all you do would be to replace:
#commands.has_role('mod')
with:
#commands.has_guild_permissions(administrator=True)
This would make the command only run if the specified user has server admin.
Please feel free to reply if you need any more help! :)

Resources