Search code examples
python-3.xdiscord.pypython-multithreading

Adding a timeout to discord bot with threading library python


I have been working on a bot that accepts votes to kick a user then re-invites them and gives them their roles back. I made it go 24/7 and realized I need a timeout so that if the vote doesn't complete after a few minutes, the voting will be reset and a new vote can be called. I tried using the Threading library but I cant get it to work properly.

import threading
import discord
from discord.ext import commands


def reset():
    global vote_started
    global users_voted
    vote_started = False
    users_voted.clear()
    print('[+] Timeout Finished')

@client.command(pass_context=True)
async def votekick(ctx, *, member: discord.Member, Reason=None):
    global vote_started
    global users_needed
    global kicked_user
    global member_to_be_kicked
    global channel
    global role
    t = threading.Timer(120.0, reset) <===== This should call reset function after 120 seconds
    if member == client.user:
        await ctx.send('[-] ok retard')
        return
    else:
        if not vote_started:
            kicked_user = member
            member_to_be_kicked = member
            vote_started = True
            users_voted.append(ctx.author.id)
            await ctx.channel.send(
                f'[+] A votekick has started to kick {member.display_name}. [{len(users_voted)}/{users_needed}]')
            t.start    <======== This should start the reset function if this is the first vote counted
            print('[+] Timeout Started')
        else:
            if ctx.author.id in users_voted:
                await ctx.send('[-] Error: You have already voted.')
            else:
                if member != member_to_be_kicked:
                    await ctx.send(f'[-] A vote to kick {member_to_be_kicked.display_name} has already started. nice '
                                   f'try retard.')
                    return
                users_voted.append(ctx.author.id)
                if len(users_voted) < users_needed:
                    await ctx.channel.send(f'[+] Users voted: [{len(users_voted)}/{users_needed}]')
                else:
                    pass
        if len(users_voted) >= users_needed:
            invite = await ctx.channel.create_invite(reason=None, max_uses=1)
            try:
                dm_channel = await member.create_dm()
                await dm_channel.send(invite)
            except:
                pass
            await ctx.channel.send(
                f"[+] [{len(users_voted)}/{users_needed}] users have voted to kick {member.display_name}.")
            await member.kick(reason=Reason)
            vote_started = False
            users_voted.clear()
            role = member.roles
            channel = ctx.channel

When I run this, I thought the reset function should have been called, but in the logs, I see [+] Timeout Started but not [+] Timeout Finished like I expected. This means the reset function is never called. What do I need to do to fix this. I am using python 3.9.4 with the discord rewrite.


Solution

  • Timer will never start, because you haven't initialized the t.start() function

    Just add parentheses to t.start and timer will start, when you'll try to votekick again

    import threading
    import discord
    from discord.ext import commands
    
    
    def reset():
        global vote_started
        global users_voted
        vote_started = False
        users_voted.clear()
        print('[+] Timeout Finished')
    
    @client.command(pass_context=True)
    async def votekick(ctx, *, member: discord.Member, Reason=None):
        global vote_started
        global users_needed
        global kicked_user
        global member_to_be_kicked
        global channel
        global role
        t = threading.Timer(120.0, reset)
        if member == client.user:
            await ctx.send('[-] ok retard')
            return
        else:
            if not vote_started:
                kicked_user = member
                member_to_be_kicked = member
                vote_started = True
                users_voted.append(ctx.author.id)
                await ctx.channel.send(
                    f'[+] A votekick has started to kick {member.display_name}. [{len(users_voted)}/{users_needed}]')
                t.start() # <========== This line
                print('[+] Timeout Started')
            else:
                if ctx.author.id in users_voted:
                    await ctx.send('[-] Error: You have already voted.')
                else:
                    if member != member_to_be_kicked:
                        await ctx.send(f'[-] A vote to kick {member_to_be_kicked.display_name} has already started. nice '
                                       f'try retard.')
                        return
                    users_voted.append(ctx.author.id)
                    if len(users_voted) < users_needed:
                        await ctx.channel.send(f'[+] Users voted: [{len(users_voted)}/{users_needed}]')
                    else:
                        pass
            if len(users_voted) >= users_needed:
                invite = await ctx.channel.create_invite(reason=None, max_uses=1)
                try:
                    dm_channel = await member.create_dm()
                    await dm_channel.send(invite)
                except:
                    pass
                await ctx.channel.send(
                    f"[+] [{len(users_voted)}/{users_needed}] users have voted to kick {member.display_name}.")
                await member.kick(reason=Reason)
                vote_started = False
                users_voted.clear()
                role = member.roles
                channel = ctx.channel