I might introduce a %NotificationWithContext{...} struct that contains all the data needed to check all those conditions. It would have keys such as channel_prefs, user_prefs, message_properties and so on. This way, testing whether or not to send a notification would be a pure function.
There would be a separate step to build that struct (fetch prefs, parse message to get mentions, etc).
So the high level code may look like
NotificationWithContext.build(user, message)
|> SlackNotificationStrategy.should_notify?






















