0
votes

I currently have a bot that pings people if certain criteria are met. The issue is that the bot will ping spam if the content is repeated. For example, say user JohnSmith#1234 says "ping me if someone says 'apple' or 'apples' in the chat.' It'll do so. However, say there are 4 messages in quick succession:

User1: "Hey, do you like apples?"

User2: "Yeah, I like apples"

User1: "What's your favorite kind of apple?"

User2: "I don't really have a favorite apple, but I like Granny Smith"

then the bot will say:

@JohnSmith#1234 for "apple"

@JohnSmith#1234 for "apple"

@JohnSmith#1234 for "apple"

@JohnSmith#1234 for "apple"

because "apple" was mentioned 4 times. Is there a way to tell the bot "if you pinged JohnSmith#1234 in the last 10 seconds, don't ping him again?" I basically want an action to not execute if it's been done recently.

Edit:

While the answers shown seem to work for just 1 user, they don't seem to work for multiple. The answers will accommodate JohnSmith#1234 just fine, but as of now, it'll render the bot useless until JohnSmith#1234's delay is done.

I'm not sure how to tell the bot "keep doing what you're doing, but run the delay loop on the side." Here's what the bot is currently doing, continuing off of the example I provided previously:

1: Say @JohnSmith#1234 for 'apple'

2: Puts John Smith into a waiting dictionary until 10 seconds is up. (Note: the bot won't do anything else but wait. This is bad because it should ping users besides John Smith).

3: If those 10 seconds are up, it'll ping John Smith again. Otherwise, it'll keep doing stuff for other users.

3

3 Answers

1
votes

Create a cooldown dictionary where you can store people the bot has already pinged and the end of the cooldown. Ex: cooldows = {"JohnSmith#1234": 1597786825}.

Then when your bot tries to ping someone just check if they are in the dictionary. if they are check the epoch timestamp, if the time now is greater than the time listed, remove the user from the dictionary and ping him, if its not don't ping them. If they are not in the dictionary the bot can ping them

1
votes

You could make a dictionary that will track when people posted their message, whenever a user types 'apple' you can add their name to the dictionary followed by the current date and time.

You can get the current date and time using this code:

import datetime

dateTime = datetime.datetime.now()

Your dictionary will look something like this:

userCooldowns = {}

You can then add users to it using this code:

dateTime =  datetime.datetime.now()
userCooldowns["JohnSmith#1234"] = dateTime

Once you have all that you can run a check to see if the message was sent within 10 seconds of the timestamp, if it was then don't do anything, otherwise send the mention:

import datetime
import time

dateTime =  datetime.datetime.now() # Gets the current date and time
seconds = datetime.timedelta(seconds=10) # Gets the 10 seconds needed
newTime = dateTime + seconds # Gets the date and time 10 seconds ahead of current time
 
while True:
    dateTime =  datetime.datetime.now() # Gets the current date and time again
    if dateTime < newTime: # Checks if the current time is less than the time 10 seconds ahead
        # Do something if time is within 10 seconds
    else:
        # Do something when 10 seconds are over
    time.sleep(1)
0
votes

I figured out a method that worked with my bot. It relies on the threading module. Here's the basic code:

import threading

global Username
global Username_ID
global Username_mention
global Username_ping
Username= []
Username_ID = XXXXXXXXXXXXXXXXXX #each X is a number. Used to actually ping people, since Discord Nitro lets people customize their mentions
Username_mention = 'Username#XXXX'
Username_ping = True

def Username_timer_func():

    global Username_ping
    Username_ping = True
    #note: the following step is optional. Useful for bug checking
    print("Username can be pinged again")

Username_timer = threading.Timer(30, Username_timer_func)
global Username_ping

if bool(in_Username) == True and bool(Username_ping) == True and str(message.author) != Username_mention:
            await channel.send("<@"+ str(Username_ID)+">" + " for "+ str(set(<list of words in the message, e.g. apple>) & set(Username))[1:][:-1])
            Username_ping = False
            Username_timer.start()

The main issue is that a lot of it is hard-coded. However, a lot of people in the server wanted specific preferences (e.g. amount of seconds after ping, what they want to be pinged for, their ID, their mention, etc.) that either can't be automated or idk how to automate without losing all that data if the bot ever goes offline.

The basic logic is "ping this user. Once you ping them, set the ability to ping that user again to "False." After 30 seconds, run a function that sets that ability to "True" again. Since threading timers run separately from the rest of the code (e.g. the bot doesn't have to keep checking while doing other tasks), I don't have to worry about unwanted stops.