Trying to disable/enable commands locally using json file - json

I'm trying to make 2 commands, One to enable commands and one to disable commands per guild, but I have problems as I don't have much experience with these json files,im trying to make it so when i disable a command it will check if the id is there and if the command is added already,and a command to enable the commmand back,where it checks for the guild id and command name
here is the code:
#client.command()
async def disablecommand(ctx, commandname):
command = client.get_command(commandname)
with open("Disabled_commands.json") as f:
configData = json.load(f)
disabledcommands = configData[f"{ctx.guild.id}"]
if str(ctx.guild.id) in configData: #check if the guild id is in list
disabledcommands.append(command)
if command in disabledcommands: #check if command is already disabled
await ctx.reply("command already disabled")
else: #if command is not disable
disabledcommands.append(command)
with open(f"{ctx.guild.id}", "r+") as f:
data = json.load(f)
data["Disabled_commands"] = disabledcommands
f.seek(0)
f.write(json.dumps(data))
f.truncate()
await ctx.send("command has been disabled")
else: #if guild id is not in json file
disabledcommands.append(command)
with open(f"{ctx.guild.id}", "r+") as f:
data = json.load(f)
data[f"{ctx.guild.id}"] = {}
data["Disabled_commands"] = disabledcommands
f.seek(0)
f.write(json.dumps(data))
f.truncate()
await ctx.send("command has been disabled")
#client.command()
async def enablecommand(ctx, commandname):
command = client.get_command(commandname)
with open("Disabled_commands.json") as f:
configData = json.load(f)
disabledcommands = configData[f"{ctx.guild.id}"]
if str(ctx.guild.id) in configData: #check if server is in the list
if command in disabledcommands: #check if command is disabled
disabledcommands.remove(command)
with open("Disabled_commands.json", "r+") as f:
data = json.load(f)
data[f"{ctx.guild.id}"] = disabledcommands
f.seek(0)
f.write(json.dumps(data))
f.truncate()
await ctx.send("Command is now enabled")
else: #if command is enabled already
await ctx.send("Command isnt disabled")
else: #if server isnt in the list
await ctx.send("Command isnt disabled")
my json should look like this:
{"816726041673990214":{"ping","mute","ban"},"786631236838883400":{"troll","thing","ping"}}

The problem is, that your json is invalid, and so the json decoder raises an error. The problem with your json is that you use {} around "ping","mute","ban", but "ping","mute","ban" is a list, so you would have to use [] around it. Your fixed json should look like this:
{"816726041673990214":["ping","mute","ban"],"786631236838883400":["troll","thing","ping"]}

Related

How can I define a log-channel?

