slackapi / bolt-python

A framework to build Slack apps using Python
https://tools.slack.dev/bolt-python/
MIT License
1.06k stars 245 forks source link

Say() Function Producing Duplicate Messages in Channel #778

Closed irrational-labs closed 1 year ago

irrational-labs commented 1 year ago

(Filling out the following details about bugs will help us solve your issue sooner.)

Reproducible in:

pip freeze | grep slack 

python --version

sw_vers && uname -v # or `ver`

The slack_bolt version

slack-sdk==3.19.5 slack-bolt==1.15.5

(Paste the output of pip freeze | grep slack)

Python runtime version

3.9.12 (Paste the output of python --version)

OS info

ProductName: macOS ProductVersion: 12.6.1 BuildVersion: 21G217 Darwin Kernel Version 21.6.0: Thu Sep 29 20:12:57 PDT 2022; root:xnu-8020.240.7~1/RELEASE_X86_64

(Paste the output of sw_vers && uname -v on macOS/Linux or ver on Windows OS)

Steps to reproduce:

(Share the commands to run, source code, and project settings (e.g., setup.py))

  1. Set up venv with starter app.py code
    
    import os
    # Use the package we installed
    from slack_bolt import App
    from slack_bolt.adapter.socket_mode import SocketModeHandler
    import re
    import logging
    logging.basicConfig(level=logging.INFO)
    logger = logging.getLogger(__name__)

Initializes your app with your bot token and signing secret

app = App( token=os.environ["SLACK_BOT_TOKEN"] )

autoreply in hw function

@app.event("message") def auto_reply(event, say): try: if 'files' in event: #need to handle events with attachments differently if event['channel'] == "C019J946UPJ" and ('.pdf' in event['files'][0]['name'] or '.google' in event['text']) and (event['user'] != 'U038JV06ZMZ' and event['user'] != 'U03612ME0BY' and event['user'] != 'U029XD01PNF' and event['user'] != 'U038JV0HD19' and event['user'] != 'U03E7JSK3LG'): #if statement clarifying channel to watch for & to not reply to replies say(text = "An IL scientist will connect with you shortly. Before we do, take this opportunity to double check your key behavior. Is it uncomfortably specific? See the examples in this document to help you refine your thinking. https://docs.google.com/document/d/1EVoyhVnX_jTzNbWBrN8C9Bz8-A7xHO8wN5dM6o-Oves/edit#", thread_ts = event['ts'])

print(event)

        elif event['channel'] == 'C01972VPPJ5' and ('.pdf' in event['files'][0]['name'] or '.jpg' in event['files'][0]['name'] or '.png' in event['files'][0]['name'] or '.google' in event['text']) and (event['user'] != 'U038JV06ZMZ' and event['user'] != 'U03612ME0BY' and event['user'] != 'U029XD01PNF' and event['user'] != 'U038JV0HD19' and event['user'] != 'U03E7JSK3LG'):
            say(text = "Great work - you have now uploaded your Behavior Diagnosis! An IL scientist will connect with you shortly but before they do it's worth doing a quick double check … Did you group your steps into steps and substeps? See an example HERE. https://docs.google.com/presentation/d/1vqK8EIEYQfr2ZzVIf3YVjHL-w29eh8TfM62UbZHKMfg/edit#slide=id.g10907c59ee3_0_465",
            thread_ts = event['ts'])
            #print(event)
    else:
        if event['channel'] == "C019J946UPJ" and ('.google' in event['text']) and (event['user'] != 'U038JV06ZMZ' and event['user'] != 'U03612ME0BY' and event['user'] != 'U029XD01PNF' and event['user'] != 'U038JV0HD19' and event['user'] != 'U03E7JSK3LG'): #if statement clarifying channel to watch for & to not reply to replies
            say(text = "An IL scientist will connect with you shortly. Before we do, take this opportunity to double check your key behavior. Is it uncomfortably specific? See the examples in this document to help you refine your thinking. https://docs.google.com/document/d/1EVoyhVnX_jTzNbWBrN8C9Bz8-A7xHO8wN5dM6o-Oves/edit#",
            thread_ts = event['ts'])
            #print(event)
        elif event['channel'] == 'C01972VPPJ5' and ('.google' in event['text'] or 'miro' in event['text']) and (event['user'] != 'U038JV06ZMZ' and event['user'] != 'U03612ME0BY' and event['user'] != 'U029XD01PNF' and event['user'] != 'U038JV0HD19' and event['user'] != 'U03E7JSK3LG'): #if statement clarifying channel to watch for & to not reply to replies
            say(text = "Great work - you have now uploaded your Behavior Diagnosis! An IL scientist will connect with you shortly but before they do it's worth doing a quick double check … Did you group your steps into steps and substeps? See an example HERE. https://docs.google.com/presentation/d/1vqK8EIEYQfr2ZzVIf3YVjHL-w29eh8TfM62UbZHKMfg/edit#slide=id.g10907c59ee3_0_465",
            thread_ts = event['ts'])
            #print(event)
        elif event['channel'] == 'C018UDKTULA' and 'parent_user_id' not in str(event): #auto reply to meet & greet post
            say(text = "Sweet :lollipop: :chocolate_bar: :doughnut:! Thank you so much for sharing. \nIf you have a sec, take some time to find someone in the #meet_and_greet channel who works in the same industry or geographic area as you do. Say #hi and tell them what you have in common!",
            thread_ts = event['ts'])
            #print(event)

