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.
Related
Basic Information
I am creating a python script that can encrypt and decrypt a file with previous session data.
The Problem
I am able to decrypt my file and read it using a key. This returns a bytes string which I can in turn convert to a string. However, this string needs to be converted to a dictionary, which I cannot do. Using ast, json and eval I have run into errors.
Bytes string
decrypted = fernet.decrypt(encrypted)
String
string = decrypted.decode("UTF-8").replace("'", '"')
If I use eval() or ast.literal_eval() I get the following error:
Then I tried using json.loads() and I get the following error:
The information blocked out on both images is to protect my SSH connections. In the first image it is giving me a SyntaxError at the last digit of my ip address.
The Function
The function that is responsible for this when called looks like this:
def FileDecryption():
with open('enc_key.key', 'rb') as filekey:
key = filekey.read()
filekey.close()
fernet = Fernet(key)
with open('saved_data.txt', 'rb') as enc_file:
encrypted = enc_file.read()
enc_file.close()
decrypted = fernet.decrypt(encrypted)
print(decrypted)
string = decrypted.decode("UTF-8").replace("'", '"')
data = f'{string}'
print(data)
#data = eval(data)
data = json.loads(data)
print(type(data))
for key in data:
#command_string = ["load", data[key][1], data[key][2], data[key][3], data[key][4]]
#SSH.CreateSSH(command_string)
print(key)
Any help would be appreciated. Thanks!
Your data seems like it was written incorrectly in the first place, but without a complete example hard to say.
Here's a complete example that round-trips a JSON-able data object.
# requirement:
# pip install cryptography
from cryptography.fernet import Fernet
import json
def encrypt(data, data_filename, key_filename):
key = Fernet.generate_key()
with open(key_filename, 'wb') as file:
file.write(key)
fernet = Fernet(key)
encrypted = fernet.encrypt(json.dumps(data).encode())
with open(data_filename, 'wb') as file:
file.write(encrypted)
def decrypt(data_filename, key_filename):
with open(key_filename, 'rb') as file:
key = file.read()
fernet = Fernet(key)
with open(data_filename, 'rb') as file:
return json.loads(fernet.decrypt(file.read()))
data = {'key1': 'value1', 'key2': 'value2'}
encrypt(data, 'saved_data.txt', 'enc_key.key')
decrypted = decrypt('saved_data.txt', 'enc_key.key')
print(decrypted)
Output:
{'key1': 'value1', 'key2': 'value2'}
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"]}
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
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+'.
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)