Why is my bot not able to detect when a user leaves in Telegram? - Stack Overflow

时间: 2025-01-06 admin 业界

General Information: I am using Python 3.11.0 and python-telegram-bot==21.5.

I am having some issues with my Telegram bot. The general idea of the bot is to keep track of a timer that is set for each new user entering the group. When the timer expires, the user is automatically kicked off. If the user leaves the group on their own accord, before the timer expires, the timer is not reset. If the user decides to join the group again, the timer will resume where it stopped. (e.g. If originally the timer starts with 10 minutes and the user leaves the group with 4 minutes remaining on the clock, when they join again the timer will be set for 4 minutes instead of 10). I am using Redis for storage.

This very simple idea is somehow causing me issues. The bot seems to not be able to detect the users leaving the group, or at least, the event is not triggered. The issue also seems to be isolated to either larger groups or other users. When I try it with a second of account of mine (to join, leave, and then join again), it works properly. When other users do the same thing, it doesn't work.

Take the following code as an approximation of what I have:


def memberJoin(chatMemberUpdate: ChatMemberUpdated):
    status = chatMemberUpdate.difference().get("status")
    old, new = status or (None, status)

    if old in (None, ChatMember.BANNED, ChatMember.LEFT) and new == ChatMember.MEMBER:
        return True
    
    return False

def memberLeft(chatMemberUpdate: ChatMemberUpdated):
    status = chatMemberUpdate.difference().get("status")
    old, new = status or (None, status)

    if old == ChatMember.MEMBER and new == ChatMember.LEFT:
        return True
    
    return False

async def check_status(update: Update, context: ContextTypes.DEFAULT_TYPE):
    left = memberLeft(update.chat_member)
    join = memberJoin(update.chat_member)
    user_id = update.chat_member.from_user.id
    chat_id = update.chat_member.chat.id

    if join:
        # If it's the user's first time joining...
        if not memory.hexists(user_id, "join"):
            memory.hset(user_id, "join", datetime.now().isoformat())
            
            # ... some other stuff with JobQueue for the timer ...

         else:
            # If the user is re-joining the group before the timer expired.
            memory.persist(user_id)
            join_time = memory.hget(user_id, "join")
            left_time = memory.hget(user_id, "left")

            logger.info(f"Join Time: {join_time} +++++++ Left Time: {left_time}")

            elapsed_time = (datetime.fromisoformat(left_time) - datetime.fromisoformat(join_time)).total_seconds()
            remaining_time = 10 * 60 - elapsed_time # Remaining time on the timer.
            
            if remaining_time > 0:
                # ... again, some JobQueue stuff to kick user off ...


    elif left:
        logger.info(f"User {user_id} left at {datetime.now().isoformat()}.")
        memory.hset(user_id, "left", datetime.now().isoformat())
        memory.expire(user_id, timedelta(hours = 1))
        
        # ... some JobQueue stuff to stop previous timer ...
        
if __name__ == "__main__":

    app = ApplicationBuilder().token(API_KEY).build()

    app.add_handler(ChatMemberHandler(check_status, ChatMemberHandler.CHAT_MEMBER))
    
    app.run_polling(drop_pending_updates = True, allowed_updates = Update.ALL_TYPES)

It seems pretty logical to me and it's supposed to work. By the time the code is at the else-block inside of the "join" if-block, it should have collected both the "join" time and the "left" time. But somehow, the "left" time is always None. This is an example of what the logger outputs when a user re-joins:

INFO:apscheduler.scheduler:Removed job 9065c10968444e8dba41593926032c99
INFO:__main__: Join Time: 2025-01-04T08:25:38.461033+00:00 +++++++ Left Time: None
ERROR:__main__:There has been an error: fromisoformat: argument must be str
INFO:apscheduler.scheduler:Added job "<JOB_NAME>" to job store "default"

As you can see, the "left" time is None, so datetime.fromisoformat(left_time) cannot be converted, and thus, the elapsed time cannot be calculated. I am currently dealing with it with try...except and fixed a constant value. But it's obviously not a long-term solution. I am not sure if I am overlooking something that is right in front of my eyes, or if it's something else. Moreover, since the "left" event is not triggered, the logger.info output in the "left" elif-block is nowhere to be seen in the terminal.

I hope someone can help me solve this issue, I would appreciate it so much!

Update

While debugging, I found out that another bot was interacting (unknowingly) with the group and kicking users out. While that is not really what is contributing majorly to the issue I was facing, it revealed the mystery of some users being kicked out prematurely and somehow messing with my own bot. After correcting this, I also noticed that my Redis DB was storing user IDs from earlier processes (because when I restarted my docker container, the JobQueue would be canceled, thus, the user IDs would not be removed). I think that was also causing some problems but, again, it did not majorly contribute to the issue I was still somewhat facing.

I am not sure if I can say that the issue has been resolved but it seems to be working for now.

INFO:__main__: User <USER_ID> left at 2025-01-04T18:05:55.069938+00:00.

General Information: I am using Python 3.11.0 and python-telegram-bot==21.5.