I am trying to read a channel ID from a JSON. With this you should be able to determine a channel, a kind of log, where the bot messages should be sent. However, I unfortunately have no idea how to get this ID for a single guild from the JSON.
My approaches:
async def logchannel():
with open("src/logchannel.json", "r") as f:
lchannel = json.load(f)
return lchannel
(Says at the top of the class)
#commands.command(hidden=True)
#commands.guild_only()
#commands.has_permissions(manage_messages=True)
async def setlog(self, ctx, channel: str):
"""Changes the log channel"""
with open('src/logchannel.json', 'r', encoding='utf-8') as fp:
log_channel = json.load(fp)
try:
log_channel[f"{ctx.channel.id}"] = channel
except KeyError:
new = {ctx.channel.id: channel}
log_channel.update(new)
await ctx.send(f "Channel set to: `{channel}`")
with open('src/logchannel.json', 'w', encoding='utf-8') as fpp:
json.dump(log_channel, fpp, indent=2)
Should then be the specified channel/always update itself.
#commands.command()
async def cdel(self, ctx, channel: discord.TextChannel):
with open('src/logchannel.json', 'r', encoding='utf-8') as fp:
log_channel = json.load(fp)
await channel.delete()
await log_channel.send(f "**Successfully deleted channel `{channel}`!**")
Which gives me the obvious error AttributeError: 'dict' object has no attribute 'send'. I probably have the error there, but don't see it/it doesn't work at all the way I want.
To put it in a nutshell: I want the user to be able to choose his log-channel the bot sends like all the ban messages etc. The user itself can always change the channel by command. The only problem I now have is that the bot gives me the error above as I am not requestion the channel in the right way out of the JSON.
EDIT:
This is how my JSON file looks like:
{
"811573570831384638": "811578547751616532",
"811623743959990295": "811573570831384638"
}
First number is the channel ID the command was executed in and the second key is the defined mod-log channel.
json.load(fp) gets you the whole json file as dictionary. You should get the channel id from it.
#commands.command(hidden=True)
#commands.guild_only()
#commands.has_permissions(manage_messages=True)
async def setlog(self, ctx, channel: discord.TextChannel):
"""Changes the log channel"""
with open('src/logchannel.json', 'r', encoding='utf-8') as fp:
log_channel = json.load(fp)
try:
log_channel[str(ctx.guild.id)] = channel.id
except KeyError:
new = {str(ctx.guild.id): channel.id}
log_channel.update(new)
await ctx.send(f"Channel set to: `{channel}`")
with open('src/logchannel.json', 'w', encoding='utf-8') as fpp:
json.dump(log_channel, fpp, indent=2)
With a little bit edit in your code, you can use this command to change log channel. You just have to mention the channel you want to make the new log channel. It will save the id of the guild where the command is used and the mentioned channel as key and value.
When you want to send a message to this channel, you have to use guild.get_channel() or discord.utils.get(). Because you need a discord.TextChannel instance in order to send message to this channel.
#commands.command()
async def cdel(self, ctx, channel: discord.TextChannel):
with open('src/logchannel.json', 'r', encoding='utf-8') as fp:
log_channel = json.load(fp)
await channel.delete()
log_c = ctx.guild.get_channel(log_channel[str(ctx.guild.id)])
# Or you can use:
# log_c = discord.utils.get(ctx.guild.text_channels, id=log_channel[str(ctx.guild.id)])
await log_c.send(f"**Successfully deleted channel `{channel}`!**")
If you want to update the log channel ID when the current log channel is deleted, you can use the on_guild_channel_delete event to check it.
#client.event
async def on_guild_channel_delete(channel):
with open('src/logchannel.json', 'r', encoding='utf-8') as fp:
log_channel = json.load(fp)
if channel.id in log_channel.values():
values = list(log_channel.values())
keys = list(log_channel.keys())
log_channel[keys[values.index(channel.id)]] = <channel id>
with open('src/logchannel.json', 'w', encoding='utf-8') as fpp:
json.dump(log_channel, fpp, indent=2)
You have to put a backup channel id between <channel id> in case of you delete the log channel. So if you delete the log channel, it will automatically changes it with this backup id.

Why does my JSON create multiple entries instead of updating them?

I have a command where you can enter a correct answer. If it is correct, the user is credited with points in a JSON. But my update function seems to be broken, because after another correct execution an entry is made again in the JSON for the same user. However, I just want the points to update. Also, the JSON stops after the second entry about the user. What was wrong?
Code:
correct_answers = "A"
# Open the JSON after start
def json_open():
with open('users.json', 'r', encoding='utf-8') as f:
users = json.load(f)
return users
class Questions(commands.Cog, name='Question'):
"""Question bot"""
def __init__(self, bot):
super().__init__()
self.bot = bot
#commands.command()
async def question(self, ctx, answer):
self.question.enabled = False
global correct_answers
if correct_answers != answer:
await ctx.author.send(f"You guessed {answer} which is **wrong**. Good luck next time!")
await ctx.message.delete()
return
# OPEN JSON FILE, LOAD DATA
with open('users.json', 'r') as f:
users = json.load(f)
await self.update_data(users, ctx.message.author)
await self.add_experience(users, ctx.message.author, 10)
with open('users.json', 'w') as f:
json.dump(users, f)
await ctx.message.delete()
# UPDATE DATA
async def update_data(self, users, user):
if not user.id in users:
users[user.id] = {}
users[user.id]['Points'] = 0
#users[user.id]['level'] = 1
async def add_experience(self, users, user, exp):
users[user.id]['Points'] += exp
It looks like the last functions do not work or is the add_experience function not needed?
The JSON looks like this after the second execution:
{"MYID": {"Points": 10}, "MYIDAGAIN": {"Points": 10}}
Somehow it is converted into a str so you have to update the function a bit. To explain it better:
Turn the user.id into a str.
async def update_data(self, users, user):
key = str(user.id)
if key not in users:
users[key] = {}
users[key]['Points'] = 0
async def add_experience(self, users, user, exp):
users[str(user.id)]['Points'] += exp
Maybe also have a look at the page where the problem is explained.
I think that you have to string the user ID like this
users[str(user.id)]['Points'] += exp

why i am getting a error while using json with discord.py

