mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
client/#75 - Added Slack module
Added collector Added integrator
This commit is contained in:
parent
9d055e9adb
commit
841124e87c
7 changed files with 214 additions and 1 deletions
|
|
@ -39,6 +39,7 @@ from .deadline import DeadlineModule
|
|||
from .project_manager_action import ProjectManagerAction
|
||||
from .standalonepublish_action import StandAlonePublishAction
|
||||
from .sync_server import SyncServerModule
|
||||
from .slack import SlackIntegrationModule
|
||||
|
||||
|
||||
__all__ = (
|
||||
|
|
@ -77,5 +78,7 @@ __all__ = (
|
|||
"ProjectManagerAction",
|
||||
"StandAlonePublishAction",
|
||||
|
||||
"SyncServerModule"
|
||||
"SyncServerModule",
|
||||
|
||||
"SlackIntegrationModule"
|
||||
)
|
||||
|
|
|
|||
46
openpype/modules/slack/README.md
Normal file
46
openpype/modules/slack/README.md
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
Slack notification for publishing
|
||||
---------------------------------
|
||||
|
||||
This module allows configuring profiles(when to trigger, for which combination of task, host and family)
|
||||
and templates(could contain {} placeholder, as "{asset} published").
|
||||
|
||||
These need to be configured in
|
||||
```Project settings > Slack > Publish plugins > Notification to Slack```
|
||||
|
||||
Slack module must be enabled in System Setting, could be configured per Project.
|
||||
|
||||
## App installation
|
||||
|
||||
Slack app needs to be installed to company's workspace. Attached .yaml file could be
|
||||
used, follow instruction https://api.slack.com/reference/manifests#using
|
||||
|
||||
## Settings
|
||||
|
||||
### Token
|
||||
Most important for module to work is to fill authentication token
|
||||
```Project settings > Slack > Publish plugins > Token```
|
||||
|
||||
This token should be available after installation of app in Slack dashboard.
|
||||
It is possible to create multiple tokens and configure different scopes for them.
|
||||
|
||||
### Profiles
|
||||
Profiles are used to select when to trigger notification. One or multiple profiles
|
||||
could be configured, 'family', 'task name' (regex available) and host combination is needed.
|
||||
|
||||
Eg. If I want to be notified when render is published from Maya, setting is:
|
||||
|
||||
- family: 'render'
|
||||
- host: 'Maya'
|
||||
|
||||
### Channel
|
||||
Message could be delivered to one or multiple channels, by default app allows Slack bot
|
||||
to send messages to 'public' channels (eg. bot doesn't need to join channel first).
|
||||
|
||||
This could be configured in Slack dashboard and scopes might be modified.
|
||||
|
||||
### Message
|
||||
Placeholders {} could be used in message content which will be filled during runtime.
|
||||
Only keys available in 'anatomyData' are currently implemented.
|
||||
|
||||
Example of message content:
|
||||
```{SUBSET} for {Asset} was published.```
|
||||
7
openpype/modules/slack/__init__.py
Normal file
7
openpype/modules/slack/__init__.py
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
from .slack_module import (
|
||||
SlackIntegrationModule
|
||||
)
|
||||
|
||||
__all__ = (
|
||||
"SlackIntegrationModule"
|
||||
)
|
||||
22
openpype/modules/slack/manifest.yml
Normal file
22
openpype/modules/slack/manifest.yml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
_metadata:
|
||||
major_version: 1
|
||||
minor_version: 1
|
||||
display_information:
|
||||
name: OpenPypeNotifier
|
||||
features:
|
||||
app_home:
|
||||
home_tab_enabled: false
|
||||
messages_tab_enabled: true
|
||||
messages_tab_read_only_enabled: true
|
||||
bot_user:
|
||||
display_name: OpenPypeNotifier
|
||||
always_online: false
|
||||
oauth_config:
|
||||
scopes:
|
||||
bot:
|
||||
- chat:write
|
||||
- chat:write.public
|
||||
settings:
|
||||
org_deploy_enabled: false
|
||||
socket_mode_enabled: false
|
||||
is_hosted: false
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
from avalon import io
|
||||
import pyblish.api
|
||||
|
||||
from openpype.lib.profiles_filtering import filter_profiles
|
||||
|
||||
|
||||
class CollectSlackFamilies(pyblish.api.InstancePlugin):
|
||||
"""Collect family for Slack notification
|
||||
|
||||
Expects configured profile in
|
||||
Project settings > Slack > Publish plugins > Notification to Slack
|
||||
|
||||
Add Slack family to those instance that should be messaged to Slack
|
||||
"""
|
||||
order = pyblish.api.CollectorOrder + 0.4999
|
||||
label = 'Collect Slack family'
|
||||
|
||||
profiles = None
|
||||
|
||||
def process(self, instance):
|
||||
task_name = io.Session.get("AVALON_TASK")
|
||||
family = self.main_family_from_instance(instance)
|
||||
|
||||
key_values = {
|
||||
"families": family,
|
||||
"tasks": task_name,
|
||||
"hosts": instance.data["anatomyData"]["app"],
|
||||
}
|
||||
self.log.debug("key_values {}".format(key_values))
|
||||
profile = filter_profiles(self.profiles, key_values,
|
||||
logger=self.log)
|
||||
|
||||
# make slack publishable
|
||||
if profile:
|
||||
if instance.data.get('families'):
|
||||
instance.data['families'].append('slack')
|
||||
else:
|
||||
instance.data['families'] = ['slack']
|
||||
|
||||
instance.data["slack_channel"] = profile["channel"]
|
||||
instance.data["slack_message"] = profile["message"]
|
||||
|
||||
slack_token = (instance.context.data["project_settings"]
|
||||
["slack"]
|
||||
["publish"]
|
||||
["CollectSlackFamilies"]
|
||||
["token"])
|
||||
instance.data["slack_token"] = slack_token
|
||||
|
||||
def main_family_from_instance(self, instance): # TODO yank from integrate
|
||||
"""Returns main family of entered instance."""
|
||||
family = instance.data.get("family")
|
||||
if not family:
|
||||
family = instance.data["families"][0]
|
||||
return family
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
from slack_sdk import WebClient
|
||||
from slack_sdk.errors import SlackApiError
|
||||
|
||||
import pyblish.api
|
||||
from openpype.lib.plugin_tools import prepare_template_data
|
||||
|
||||
|
||||
class IntegrateSlackAPI(pyblish.api.InstancePlugin):
|
||||
""" Send message notification to a channel.
|
||||
|
||||
Triggers on instances with "slack" family, filled by
|
||||
'collect_slack_family'.
|
||||
|
||||
Expects configured profile in
|
||||
Project settings > Slack > Publish plugins > Notification to Slack
|
||||
|
||||
Message template can contain {} placeholders from anatomyData.
|
||||
"""
|
||||
order = pyblish.api.IntegratorOrder+0.499
|
||||
label = "Integrate Slack Api"
|
||||
families = ["slack"]
|
||||
|
||||
optional = True
|
||||
|
||||
def process(self, instance):
|
||||
message_templ = instance.data["slack_message"]
|
||||
|
||||
fill_pairs = set()
|
||||
for key, value in instance.data["anatomyData"].items():
|
||||
if not isinstance(value, str):
|
||||
continue
|
||||
fill_pairs.add((key, value))
|
||||
self.log.debug("fill_pairs:: {}".format(fill_pairs))
|
||||
|
||||
message = message_templ.format(**prepare_template_data(fill_pairs))
|
||||
|
||||
self.log.debug("message:: {}".format(message))
|
||||
if '{' in message:
|
||||
self.log.warning(
|
||||
"Missing values to fill message properly {}".format(message))
|
||||
|
||||
return
|
||||
|
||||
for channel in instance.data["slack_channel"]:
|
||||
try:
|
||||
client = WebClient(token=instance.data["slack_token"])
|
||||
_response = client.chat_postMessage(
|
||||
channel=channel,
|
||||
text=message
|
||||
)
|
||||
except SlackApiError as e:
|
||||
# You will get a SlackApiError if "ok" is False
|
||||
self.log.warning("Error happened {}".format(e.response[
|
||||
"error"]))
|
||||
26
openpype/modules/slack/slack_module.py
Normal file
26
openpype/modules/slack/slack_module.py
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import os
|
||||
from openpype.modules import (
|
||||
PypeModule, IPluginPaths)
|
||||
|
||||
|
||||
class SlackIntegrationModule(PypeModule, IPluginPaths):
|
||||
"""Allows sending notification to Slack channels during publishing."""
|
||||
|
||||
name = "slack"
|
||||
|
||||
def initialize(self, modules_settings):
|
||||
slack_settings = modules_settings[self.name]
|
||||
self.enabled = slack_settings["enabled"]
|
||||
|
||||
def connect_with_modules(self, _enabled_modules):
|
||||
"""Nothing special."""
|
||||
return
|
||||
|
||||
def get_plugin_paths(self):
|
||||
"""Deadline plugin paths."""
|
||||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
return {
|
||||
"publish": [os.path.join(current_dir, "plugins", "publish")]
|
||||
}
|
||||
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue