Question:
Adding cooldown for user tries to farm XP-discord.py

Problem:

I'm working on a leveling bot. I found a major issue with gaining XP, and it can be abused extremely easily. I want it to have a cooldown after a user sends a message, so when a user spams in a channel to farm XP, they have a .5-second cooldown for each message, to prevent spam. If you spam a lot of messages, you can easily get to the #1 spot, and I don't want that to happen. Everything works with it, the XP gaining, leaderboard, and the level-up message. I've looked on the internet for an answer to this, and none of them helped. Does anyone know? Please and thank you!!! :)


Code:

@bot.event

async def on_message(message):

    if not message.author.bot:

        # specify the encoding as utf-8 when opening the file

        with open('level.json','r', encoding='utf-8') as f:

            users = json.load(f)

        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)


        # specify the encoding as utf-8 when writing the file

        with open('level.json','w', encoding='utf-8') as f:

            json.dump(users, f)

    await bot.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(':fire: {} has leveled up to **Level {}!**'.format(user.mention, lvl_end))

      users[str(user.guild.id)][str(user.id)]['level'] = lvl_end

      embed=discord.Embed(title=':fire: {} has leveled up to **Level {}!**'.format(user.mention, lvl_end), color=0x38ff6a)

      await bot.get_channel(log_channel_id).send(embed=embed)


@bot.command(aliases = ['rank','lvl'])

async def level(ctx,member: discord.Member = None):

    if not member:

        user = ctx.message.author

        # specify the encoding as utf-8 when opening the file

        with open('level.json','r', encoding='utf-8') 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:

      # specify the encoding as utf-8 when opening the file

      with open('level.json','r', encoding='utf-8') 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)


JSON file example:

{

  "757383943116030074": {

    "123456789012345678": {

      "experience": 0,

      "level": 1

    }

  }

}


Solution:

You can use a dictionary to keep track of the last message time for each user, and then check this timestamp to impose the cooldown.


# Create a dictionary to keep track of last message times

message_cooldown = {}


@bot.event

async def on_message(message):

    if not message.author.bot:

        # Check if the user is in the cooldown dictionary

        if message.author.id in message_cooldown:

            current_time = time.time()

            last_message_time = message_cooldown[message.author.id]


            # Check if the user's cooldown has expired (0.5 seconds cooldown)

            if current_time - last_message_time < 0.5:

                return


        with open('level.json', 'r', encoding='utf-8') as f:

            users = json.load(f)

        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)


        # Update the last message time for the user

        message_cooldown[message.author.id] = time.time()


        with open('level.json', 'w', encoding='utf-8') as f:

            json.dump(users, f)

    await bot.process_commands(message)


Here we use the time module to calculate the time elapsed between messages for each user and impose a 0.5-second cooldown. The message_cooldown dictionary stores the last message time for each user. If the cooldown hasn't expired, the message is ignored.


Answered By: >xapi

Credit: >Stack Overflow


Suggested blogs:

>PHP Error Solved: htaccess problem with an empty string in URL

>Plugins and Presets for Vuejs project

>Python Error Solved: load_associated_files do not load a txt file

>Python Error Solved: pg_config executable not found

>Set up Node.js & connect to a MongoDB Database Using Node.js

>Setting up a Cloud Composer environment: Step-by-step guide

>How to merge cells with HTML, CSS, and PHP?

>How to Migrate your apps to Android 13

>How to Read a csv file with php using cURL


Ritu Singh

Ritu Singh

Submit
0 Answers