Posting an image to Slack#

The user experience at the beamline benefits a lot by having text messages regularly posted to Slack. Adding images makes that experience much richer. Being able to see the result of a measurement on Slack makes it much easier to walk away from the beamline and have confidence that things are proceeding apace.

Obtain an uploader token#

The process to enabling image posts is rather similar to posting text. Here is a handy page that I followed when I was setting this up: https://hamzaafridi.com/sending-a-file-to-a-slack-channel-using-api/

The interface is similar in that you need to establish a App and associate with a specific channel on a specific workspace. But then you need to use the Slack python interface to post a file. There is likely a way to stream an image directly to Slack, but I have implemented it as posting a file. The images I post to Slack are things I also write to disk and include in the data product that the user takes home from the beamline. Since I am writing files in any case, it seemed sensible to implement this as a file posting.

Code for posting an image#

Setting up the App results in being given a token for uploading files. In lines 4 to 10 of the code below, I am reading this token from the beamline NAS. This token is also a thing that must not be committed to the beamline Github repo. Again, keeping it on a NAS or on Lustre, while not actually secure, is adequate.

Here is the code for posting an image stored in a file to a Slack channel.

 1   ## Simple but useful guide to configuring a slack app:
 2   ## https://hamzaafridi.com/sending-a-file-to-a-slack-channel-using-api/
 3   def img_to_slack(imagefile):
 4       token_file = os.path.join(startup_dir, 'BMM', 'image_uploader_token')
 5       try:
 6           with open(token_file, "r") as f:
 7               token = f.read().replace('\n','')
 8       except:
 9           post_to_slack(f'failed to post image: {imagefile}')
10           return()
11       client = WebClient(token=token)
12       #client = WebClient(token=os.environ['SLACK_API_TOKEN'])
13       try:
14           response = client.files_upload(channels='#beamtime', file=imagefile)
15           assert response["file"]  # the uploaded file
16       except SlackApiError as e:
17           post_to_slack('failed to post image: {imagefile}')
18           # You will get a SlackApiError if "ok" is False
19           assert e.response["ok"] is False
20           assert e.response["error"]  # str like 'invalid_auth', 'channel_not_found'
21           print(f"Got an error: {e.response['error']}")
22       except Exception as em:
23           print("EXCEPTION: " + str(em))
24           print(f'failed to post image: {imagefile}')
25           post_to_slack(f'failed to post image: {imagefile}')

This can be used at the bsui command line or in a Bluesky plan like so:

from beamline_slack import img_to_slack
img_to_slack('/path/to/data/plot.png')

The file posted does not need to be a PNG image. You can post all kinds of files in this manner.

Here is what it looks like for BMM. At the end of a sequence of repetitions on the same sample, the data are merged and lightly reduced. Matplotlib is used to make a fun representation of the reduced data, which is saved to a PNG file. That PNG file is then posted to the Slack channel.

../_images/slack_image.png

Fig. 6 Posting images to Slack.#