Hi i am trying to add a money system in my discord.py. i am using json with it. i am following a tutorial on youtube. bot and i am trying this code:
#client.command()
async def bal(ctx):
user = ctx.author
await open_account(ctx.author)
users = await get_bank_data()
wallet_amt = users[str(user.id)]["wallet"]
bank_amt = users[str(user.id)]["bank"]
em = discord.Embed(title=f"{ctx.author.name}'s balance")
em.add_field(name = "Wallet", value = wallet_amt)
em.add_field(name = "Bank", value = bank_amt)
await ctx.send(embed=em)
async def open_account(user):
users = await get_bank_data()
if str(user.id) in users:
return False
else:
users[str(user.id)] = {}
users[str(user.id)]["wallet"] = 0
users[str(user.id)]["bank"] = 0
with open("mainBank.json", "r") as f:
json.dump(users, f)
return True
async def get_bank_data():
with open("mainBank.json", "r") as f:
users = json.load(f)
return users
but i am getting a error:
Ignoring exception in command bal:
Traceback (most recent call last):
File "C:\Users\saheb\AppData\Local\Programs\Python\Python38-32\lib\site-packages\discord\ext\commands\core.py", line 85, in wrapped
ret = await coro(*args, **kwargs)
File "c:/Users/saheb/Documents/Discord Bot/DiscordBot.py", line 49, in bal
await open_account(ctx.author)
File "c:/Users/saheb/Documents/Discord Bot/DiscordBot.py", line 84, in open_account
json.dump(users, f)
File "C:\Users\saheb\AppData\Local\Programs\Python\Python38-32\lib\json\__init__.py", line 180, in dump
fp.write(chunk)
io.UnsupportedOperation: not writable
please help if you know how to fix this.
You need to open files in the write mode to write to them (json.dumps writes to a file), you can open a file by doing open("filename", "w"), the "w" says to open in write mode.
Because you use with open("mainBank.json", "r") as f:. That 'r' means just read. If you just want to append new values, use 'a', if you want to read and write, use 'r+'.

Python discord bot command with json database

So I'm making a command that is !command
When you type that in a chat it will update the member(s) score in the database.
The database will look something like this
{
"person": "epikUbuntu"
"score": "22"
}
How would I go on doing that?
edit:
If I wasn't clear I meant ho would I go on doing the python part of it?
JSON objects in python work like dictionaries.
You can write a new dictionary and save it:
data = {"foo": "bar"}
with open("file.json", "w+") as fp:
json.dump(data, fp, sort_keys=True, indent=4) # kwargs for beautification
And you can load data from a file (this will be for updating the file):
with open("file.json", "r") as fp:
data = json.load(fp) # loading json contents into data variable - this will be a dict
data["foo"] = "baz" # updating values
data["bar"] = "foo" # writing new values
with open("file.json", "w+") as fp:
json.dump(data, fp, sort_keys=True, indent=4)
Discord.py example:
import os # for checking the file exists
def add_score(member: discord.Member, amount: int):
if os.path.isfile("file.json"):
with open("file.json", "r") as fp:
data = json.load(fp)
try:
data[f"{member.id}"]["score"] += amount
except KeyError: # if the user isn't in the file, do the following
data[f"{member.id}"] = {"score": amount} # add other things you want to store
else:
data = {f"{member.id}": {"score": amount}}
# saving the file outside of the if statements saves us having to write it twice
with open("file.json", "w+") as fp:
json.dump(data, fp, sort_keys=True, indent=4) # kwargs for beautification
# you can also return the new/updated score here if you want
def get_score(member: discord.Member):
with open("file.json", "r") as fp:
data = json.load(fp)
return data[f"{member.id}"]["score"]
#bot.command()
async def cmd(ctx):
# some code here
add_score(ctx.author, 10)
# 10 is just an example
# you can use the random module if you want - random.randint(x, y)
await ctx.send(f"You now have {get_score(ctx.author)} score!")
References:
Context managers
Dictionaries
Loading data from a json
Writing to a json

discord rewrite How to make bot delete data when left a server

How can i make my bot remove its data from json file when it leaves a server.
#client.event
async def on_guild_leave(guild):
guildname = str(guild)
with open("serverchannel.json") as jsong_file:
data = json.load(jsong_file)
data = data.remove(guildname)
with open("serverchannel.json", "w") as outfile:
json.dump(data, outfile)
on_guild_leave() is not a client event, you should be using on_guild_remove(), which is called whenever the client is no longer in a guild anymore. Reasons can include leaving, getting banned, getting kicked, and the guild being deleted.
#client.event
async def on_guild_remove(guild):
guildname = guild.name
with open("serverchannel.json") as jsong_file:
data = json.load(jsong_file)
data = data.remove(guildname)
with open("serverchannel.json", "w") as outfile:
json.dump(data, outfile)