简体   繁体   中英

How to create a leveling system with discord.py with python?

I tried to make a levelling system into my bot but something went wrong. The level rose after each message and not after the specified parameter. How to make it work properly? How to create a leveling system with discord.py ?

Here is my code:

@client.event
async def on_message( message ):
    if message.author.bot:
        return
    else:
        member = message.author.name
        msg_len = len(message.content)
        rand_number_1 = randint( 1, 10 )
        exp_first = msg_len * rand_number_1
        coins_first = exp_first // 10
        data_levels_member_first = { 'exp': 0, 'coins': 0, 'level': 0}
        filename = f'./Data/Profiles/Profile{message.author.name}{message.guild}.json'
        try:
            data = load( filename )
        except Exception:
            dump( filename, data_levels_member_first )
            data = load( filename )
        exp_member = data['exp']
        coins_member = data['coins']
        lvl_member = data['level']
        exp = exp_first + exp_member
        coins = coins_first + coins_member
        lvl = exp ** (1/4)
        if lvl_member < lvl:
            lvl_member += 1
            print('You lvl up!')
        data_levels_member = { 'exp': exp, 'coins': coins, 'level': lvl_member}
        dump( filename, data_levels_member )
        await client.process_commands(message)
import json
import discord

try:
    with open("users.json") as fp:
        users = json.load(fp)
except Exception:
    users = {}

def save_users():
    with open("users.json", "w+") as fp:
        json.dump(users, fp, sort_keys=True, indent=4)

def add_points(user: discord.User, points: int):
    id = user.id
    if id not in users:
        users[id] = {}
    users[id]["points"] = users[id].get("points", 0) + points
    print("{} now has {} points".format(user.name, users[id]["points"]))
    save_users()

def get_points(user: discord.User):
    id = user.id
    if id in users:
        return users[id].get("points", 0)
    return 0

@client.event
async def on_message(message):
    if message.author == client.user:
        return
    print("{} sent a message".format(message.author.name))
    if message.content.lower().startswith("!lvl"):
        msg = "You have {} points!".format(get_points(message.author))
        await client.process_commands(message.channel, msg)
    add_points(message.author, 1)

client.run(token)

I've made one for you. It's server specific as well

@client.event
async def on_message(message):
    if not message.author.bot:
        print('function load')
        with open('level.json','r') as f:
            users = json.load(f)
            print('file load')
        await update_data(users, message.author,message.guild)
        await add_experience(users, message.author, 4, message.guild)
        await level_up(users, message.author,message.channel, message.guild)

        with open('level.json','w') as f:
            json.dump(users, f)
    await client.process_commands(message)




async def update_data(users, user,server):
    if not str(server.id) in users:
        users[str(server.id)] = {}
        if not str(user.id) in users[str(server.id)]:
            users[str(server.id)][str(user.id)] = {}
            users[str(server.id)][str(user.id)]['experience'] = 0
            users[str(server.id)][str(user.id)]['level'] = 1
    elif not str(user.id) in users[str(server.id)]:
            users[str(server.id)][str(user.id)] = {}
            users[str(server.id)][str(user.id)]['experience'] = 0
            users[str(server.id)][str(user.id)]['level'] = 1

async def add_experience(users, user, exp, server):
  users[str(user.guild.id)][str(user.id)]['experience'] += exp

async def level_up(users, user, channel, server):
  experience = users[str(user.guild.id)][str(user.id)]['experience']
  lvl_start = users[str(user.guild.id)][str(user.id)]['level']
  lvl_end = int(experience ** (1/4))
  if str(user.guild.id) != '757383943116030074':
    if lvl_start < lvl_end:
      await channel.send('{} has leveled up to Level {}'.format(user.mention, lvl_end))
      users[str(user.guild.id)][str(user.id)]['level'] = lvl_end


@client.command(aliases = ['rank','lvl'])
async def level(ctx,member: discord.Member = None):

    if not member:
        user = ctx.message.author
        with open('level.json','r') as f:
            users = json.load(f)
        lvl = users[str(ctx.guild.id)][str(user.id)]['level']
        exp = users[str(ctx.guild.id)][str(user.id)]['experience']

        embed = discord.Embed(title = 'Level {}'.format(lvl), description = f"{exp} XP " ,color = discord.Color.green())
        embed.set_author(name = ctx.author, icon_url = ctx.author.avatar_url)
        await ctx.send(embed = embed)
    else:
      with open('level.json','r') as f:
          users = json.load(f)
      lvl = users[str(ctx.guild.id)][str(member.id)]['level']
      exp = users[str(ctx.guild.id)][str(member.id)]['experience']
      embed = discord.Embed(title = 'Level {}'.format(lvl), description = f"{exp} XP" ,color = discord.Color.green())
      embed.set_author(name = member, icon_url = member.avatar_url)

      await ctx.send(embed = embed)

Here's a working one:

import discord
from discord.ext import commands
import asyncio
import os
import json
 
bot = commands.Bot(command_prefix="!")
##### START LEVEL COMMAND #####
 
with open("users.json", "ab+") as ab:
    ab.close()
    f = open('users.json','r+')
    f.readline()
    if os.stat("users.json").st_size == 0:
      f.write("{}")
      f.close()
    else:
      pass
 
