Skip to content

Commit b85de97

Browse files
Merge pull request #71 from PortableProgrammer/dev
Better Slack Huddle/Call/custom status handling, linting
2 parents d39a4fb + b151185 commit b85de97

4 files changed

Lines changed: 51 additions & 22 deletions

File tree

README.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ services:
6161
- "SLACK_CUSTOM_AVAILABLE_STATUS=''"
6262
- "SLACK_CUSTOM_SCHEDULED_STATUS=':spiral_calendar_pad: In a meeting'"
6363
- "SLACK_CUSTOM_BUSY_STATUS=':headphones: In a huddle',':slack_call:',':no_entry_sign: Do not disturb'"
64-
- "SLACK_CUSTOM_OFF_STATUS=':no_entry: Out of office',':airplane:'"
64+
- "SLACK_CUSTOM_OFF_STATUS=':no_entry: Out of office',':airplane:',':palm_tree: Vacationing'"
6565
- "ACTIVE_DAYS=Monday,Tuesday,Wednesday,Thursday,Friday"
6666
- "ACTIVE_HOURS_START=08:00:00"
6767
- "ACTIVE_HOURS_END=17:00:00"
@@ -234,26 +234,26 @@ While Slack only offers the `active` and `inactive` presence flags, it also offe
234234
These options accept a list of strings that should match the beginning of the Slack custom status.
235235
Take the following scenario:
236236
```python
237-
SLACK_BUSY_STATUS = [':headphones: In a huddle', ':no_entry_sign: Do not disturb']
237+
SLACK_BUSY_STATUS = [':no_entry_sign: Do Not Disturb']
238238
BUSY_STATUS = [CALL, DONOTDISTURB, MEETING, PRESENTING, PENDING]
239239
AVAILABLE_STATUS = ACTIVE
240240
slack.Presence = ACTIVE
241241
slack.CustomStatus = ':no_entry_sign: Do not disturb, I need to finish project X today!'
242242
```
243-
In the example above, the Slack custom status would match, and therefore take precedence over the Slack presence, causing Status-Light to treat Slack as `BUSY` instead of `AVAILABLE`.
243+
In the example above, the Slack custom status would match (since it is a case-insensitive comparison), and therefore take precedence over the Slack presence, causing Status-Light to treat Slack as `BUSY` instead of `AVAILABLE`.
244244

245245
#### `SLACK_CUSTOM_AVAILABLE_STATUS`
246-
- *Optional*
246+
- *Optional*, case-insensitive
247247
- Default value: `''`
248248
- Slack's `active` presence lines up nicely with the default [`AVAILABLE_STATUS`](#availablestatus), so there is no default custom override for this option.
249249