except Exception as e:
    logger.error(f"Error publishing home tab: {e}")


3. Use test channel in place of last "elif " statement in the "auto_reply" function. (elif event['channel'] == 'C018UDKTULA' and 'parent_user_id' not in str(event):)
4. Run code with requirements specified above. 

### Expected result:

The reply bot replies in the thread once with the automated message.
### Actual result:

The reply occurs successfully, but several hours later the bot posts a message in channel unprompted. I created a screenshot wireframe of what this looks like (I've been deleting them as they come along. If this doesn't convey the message well let me know and I can send over a real example).

![Screen Shot 2022-12-06 at 10 36 35 AM](https://user-images.githubusercontent.com/104223930/205956126-57ca9279-725f-4a68-8dea-c6e9cf2216a5.png)

(Tell what actually happened with logs, screenshots)

## Requirements

Please read the [Contributing guidelines](https://github.com/slackapi/bolt-python/blob/main/.github/contributing.md) and [Code of Conduct](https://slackhq.github.io/code-of-conduct) before creating this issue or pull request. By submitting, you are agreeing to those rules.
filmaj commented 1 year ago

Hi @irrational-labs ,

Are you able to provide more information around these retries?

Information like timestamps, or request IDs (if you set your app to provide debug level logging, you should be able to see this information). Does the bot post the same say message? I.e. is it following the same branch through your if/elif chain? Also, are you sure no other message or post to the relevant thread has triggered the event again?

irrational-labs commented 1 year ago

Hey @filmaj, I am not able to provide more info about these re-tries. If it's not too much to ask, could you provide documentation about how to set up debug-level logging? I just un-commented out a "print(event)" command under the elif statement that's triggering multiple times, so I'll reply if there are any weird payloads there after a dupe is sent. The bot does say the same "say" message that it said earlier. I'm positive no other message or post triggered it, the post will happen when there have been no new messages in the channel.

irrational-labs commented 1 year ago

I saw this response from a similar issue saying to remove and re-add the bot from the channel. I also realized printing events more that I wasn't filtering out events with a 'subtype' so I'm wondering if the bot was triggering responses when someone edited or deleted a message. I'll let you know if these changes work! https://github.com/microsoft/botframework-sdk/issues/3614

filmaj commented 1 year ago

If it's not too much to ask, could you provide documentation about how to set up debug-level logging?

Check out our documentation on logging; something like the following near the top of your app code should do it:

import logging
logging.basicConfig(level=logging.DEBUG)

I also realized printing events more that I wasn't filtering out events with a 'subtype' so I'm wondering if the bot was triggering responses when someone edited or deleted a message.

Ooooh this is a great theory! Definitely have a look at the available message subtypes, and, if there is a particular subtype that you could constrain your message event listener to, then that would limit the potential for getting seemingly-duplicate events that may be, sneakily, not actually duplicates. Check out our documentation on listening to events, and in particular, expand "Filtering on message subtypes" section in that documentation link. Related, if you enable debug logging (or, as you've done before, simply print out a dictionary representation of the full event payload), you should be able to inspect the subtype property on the event payload to see if maybe this property differs across these 'duplicate' events you are seeing.

irrational-labs commented 1 year ago

Thanks so much @filmaj! I will add logging now. Really appreciate all of your help 🙏 I'll keep you updated on what happens.

seratch commented 1 year ago

Hey @irrational-labs, another way to handle only new messages would be:

@app.event({"type": "message", "subtype": (None, "bot_message", "thread_broadcast", "file_share")})

This is the same with the way @app.message listeners receive only new messages: https://github.com/slackapi/bolt-python/blob/v1.15.5/slack_bolt/app/app.py#L771-L786

irrational-labs commented 1 year ago

Amazing, thank you @seratch! I'm astounded by how awesome the community in this repository is. I believe I fixed the issue and am going to close the ticket. @filmaj it does seem that filtering out deleted and edited messages (i did this by filtering out any event that contained 'subtype' in it) stopped the dupe issue. Really appreciate all of your help :)

filmaj commented 1 year ago

Glad to hear!