I am having some issues with my Telegram bot. The general idea of the bot is to keep track of a timer that is set for each new user entering the group. When the timer expires, the user is automatically kicked off. If the user leaves the group on their own accord, before the timer expires, the timer is not reset. If the user decides to join the group again, the timer will resume where it stopped. (e.g. If originally the timer starts with 10 minutes and the user leaves the group with 4 minutes remaining on the clock, when they join again the timer will be set for 4 minutes instead of 10). I am using Redis for storage.

This very simple idea is somehow causing me issues. The bot seems to not be able to detect the users leaving the group, or at least, the event is not triggered. The issue also seems to be isolated to either larger groups or other users. When I try it with a second of account of mine (to join, leave, and then join again), it works properly. When other users do the same thing, it doesn't work.

Take the following code as an approximation of what I have:


def memberJoin(chatMemberUpdate: ChatMemberUpdated):
    status = chatMemberUpdate.difference().get("status")
    old, new = status or (None, status)

    if old in (None, ChatMember.BANNED, ChatMember.LEFT) and new == ChatMember.MEMBER:
        return True
    
    return False

def memberLeft(chatMemberUpdate: ChatMemberUpdated):
    status = chatMemberUpdate.difference().get("status")
    old, new = status or (None, status)

    if old == ChatMember.MEMBER and new == ChatMember.LEFT:
        return True
    
    return False

async def check_status(update: Update, context: ContextTypes.DEFAULT_TYPE):
    left = memberLeft(update.chat_member)
    join = memberJoin(update.chat_member)
    user_id = update.chat_member.from_user.id
    chat_id = update.chat_member.chat.id

    if join:
        # If it's the user's first time joining...
        if not memory.hexists(user_id, "join"):
            memory.hset(user_id, "join", datetime.now().isoformat())
            
            # ... some other stuff with JobQueue for the timer ...

         else:
            # If the user is re-joining the group before the timer expired.
            memory.persist(user_id)
            join_time = memory.hget(user_id, "join")
            left_time = memory.hget(user_id, "left")

            logger.info(f"Join Time: {join_time} +++++++ Left Time: {left_time}")

            elapsed_time = (datetime.fromisoformat(left_time) - datetime.fromisoformat(join_time)).total_seconds()
            remaining_time = 10 * 60 - elapsed_time # Remaining time on the timer.
            
            if remaining_time > 0:
                # ... again, some JobQueue stuff to kick user off ...


    elif left:
        logger.info(f"User {user_id} left at {datetime.now().isoformat()}.")
        memory.hset(user_id, "left", datetime.now().isoformat())
        memory.expire(user_id, timedelta(hours = 1))
        
        # ... some JobQueue stuff to stop previous timer ...
        
if __name__ == "__main__":

    app = ApplicationBuilder().token(API_KEY).build()

    app.add_handler(ChatMemberHandler(check_status, ChatMemberHandler.CHAT_MEMBER))
    
    app.run_polling(drop_pending_updates = True, allowed_updates = Update.ALL_TYPES)

It seems pretty logical to me and it's supposed to work. By the time the code is at the else-block inside of the "join" if-block, it should have collected both the "join" time and the "left" time. But somehow, the "left" time is always None. This is an example of what the logger outputs when a user re-joins:

INFO:apscheduler.scheduler:Removed job 9065c10968444e8dba41593926032c99
INFO:__main__: Join Time: 2025-01-04T08:25:38.461033+00:00 +++++++ Left Time: None
ERROR:__main__:There has been an error: fromisoformat: argument must be str
INFO:apscheduler.scheduler:Added job "<JOB_NAME>" to job store "default"

As you can see, the "left" time is None, so datetime.fromisoformat(left_time) cannot be converted, and thus, the elapsed time cannot be calculated. I am currently dealing with it with try...except and fixed a constant value. But it's obviously not a long-term solution. I am not sure if I am overlooking something that is right in front of my eyes, or if it's something else. Moreover, since the "left" event is not triggered, the logger.info output in the "left" elif-block is nowhere to be seen in the terminal.

I hope someone can help me solve this issue, I would appreciate it so much!

Update

While debugging, I found out that another bot was interacting (unknowingly) with the group and kicking users out. While that is not really what is contributing majorly to the issue I was facing, it revealed the mystery of some users being kicked out prematurely and somehow messing with my own bot. After correcting this, I also noticed that my Redis DB was storing user IDs from earlier processes (because when I restarted my docker container, the JobQueue would be canceled, thus, the user IDs would not be removed). I think that was also causing some problems but, again, it did not majorly contribute to the issue I was still somewhat facing.

I am not sure if I can say that the issue has been resolved but it seems to be working for now.

INFO:__main__: User <USER_ID> left at 2025-01-04T18:05:55.069938+00:00.
Share Improve this question edited 23 hours ago Leah asked yesterday LeahLeah 771 silver badge7 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 0

While I’m not entirely sure this’ll work, try using the my_chat_member update type instead of just chat_member. The former is specifically for updates related to the bot itself and may capture more reliable events.

Simply modify your handler:

app.add_handler(ChatMemberHandler(check_status, ChatMemberHandler.MY_CHAT_MEMBER))