Search code examples
pythonwindowsdiscorddiscord.pybots

How To Prevent Leveling Up From Spam For A Python Discord Bot


So, recently I've been working on a discord bot in python that levels you up while you talk in a discord server, many popular bots like Carl Bot and MEE6 both have functions like this which I find to be quite interesting; so I wanted to try and recreate it. The bot works perfectly fine, but it does have a few caveats which I would like to try and have fixed, for one there is no way to check your level or get a leader board for the top 10 people in the server. But, the issue I want to find and resolve here is the fact that you can spam messages; messages that make no sense and many messages that only need to be one or two. So, how do I do this, how can I prevent spam in my system for creating this. Any help would be appreciated!

from discord.ext import commands
import json
import discord
import os
import random
import time

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

#On Ready Event.
@client.event
async def on_ready():
  print("Bot Ready.")
  time.sleep(1)
  print("Logged In As:  {0.user}".format(client))

#Function For Getting A Users Information And Level Them Up.
@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)

#Function For Sending Messages.
@client.event
async def on_message(message):
  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.channel)

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

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

async def add_experience(users, user, exprience):
  users[str(user.id)]["experience"] += exprience

async def level_up(users, user, channel):
  experience = users[str(user.id)]["experience"]
  level_start = users[str(user.id)]["level"]
  level_end = int(experience ** (1/4))

  if level_start < level_end:
    await channel.send(f"{user.mention} Has Leveled Up!  They Have Leveled Up To Level {level_end}!")
    users[str(user.id)]["level"] = level_end

client.run(os.getenv("Token"))
client.run(os.environ["Token"])

Solution

  • The method to ensure that members cannot spam messages and still receive credit is to implore some form of a timing system. To do this, you need to be able to store data. However, you likely won't need a persistent data storage and can use a dictionary.

    In this example, I am storing a member's last message in terms of seconds from time.time() and checking if their last recorded message timestamp is less than 60 seconds from now. This will only allow members to receive credit for their message minutes at a time. Additionally, notice that I only update a member's last message after receiving credit. This ensures that they may still send more than a single message a minute and still receive credit for their messages spaced a minute apart.

    from time import time
    
    member_messages = {}
    
    @client.event
    async def on_message(message):
        global member_messages
    
        current_time = time()
        last_message_requirement = current_time - 60  # Change this cooldown (in seconds) however you like
    
        if member_messages.get(message.author.id, 0) <= last_message_requirement:
            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.channel)
    
            with open("users.json", "w") as f:
                json.dump(users, f)
    
            member_messages[message.author.id] = current_time