250250
#### `SLACK_CUSTOM_SCHEDULED_STATUS`
251-
- *Optional*
251+
- *Optional*, case-insensitive
252252
- Default value: `':spiral_calendar_pad: In a meeting'`
253253
- If you have a calendaring source configured in Slack but not in Status-Light, this default [`SCHEDULED_STATUS`](#scheduledstatus) is an easy way to obtain both collaboration and calendar status from a single source. If you also have the same calendaring source configured in Status-Light, this will duplicate it, assuming that they're fully in sync.
254254

255255
#### `SLACK_CUSTOM_BUSY_STATUS`
256-
- *Optional*
256+
- *Optional*, case-insensitive
257257
- Default value: `':headphones: In a huddle',':slack_call:',':no_entry_sign: Do not disturb'`
258258
- This custom status allows Status-Light to recognize Slack A/V collaboration modes, like [Huddles](https://slack.com/help/articles/4402059015315-Use-huddles-in-Slack) and [Calls](https://slack.com/help/articles/216771908-Make-calls-in-Slack).
259259

@@ -262,8 +262,8 @@ In the example above, the Slack custom status would match, and therefore take pr
262262
**Note 2:** Slack, by default, will not automatically change your custom status when you join a Call or Huddle, if you already have one set. In this instance, Status-Light will react to your existing custom status and other Source statuses.
263263

264264
#### `SLACK_CUSTOM_OFF_STATUS`
265-
- *Optional*
266-
- Default value: `':no_entry: Out of office',':airplane:'`
265+
- *Optional*, case-insensitive
266+
- Default value: `':no_entry: Out of office',':airplane:',':palm_tree: vacationing'`
267267
- If you have a calendaring source configured in Slack but not in Status-Light, this default [`OFF_STATUS`](#offstatus) is an easy way to obtain both collaboration and calendar status from a single source. If you also have the same calendaring source configured in Status-Light, this will duplicate it, assuming that they're fully in sync.
268268

269269
---

status-light/sources/collaboration/slack.py

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,24 +81,40 @@ def _parse_custom_status(self, client:WebClient, default:enum.Status = enum.Sta
8181
try:
8282
# Get the latest user info
8383
user_info = self.get_user_info(client)
84+
85+
if not user_info['profile']:
86+
return enum.Status.UNKNOWN
87+
8488
# Join the emoji and text with a space
85-
custom_status = user_info['profile']['status_emoji'] + ' ' \
86-
+ user_info['profile']['status_text']
89+
custom_status = (user_info['profile']['status_emoji'] + ' ' \
90+
+ user_info['profile']['status_text']).casefold()
8791

8892
# For each of the Slack custom statuses, check them in reverse precedence order
8993
# Off, Available, Scheduled, Busy
9094
if self.custom_off_status and custom_status.startswith(tuple(self.custom_off_status)):
9195
return_value = self.custom_off_status_map
9296

93-
if self.custom_available_status and custom_status.startswith(tuple(self.custom_available_status)):
97+
if self.custom_available_status and \
98+
custom_status.startswith(tuple(self.custom_available_status)):
99+
94100
return_value = self.custom_available_status_map
95101

96-
if self.custom_scheduled_status and custom_status.startswith(tuple(self.custom_scheduled_status)):
102+
if self.custom_scheduled_status and \
103+
custom_status.startswith(tuple(self.custom_scheduled_status)):
104+
97105
return_value = self.custom_scheduled_status_map
98106

99-
if self.custom_busy_status and custom_status.startswith(tuple(self.custom_busy_status)):
107+
if self.custom_busy_status and \
108+
custom_status.startswith(tuple(self.custom_busy_status)):
109+
100110
return_value = self.custom_busy_status_map
101111

112+
# Check for Huddle and Call
113+
if user_info['profile']['huddle_state'] == 'in_a_huddle' or \
114+
user_info['profile']['status_emoji'] == ':slack_call:':
115+
116+
return_value = enum.Status.CALL
117+
102118
return return_value
103119
except (SystemExit, KeyboardInterrupt):
104120
pass

status-light/utility/env.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ class Environment:
3131
slack_user_id = None
3232
slack_bot_token = None
3333
# 66 - Add Slack custom status support
34-
slack_off_status = [':no_entry: Out of office',':airplane:']
34+
slack_off_status = [':no_entry: Out of Office',':airplane:',':palm_tree: Vacationing']
3535
slack_available_status = None
3636
slack_scheduled_status = ':spiral_calendar_pad: In a meeting'
37-
slack_busy_status = [':headphones: In a huddle',':slack_call:',':no_entry_sign: Do not disturb']
37+
slack_busy_status = [':no_entry_sign: Do not Disturb']
3838

3939
office_app_id = None
4040
office_app_secret = None
@@ -117,13 +117,13 @@ def get_slack(self):
117117
# NOTE: Since these are all optional, and at least one defaults to None,
118118
# they should not be checked in the return statement
119119
self.slack_available_status = util.parse_str_array(os.environ.get('SLACK_AVAILABLE_STATUS'),
120-
default=self.slack_available_status)
120+
default=self.slack_available_status, casefold=True)
121121
self.slack_busy_status = util.parse_str_array(os.environ.get('SLACK_BUSY_STATUS'),
122-
self.slack_busy_status)
122+
self.slack_busy_status, casefold=True)
123123
self.slack_off_status = util.parse_str_array(os.environ.get('SLACK_OFF_STATUS'),
124-
self.slack_off_status)
124+
self.slack_off_status, casefold=True)
125125
self.slack_scheduled_status = util.parse_str_array(os.environ.get('SLACK_SCHEDULED_STATUS'),
126-
self.slack_scheduled_status)
126+
self.slack_scheduled_status, casefold=True)
127127
return (None not in [self.slack_user_id, self.slack_bot_token])
128128

129129
def get_office(self):

status-light/utility/util.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,19 +111,32 @@ def parse_enum(value_string, value_enum:Enum, description, default, value_is_lis
111111
return temp_value
112112

113113
# 66 - Support Slack custom statuses
114-
def parse_str_array(value_string, default, delimiter:str = ','):
115-
"""Given a string containing an array of strings, attempts to parse the string into an array"""
114+
def parse_str_array(value_string, default, delimiter:str = ',', casefold:bool = False):
115+
"""Given a string containing an array of strings, attempts to parse the string into an array.
116+
Pass casefold=True to build an array ready for case-insensitive comparison."""
116117
temp_value = default
117118
if value_string in [None, '']:
118119
value_string = temp_value
119120

120121
try:
122+
# If we were passed None, and the default is None, just return None
123+
if not value_string:
124+
return None
125+
121126
# Ensure that we return a true list, since the incoming string
122127
# might have a single element only.
123-
if value_string and not isinstance(value_string, list):
128+
if not isinstance(value_string, list):
124129
temp_value = []
125130
for value in value_string.split(delimiter):
131+
if casefold:
132+
value = value.casefold()
126133
temp_value.append(value)
134+
else:
135+
if casefold:
136+
temp_value = []
137+
for value in value_string:
138+
temp_value.append(value.casefold())
139+
127140
except BaseException as ex: # pylint: disable=broad-except
128141
logger.warning('Exception while parsing a string array: %s', ex)
129142
temp_value = default

0 commit comments

Comments
 (0)