discord.py file append rewrite the file instead of appending - discord.py

When I want to append something to a file (with the append mode), it just erases everything before writing what I would like to append.
I have an on_voice_state_update event, when triggered, it checks if the user joined a specific channel. If yes: it creates a voice channel with some permissions for the user who created the voice channel. It is also supposed to append to a file called member_channel.txt this: {member.id}:{the_member's_voice_channel.id} but instead of adding this at the end of the file, it first erases everything and then add what I want to add.
Here are the codes:
#bot.event
async def on_voice_state_update(member, before, after):
if after.channel is not None and after.channel.id == 1059553884286230608:
guild = bot.get_guild(966749409314504774)
everyone_role = discord.utils.get(guild.roles, id = 966749409314504774)
community_role = discord.utils.get(guild.roles, id = 1006304076331483256)
muted_role = discord.utils.get(guild.roles, id = 1059109986258653244)
global member_channel
member_channel = await guild.create_voice_channel(name = f"Vocal de {member.name}")
await member_channel.edit(category = discord.utils.get(get_guild().channels, id = 1057730051321372692))
await member_channel.set_permissions(member, connect = True, manage_channels = True, manage_permissions = True, priority_speaker = True, move_members = True)
await member_channel.set_permissions(everyone_role, view_channel = False)
await member_channel.set_permissions(community_role, view_channel = True)
await member_channel.set_permissions(muted_role, speak = False)
await member.move_to(member_channel)
with open("member_channel.txt", "a") as file: # THIS IS WHAT
file.write(str(member.id) + ":" + str(member_channel.id) + "\n") # IS'NT WORKING
print(f"[{await get_hour()}] {member.name} created a temporary voice channel.")
#then we want to delete the channel if it is empty and equal to a member's channel, and also delete the line in member_channel.txt corresponding to this member and channel
elif before.channel is not None and len(before.channel.members) == 0:
new_file_value = ""
try:
with open("member_channel.txt", "r") as file:
lines = file.readlines()
splited_lines = []
for line in lines:
splited_lines.append((line.split(":")))
for lines in splited_lines:
for line in lines:
line.replace("\n", "")
if str(before.channel.id) in line:
await before.channel.delete()
del splited_lines[line.index(str(before.channel.id))]
for line in splited_lines:
print(new_file_value)
new_file_value += line
for ele in line:
print(new_file_value)
new_file_value += line + ":"
with open("member_channel.txt", "w+") as file:
file.write(new_file_value)
print(f"[{await get_hour()}] I deleted the temporary voice channel {member_channel.name}.")
file.close()
file.close()
except NameError:
return

Related

Remove Discord Markdown

How do I remove mark down in discord.py?
I was making an eval command but I realized that sending it with markdown eg. '```py print("example code")```', breaks it.
I want to make it easier for me and the other admin/mod to use it for debugging purposes in my bot.
#client.command()
async def eval(ctx, *, code):
out, err = io.StringIO(), io.StringIO()
sys.stdout = out
sys.stderr = err
await ctx.channel.trigger_typing()
await aexec(code)
results = out.getvalue()
errors = err.getvalue()
await ctx.send(embed = discord.Embed(title = "Eval", description=f"Output Is \n```bash\n{results}```"))
await ctx.send(embed = discord.Embed(title = "Code", description=f"Input Is \n```py\n{code}```"))
the above code is what I've tried so far..
I've been playing with your code for the past hours, I wanted to figured it out :P
This is the only way i could access the code when there was markdown in the command input.
#client.command()
async def eval(ctx, *, code):
raw = f'{ctx.message.content}'[12:-3] # Get rid of markdown + py and last 3 ```
#Might want to add if statements in case the input doesn't include py / something else.
print (raw) # Testing purpose
out, err = io.StringIO(), io.StringIO()
sys.stdout = out
sys.stderr = err
await ctx.channel.trigger_typing()
exec(raw) # no need for await
results = out.getvalue()
errors = err.getvalue()
await ctx.send(embed = discord.Embed(title = "Eval", description=f"Output Is \n```bash\n{results}```"))
await ctx.send(embed = discord.Embed(title = "Code", description=f"Input Is \n```py\n{raw}```"))
await ctx.send(embed = discord.Embed(title = "Errors", description=f"Errors are \n```py\n{errors}```")) #Added this output to see errors

Word limit in an embed description

I want to make that when the description of the embed exceeds the word limit it continues in another separate embed but that the previous embed ends with 3 dots (...) eliminating a small part of the message and following it in the other embed, at the moment this is the code I have:
#commands.command(aliases=["pj"])
async def personaje(self, ctx, personaje=None, member: discord.Member=None):
if personaje is None:
await ctx.send(":x: Debes proporcionar la id del personaje que quieres visualizar")
else:
if member is None:
member = ctx.author
if os.path.exists("json/Roleplay/Personajes/{member}/{idpersonaje}.json".format(member=member.id, idpersonaje=personaje)):
with open("json/Roleplay/Personajes/{member}/{idpersonaje}.json".format(member=member.id, idpersonaje=personaje), 'r') as f:
data = json.load(f)
Nombre = data["Nombre"]
Historia = data["Historia"]
color = data["Color"]
personalizado = data["Personalizado"]
text = ""
text = "".join(f"\n**{key}:** {value}" for key, value in personalizado.items())
color = int(color, 16)
timestamp = datetime.datetime.now()
prueba = f"**{Nombre} {text}\n\n**Historia:**\n {Historia}"
if len(prueba) < 2000:
embed=discord.Embed(description=prueba, color=color)
embed.set_author(name=ctx.author, icon_url=ctx.author.avatar_url)
embed.timestamp = timestamp
await ctx.send(embed=embed)
else:
embed=discord.Embed(description=prueba[:-3] + "...", color=color)
embed.set_author(name=ctx.author, icon_url=ctx.author.avatar_url)
embed.timestamp = timestamp
await ctx.send(embed=embed)
else:
await ctx.send(":x: Este personaje no existe")
return
Example of what I want to do (obviously with a longer text that exceeds 2000 words):
https://imgur.com/zytqLKm
Best method is making a function to split the data every n char.
Making fields with name of empty line might be better then using new embed.
Keep in mind don't split at 2000 exactly because you have ...
def split_length(data: str, n: int):
'''Splits the data given at each given n'''
out_data = []
for i in range(0, len(data), n):
out_data.append(data[i:i+n])
return out_data
#bot.command()
async def longtext(ctx):
embed = discord.Embed(title='Example of long text split')
long_text = 'qwerty'
field_values = split_length(data=long_text, n=2)
for value in field_values:
# "\u200b" is just an empty line
embed.add_field(name="\u200b", value=f"{value} ...", inline=True)
await ctx.send(embed=embed)

Ignoring exception in command buy

An error pops up when I do the buy command. This code comes from The coding academy discord server but they didn't seem to know how to help. This is for a currency bot. Here is my code followed by the error:
`mainshop = [{"name":"Knife","price":65,"description":"Mug people! Unlocks `mug`"},
{"name":"Laptop","price":5000,"description":"Post some sweet memes on reddit! Unlocks `postmemes`"},
{"name":"Gun","price":10000,"description":"Rob people and businesses! Unlocks `rob` and `mug`"}]
#client.command()
async def shop(ctx):
em = discord.Embed(title = "Shop")
for item in mainshop:
name = item["name"]
price = item["price"]
desc = item["description"]
em.add_field(name = name, value = f"${price} | {desc}")
await ctx.send(embed = em)
#client.command()
async def buy(ctx,item,amount = 1):
await open_account(ctx.author)
res = await buy_this(ctx.author,item,amount)
if not res[0]:
if res[1]==1:
await ctx.send("That Object isn't there!")
return
if res[1]==2:
await ctx.send(f"You don't have enough money in your wallet to buy {amount} {item}")
return
await ctx.send(f"You just bought {amount} {item}")
#client.command()
async def inventory(ctx):
await open_account(ctx.author)
user = ctx.author
users = await get_bank_data()
try:
bag = users[str(user.id)]["bag"]
except:
bag = []
em = discord.Embed(title = "Inventory")
for item in bag:
name = item["item"]
amount = item["amount"]
em.add_field(name = name, value = amount)
await ctx.send(embed = em)
async def buy_this(user,item_name,amount):
item_name = item_name.lower()
name_ = None
for item in mainshop:
name = item["name"].lower()
if name == item_name:
name_ = name
price = item["price"]
break
if name_ == None:
return [False,1]
cost = price*amount
users = await get_bank_data()
bal = await update_bank(user)
if bal[0]<cost:
return [False,2]
try:
index = 0
t = None
for thing in users[str(user.id)]["bag"]:
n = thing["item"]
if n == item_name:
old_amt = thing["amount"]
new_amt = old_amt + amount
users[str(user.id)]["bag"][index]["amount"] = new_amt
t = 1
break
index+=1
if t == None:
obj = {"item":item_name , "amount" : amount}
users[str(user.id)]["bag"].append(obj)
except:
obj = {"item":item_name , "amount" : amount}
users[str(user.id)]["bag"] = [obj]
with open("mainbank.json","w") as f:
json.dump(users,f)
await update_bank(user,cost*-1,"wallet")
return [True,"Worked"]
Error:
Ignoring exception in command buy: Traceback (most recent call last): File "/opt/virtualenvs/python3/lib/python3.8/site-packages/discord/ext/commands/core.py", line 85, in wrapped
ret = await coro(*args, **kwargs) File "main.py", line 50, in buy
res = await buy_this(ctx.author,item,amount) File "main.py", line 104, in buy_this
if bal[0]<cost: TypeError: 'bool' object is not subscriptable
The above exception was the direct cause of the following exception:
Traceback (most recent call last): File "/opt/virtualenvs/python3/lib/python3.8/site-packages/discord/ext/commands/bot.py", line 902, in invoke
await ctx.command.invoke(ctx) File "/opt/virtualenvs/python3/lib/python3.8/site-packages/discord/ext/commands/core.py", line 864, in invoke
await injected(*ctx.args, **ctx.kwargs) File "/opt/virtualenvs/python3/lib/python3.8/site-packages/discord/ext/commands/core.py", line 94, in wrapped
raise CommandInvokeError(exc) from exc discord.ext.commands.errors.CommandInvokeError: Command raised an exception: TypeError: 'bool' object is not subscriptable
As much as I see your value of the variable bal is somehow a bool value due to which if you try to take out values from it as a list, it gives you the following error.
You should try checking the definition of the update_bank() command and check it's return type.
Check this website for more info on the error : https://www.tutorialexample.com/fix-typeerror-bool-object-is-not-subscriptable-in-python-python-tutorial/

Discord.py Embed text file, get more results

I'd like to send on a Discord bot a message embed but text is from another file. I did this way and it doesn't work:
bot.command()
async def gdc(ctx):
"""Wins GDC"""
index1 = 0
file = open("/home/plo/rkr/res_wins2", "r")
for line in file.readlines():
line = line.strip()
index1 += 1
if index1 == 4: break
message = line
embed = discord.Embed()
embed.description = message
embed.title = title
embed.colour = 0xF1C40F
await ctx.send(embed=embed)
However, it seems only one result goes out... Here is a part of my txt file:
Roi mouton: 9
tomate: 8
The_Portos: 8
And here is the result:
You're changing the value of line every loop in the for loop so you'll have to make a list of the lines
lines = []
with open("/home/plo/rkr/res_wins2", "r") as file: # Use this to open and close the file
for line in file.readlines():
line = line.strip()
lines.append(line)
index1 += 1
if index1 == 4: break
embed = discord.Embed()
embed.description = '\n'.join(lines)
embed.title = title
embed.colour = 0xF1C40F
await ctx.send(embed=embed)

WTForms and Flask - Validate file size in another field

I have a structure like this in my Flask app:
class UploadForm(FlaskForm):
username = StringField('Username',
validators=[DataRequired(),
Length(min=2, max=20)])
email = StringField('Email address',
validators=[DataRequired(),
Email()])
domain_name = StringField('Project name',
validators=[DataRequired()])
dir_level = IntegerField(
'Folder level', validators=[DataRequired()])
file = FileField('Upload textfile', validators=[
FileRequired(), FileAllowed(['txt'])])
scan_now = BooleanField('Scan website now')
submit = SubmitField('Submit')
The scan_now field submits the project into a different, faster queue and it is used for priority reports.
Given that scan_now is more computationally expensive, I would like to limit its usage only for .txt files that have less than 500 rows, and raise error if a user checks the box but has uploaded more than 500 rows.
How can I do that?
Thanks you!
EDIT:
Since you have requested my manager views file, here's the logic behind it:
#manager.route("/manage_url", methods=["GET", "POST"])
#auth.login_required
def manage_url():
form = UploadForm()
config = []
if request.method == "POST" and form.validate_on_submit():
date = datetime.today()
date = date.strftime('%d/%m/%Y')
file = request.files['file']
filename = file.filename
email = request.form['email']
username = request.form['username']
livello_dir = request.form['livello_dir']
domain_name = request.form['domain_name']
try:
scan_now = request.form['scan_now']
except:
scan_now = False
file.save(os.path.join(uploads_dir, secure_filename(filename)))
# crea file config da leggere
config_entry = {
filename:
{
"date": date,
"email": email,
"user": username,
"domain_name": domain_name.replace(" ", "_"),
"livello_dir": livello_dir
}
}
config.append(config_entry)
if not os.path.exists('./config.json'):
with open(os.path.join(ROOT_DIR, 'config.json'), 'w', encoding='utf-8') as f:
json.dump(config, f, ensure_ascii=False, indent=4)
else:
with open(os.path.join(ROOT_DIR, 'config.json'), 'r') as f:
config = json.load(f)
config.append(config_entry)
with open(os.path.join(ROOT_DIR, 'config.json'), 'w', encoding='utf-8') as f:
json.dump(config, f, ensure_ascii=False, indent=4)
if scan_now:
def on_the_fly_scan():
executor = Executor()
executor.start_process(file=filename)
thread = Thread(target=on_the_fly_scan)
thread.daemon = True
thread.start()
return redirect(url_for('manager.manage_url'))
try:
files = os.listdir(os.path.join(app.instance_path, 'uploads'))
paths = [os.path.join(app.instance_path, 'uploads') +
"/" + file for file in files]
except:
files = None
paths = None
domini = []
contact = ""
operator_name = ""
livello_dir = ""
for file in paths:
with open(file, 'r', encoding='unicode_escape') as csv_file:
csv_reader = csv.reader(csv_file, delimiter='\t')
rows = list(csv_reader)
dominio_rilevato = tldextract.extract(rows[1][0]).registered_domain
domini.append(dominio_rilevato)
with open(os.path.join(ROOT_DIR, 'config.json'), 'r') as f:
data = json.load(f)
content_package = list(zip(domini, files))
return render_template("manage_url.html",
content_package=content_package,
data=data,
config_entries=list(list(d.keys()) for d in data),
form=form,
contact=contact,
operator_name=operator_name,
livello_dir=livello_dir)
You can Validate file size manually before saving the file. add these lines before file.save(os.path.join(uploads_dir, secure_filename(filename))) :
if scan_now:
if count_rows(filename) > 500:
raise Exception('too many rows')
# or whatever you need to do
And here's the count_rows() code :
def count_rows(filename):
with open(filename, 'r') as f:
return sum(1 for row in f)
# if file is in csv format return sum() - 1
# because first line is not a record
return sum(1 for row in f) - 1

Resources