with open('users.json', 'r') as f:
  users = json.load(f)
 
@bot.event    
async def on_message(message):
    if message.author.bot == False:
        with open('users.json', 'r') as f:
            users = json.load(f)
        await add_experience(users, message.author)
        await level_up(users, message.author, message)
        with open('users.json', 'w') as f:
            json.dump(users, f)
            await bot.process_commands(message)
 
async def add_experience(users, user):
  if not f'{user.id}' in users:
        users[f'{user.id}'] = {}
        users[f'{user.id}']['experience'] = 0
        users[f'{user.id}']['level'] = 0
  users[f'{user.id}']['experience'] += 6
  print(f"{users[f'{user.id}']['level']}")
 
async def level_up(users, user, message):
  experience = users[f'{user.id}']["experience"]
  lvl_start = users[f'{user.id}']["level"]
  lvl_end = int(experience ** (1 / 4))
  if lvl_start < lvl_end:
    await message.channel.send(f':tada: {user.mention} has reached level {lvl_end}. Congrats! :tada:')
    users[f'{user.id}']["level"] = lvl_end
 
@bot.command()
async def rank(ctx, member: discord.Member = None):
  if member == None:
    userlvl = users[f'{ctx.author.id}']['level']
    await ctx.send(f'{ctx.author.mention} You are at level {userlvl}!')
  else:
    userlvl2 = users[f'{member.id}']['level']
    await ctx.send(f'{member.mention} is at level {userlvl2}!')
 
##### END LEVEL COMMAND #####
 
bot.run(TOKEN)

I'd really caution against using a json file as a database. Json files tend to get corrupted on overwrite. There are a ton of cautionary tales of developers who have fallen into that trap and gotten burned. For a lightweight, quick solution, I'd recommend using sqlite. It's a built-in module in python and is much more stable and secure than a json file. A very simple sqlite3 example with comments (intents not included):

import sqlite3
import discord

bot = commands.Bot(command_prefix="!")

@client.event
async def on_member_join(member): ###add a row in the db for the new member when they join
   user = member.id
   conn = sqlite3.connect('your_db_here.db')
   cur = conn.cursor()
   cur.execute('INSERT INTO table_name_here("userid", "xp", "level")VALUES(user, 0, 0)')
   conn.commit()
   conn.close()
   await member.send("Your welcome message here")

@client.event
async def on_message(message):
    user = message.author.id
    conn = sqlite3.connect('your_db_here.db')
    cur = conn.cursor()
    cur.execute('SELECT xp, level FROM table_name_here WHERE userid = ?')
    results = cur.fetchone()
    row = results[0] ### results will be a list and since you're only fetching one record in this instance, you only need the first index
    old_xp = row[0] ##the first item in the index, in this case, xp
    old_level = row[1] ## the second item in the index, in this case, level
    new_xp = old_xp + 1
    if new_xp == 25: #this is where you set the threshold for leveling up to the first level
        new_level = 1
    else:
        new_level = old_level
    ###add more logic here for successive level-ups
    cur.execute('UPDATE table_name_here SET xp = ?, level = ? WHERE userid = ?', (new_xp, new_level, user)
    conn.commit()
    conn.close()
    ### here is where you'd put any sort of messaging if a person levels up or however you'd like to set it.
    
    

Here is a level sytem code that might help you:

@client.event
async def on_member_join(member):
    with open('users.json', 'r') as f:
        users = json.load(f)

    await update_data(users, member)

    with open('users.json', 'w') as f:
        json.dump(users, f)


@client.event
async def on_message(message):
    if message.author.bot == False:
        with open('users.json', 'r') as f:
            users = json.load(f)

        await update_data(users, message.author)
        await add_experience(users, message.author, 5)
        await level_up(users, message.author, message)

        with open('users.json', 'w') as f:
            json.dump(users, f)

    await client.process_commands(message)


async def update_data(users, user):
    if not f'{user.id}' in users:
        users[f'{user.id}'] = {}
        users[f'{user.id}']['experience'] = 0
        users[f'{user.id}']['level'] = 1


async def add_experience(users, user, exp):
    users[f'{user.id}']['experience'] += exp


async def level_up(users, user, message):
    with open('levels.json', 'r') as g:
        levels = json.load(g)
    experience = users[f'{user.id}']['experience']
    lvl_start = users[f'{user.id}']['level']
    lvl_end = int(experience ** (1 / 4))
    if lvl_start < lvl_end:
        await message.channel.send(f'{user.mention} has leveled up to level {lvl_end}')
        users[f'{user.id}']['level'] = lvl_end

@client.command()
async def level(ctx, member: discord.Member = None):
    if not member:
        id = ctx.message.author.id
        with open('users.json', 'r') as f:
            users = json.load(f)
        lvl = users[str(id)]['level']
        await ctx.send(f'You are at level {lvl}!')
    else:
        id = member.id
        with open('users.json', 'r') as f:
            users = json.load(f)
        lvl = users[str(id)]['level']
        await ctx.send(f'{member} is at level {lvl}!')

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM