Как можно улучшить и сократить систему Levels и Xp в discord.py?
У меня есть такой код:
from webserver import keep_alive
import discord
import json
import sqlite3
import re
import os
from discord.ext import commands
import datetime
import random
from pymongo import MongoClient
import asyncio
import levelsys
cogs = [levelsys]
client = commands.Bot(command_prefix = "!", intents=discord.Intents.all());
bot_channel = 702341394014011425
talk_channels = [702341394014011425]
level = ["?Bronze?", "?Iron?", "?Gold?", "?Diamond?", "?Elite?"]
levelnum = [5, 10, 20, 30, 50]
cluster = MongoClient("mongodb+srv://<ss>:<12345>@cluster0.vraiu.mongodb.net/Cluster0?retryWrites=true&w=majority")
levelling = cluster["discord"]["levelling"]
class levelsys(commands.Cog):
def __init__(self, client):
self.client = client
@commands.Cog.listener()
async def on_ready(self):
print("ready!")
@commands.Cog.listener()
async def on_message(self, message):
if message.channel.id in talk_channels:
stats = levelling.find_one({"id" : message.author.id})
if not message.author.bot:
if stats is None:
newuser = {"id" : message.author.id, "xp" : 100}
levelling.insert_one(newuser)
else:
xp = stats["xp"] + 5
levelling.update_one({"id":message.author.id}, {"$set":{"xp":xp}})
lvl = 0
while True:
if xp < ((50*(lvl**2))+(50*lvl)):
break
lvl += 1
xp -= ((50*((lvl-1)**2))+(50*(lvl-1)))
if xp == 0:
await message.channel.send(f'У {message.author.mention} повысился уровень до **{lvl}**!')
for i in range(len(level)):
if lvl == levelnum[1]:
await message.author.add_roles(discord.utils.get(message.author.guild.roles, name=level[1]))
embed = discord.Embed(description=f"{message.author.mention} ты получил роль **{level[1]}**!")
embed.set_thumbnail(url=message.author.avatar_url)
await message.channel.send(embed=embed)
@commands.command()
async def ранг(self, ctx):
if ctx.channel.id == bot_channel:
stats = levelling.find_one({"id" : ctx.author.id})
if stats is None:
embed = discord.Embed(description="Вы не отправляли ни одного сообщения!")
await ctx.channel.send(embed=embed)
else:
xp = stats["xp"]
lvl = 0
rank = 0
while True:
if xp < ((50*(lvl**2))+(50*lvl)):
break
lvl += 1
xp -= ((50*((lvl-1)**2))+(50*(lvl-1)))
boxes = int((xp/(200*((1/2) * lvl)))*20)
rankings = levelling.find().sort("xp",-1)
for x in rankings:
rank += 1
if stats["id"] == x["id"]:
break
embed = discord.Embed(title="{} статистика уровней".format(ctx.author.name))
embed.add_field(name="Имя", value=ctx.author.mention, inline=True)
embed.add_field(name="XP", value=f"{xp}/{int(200*((1/2)*lvl))}", inline=True)
embed.add_field(name="Ранг", value=f"{rank}/{ctx.guild.member_count}", inline=True)
embed.add_field(name="Progress Bar [lvl]", value=boxes * ":blue_square:" + (20-boxes) * ":white_large_square:", inline=False)
embed.set_thumbnail(url=ctx.author.avatar_url)
await ctx.channel.send(embed=embed)
@commands.command()
async def лидеры(self, ctx):
if (ctx.channel.id == bot_channel):
rankings = levelling.find().sort("xp",-1)
i = 1
embed = discord.Embed(title="Рейтинг участнков:")
for x in rankings:
try:
temp = ctx.guild.get_member(x["id"])
tempxp = x["xp"]
embed.add_field(name=f"{i}: {temp.name}", value=f"Всего опыта: {tempxp}", inline=False)
i += 1
except:
pass
if i == 11:
break
await ctx.channel.send(embed=embed)
Но он:
- Не работает
- Длинный (по моему мнению)
Как можно его улучшить и сократить?
Пишу на replit
Ответы (1 шт):
Автор решения: Be3y4uu_K0T
→ Ссылка
Я бы предложил Вам переписать вот эту часть:
lvl = 0
while True:
if xp < ((50*(lvl**2))+(50*lvl)):
break
lvl += 1
xp -= ((50*((lvl-1)**2))+(50*(lvl-1)))
Простая математика:
(50*(lvl**2)) + (50*lvl) = xp
50*lvl**2 + 50*lvl = xp
50*(lvl**2 + lvl) = xp
lvl**2 + lvl = xp/50
lvl**2 + lvl - xp/50 = 0
D = b**2 -4*a*c = 1 + 4*xp/50
lvl = (math.sqrt(4*xp/50 + 1) - 1) / 2
И лучше вынести это в функции:
def get_level(xp: int) -> float:
k = 50
return (math.sqrt(4*xp/k + 1) - 1) / 2
def get_current_level(xp: int) -> float:
k = 50
return (math.sqrt(4*xp/k + 1) - 1) // 2
def get_xp(level: int) -> int:
k = 50
return k*level*(level + 1)
>>> get_level(2800)
7.0
>>> get_level(2700)
6.865459931328117
>>> get_current_level(2800)
7.0
>>> get_current_level(2700)
6.0
Также поиск для ранга:
rankings = levelling.find().sort("xp",-1)
for user in rankings:
rank += 1
if stats['id'] == user['id']:
break
rank = levelling.count_documents({'xp': {'$gt': xp}}) + 1
Также эти три строчки можно объединить:
stats = levelling.find_one({"id" : message.author.id})
...
xp = stats["xp"] + 5
levelling.update_one({"id":message.author.id}, {"$set":{"xp":xp}})
stats = levelling.find_one_and_update(
{'_id': message.author.id}, {'$inc': {'xp': 5}},
return_document=pymongo.ReturnDocument.AFTER
)
Итоговый мой код:
from discord.ext import commands
import pymongo as mg
import discord as ds
import math
bot = commands.Bot(command_prefix='!', intents=ds.Intents.all())
bot_channel = ...
talk_channels = {...}
levels = {
5: '?Bronze?',
10: '?Iron?',
20: '?Gold?',
30: '?Diamond?',
50: '?Elite?',
}
client = mg.MongoClient('mongodb://127.0.0.1:27017/', connect=False)
db = client.discord
def get_level(xp: int) -> float:
k = 50
return (math.sqrt(4*xp/k + 1) - 1) / 2
def get_current_level(xp: int) -> float:
k = 50
return (math.sqrt(4*xp/k + 1) - 1) // 2
def get_xp(level: int) -> int:
k = 50
return k*level*(level + 1)
@bot.event
async def on_ready():
print('ready!')
@bot.event
async def on_message(message):
if message.channel.id not in talk_channels:
return
if message.author.bot:
return
if message.content.startswith(bot.command_prefix):
return await bot.process_commands(message)
user = db.levelling.find_one_and_update(
{'_id': message.author.id}, {'$inc': {'xp': 100}},
return_document=mg.ReturnDocument.AFTER
)
if user is None:
new_user = {'_id' : message.author.id, 'xp' : 100}
db.levelling.insert_one(new_user)
else:
level = get_level(user['xp'])
print(user['xp'], level, level.is_integer())
if level.is_integer():
await message.channel.send(
f'У {message.author.mention} повысился уровень до **{int(level)}**!'
)
if rank := levels.get(level):
await message.author.add_roles(ds.utils.get(message.author.guild.roles, name=rank))
embed = ds.Embed(description=f'{message.author.mention} ты получил роль **{rank}**!')
embed.set_thumbnail(url=message.author.avatar_url)
await message.channel.send(embed=embed)
@bot.command()
async def ранг(ctx):
if ctx.channel.id != bot_channel:
return
stats = db.levelling.find_one({'_id' : ctx.author.id})
if stats is None:
embed = ds.Embed(description='Вы не отправляли ни одного сообщения!')
await ctx.channel.send(embed=embed)
else:
xp = stats['xp']
lvl = get_current_level(xp)
next_xp = get_xp(lvl + 1)
rank = db.levelling.count_documents({'xp': {'$gt': xp}}) + 1
length_bar = 20
boxes = int(length_bar * xp / next_xp)
embed = ds.Embed(title=f'{ctx.author.name} статистика уровней')
embed.add_field(name='Имя', value=ctx.author.mention, inline=True)
embed.add_field(name='XP', value=f'{xp}/{next_xp}', inline=True)
embed.add_field(name='Ранг', value=f'{rank}/{ctx.guild.member_count}', inline=True)
embed.add_field(
name='Progress Bar [lvl]',
value=boxes * ':blue_square:' + (length_bar-boxes) * ':white_large_square:',
inline=False
)
embed.set_thumbnail(url=ctx.author.avatar_url)
await ctx.channel.send(embed=embed)
@bot.command()
async def лидеры(ctx):
if ctx.channel.id != bot_channel:
return
embed = ds.Embed(title='Рейтинг участнков:')
rankings = db.levelling.aggregate([
{'$limit': 10},
{
'$setWindowFields': {
'sortBy': {'xp': mg.DESCENDING},
'output': {
'rank': {'$rank': {}},
},
}
}
])
for user in rankings:
xp = user['xp']
member = ctx.guild.get_member(user['_id'])
embed.add_field(
name=f'{user["rank"]}: {member.name}',
value=f'Всего опыта: {xp}',
inline=False
)
await ctx.channel.send(embed=embed)
bot.run('<token>')