Some checks failed
Code scanning / CodeQL (push) Failing after 15s
Zulip production suite / Ubuntu 22.04 production build (push) Failing after 26s
Zulip production suite / Debian 12 production install with custom db name and user (push) Has been skipped
Zulip production suite / Ubuntu 22.04 production install and PostgreSQL upgrade with pgroonga (push) Has been skipped
Zulip production suite / Ubuntu 24.04 production install (push) Has been skipped
Zulip production suite / Ubuntu 26.04 production install (push) Has been skipped
Zulip production suite / Debian 13 production install (push) Has been skipped
Zulip production suite / 7.0 Version Upgrade (push) Has been skipped
Zulip production suite / 8.0 Version Upgrade (push) Has been skipped
Zulip production suite / 6.0 Version Upgrade (push) Has been skipped
Zulip production suite / 10.0 Version Upgrade (push) Has been skipped
Zulip production suite / 9.0 Version Upgrade (push) Has been skipped
Zulip production suite / 12.0 Version Upgrade (push) Has been skipped
Zulip production suite / 11.0 Version Upgrade (push) Has been skipped
GitHub Actions Security Analysis with zizmor / zizmor (push) Failing after 3s
Zulip CI / Debian 12 (Python 3.11, backend + documentation) (push) Failing after 26s
Zulip CI / Ubuntu 22.04 (Python 3.10, backend + frontend) (push) Failing after 3s
Zulip CI / Ubuntu 24.04 (Python 3.12, backend) (push) Failing after 25s
Zulip CI / Ubuntu 26.04 (Python 3.14, backend) (push) Failing after 25s
Zulip CI / Debian 13 (Python 3.13, backend) (push) Failing after 28s
Zulip production suite / Required jobs (push) Has been cancelled
Zulip CI / Required jobs (push) Has been cancelled
API Documentation Update Check / check-feature-level-updated (push) Has been cancelled
API Documentation Update Check / notify-if-api-docs-changed (push) Has been cancelled
1245 lines
43 KiB
Python
1245 lines
43 KiB
Python
import os
|
|
from collections.abc import Callable, Sequence
|
|
from dataclasses import dataclass, field
|
|
from itertools import chain, zip_longest
|
|
from typing import Any
|
|
|
|
from django.contrib.staticfiles.storage import staticfiles_storage
|
|
from django.http import HttpRequest, HttpResponseBase
|
|
from django.urls import URLPattern, path
|
|
from django.utils.module_loading import import_string
|
|
from django.views.decorators.csrf import csrf_exempt
|
|
from typing_extensions import override
|
|
|
|
from zerver.lib.storage import static_path
|
|
from zerver.lib.webhooks.common import PresetUrlOption, WebhookConfigOption, WebhookUrlOption
|
|
from zerver.webhooks import fixtureless_integrations
|
|
|
|
"""This module declares all of the (documented) integrations available
|
|
in the Zulip server.
|
|
|
|
The Integration class is used as part of generating the
|
|
documentation on the /integrations/ page, while the
|
|
IncomingWebhookIntegration class is also used to generate the URLs in
|
|
`zproject/urls.py` for incoming webhook integrations.
|
|
|
|
To add a new integration, register it in the appropriate *_INTEGRATIONS
|
|
list. For example, to add a new incoming webhook integration, declare a
|
|
IncomingWebhookIntegration in the INCOMING_WEBHOOK_INTEGRATIONS list. All
|
|
*_INTEGRATIONS lists are automatically aggregated into the INTEGRATIONS dict.
|
|
|
|
To add a new integration category, add it to the CATEGORIES dict below.
|
|
|
|
Over time, we expect this registry to grow additional convenience
|
|
features for writing and configuring integrations efficiently.
|
|
"""
|
|
|
|
CATEGORIES: dict[str, str] = {
|
|
"meta-integration": "Integration frameworks",
|
|
"bots": "Interactive bots",
|
|
"video-calling": "Video calling",
|
|
"continuous-integration": "Continuous integration",
|
|
"customer-support": "Customer support",
|
|
"deployment": "Deployment",
|
|
"entertainment": "Entertainment",
|
|
"communication": "Communication",
|
|
"financial": "Financial",
|
|
"hr": "Human resources",
|
|
"marketing": "Marketing",
|
|
"misc": "Miscellaneous",
|
|
"monitoring": "Monitoring",
|
|
"project-management": "Project management",
|
|
"productivity": "Productivity",
|
|
"version-control": "Version control",
|
|
}
|
|
|
|
# Can also be computed from INTEGRATIONS by removing entries from
|
|
# INCOMING_WEBHOOK_INTEGRATIONS and NO_SCREENSHOT_CONFIG, but defined explicitly to
|
|
# avoid circular dependency
|
|
FIXTURELESS_INTEGRATIONS_WITH_SCREENSHOTS: list[str] = [
|
|
"asana",
|
|
"capistrano",
|
|
"codebase",
|
|
"discourse",
|
|
"git",
|
|
"github-actions",
|
|
"google-calendar",
|
|
"jenkins",
|
|
"mastodon",
|
|
"mercurial",
|
|
"nagios",
|
|
"notion_via_zapier",
|
|
"openshift",
|
|
"perforce",
|
|
"puppet",
|
|
"rss",
|
|
"svn",
|
|
"trac",
|
|
"errbot",
|
|
"github_detail",
|
|
"hubot",
|
|
"irc",
|
|
"matrix",
|
|
"xkcd",
|
|
]
|
|
FIXTURELESS_SCREENSHOT_CONTENT: dict[str, list[fixtureless_integrations.ScreenshotContent]] = {
|
|
key: [getattr(fixtureless_integrations, key.upper().replace("-", "_"))]
|
|
for key in FIXTURELESS_INTEGRATIONS_WITH_SCREENSHOTS
|
|
}
|
|
|
|
|
|
@dataclass
|
|
class WebhookScreenshotConfig:
|
|
fixture_name: str
|
|
image_name: str = "001.png"
|
|
image_dir: str | None = None
|
|
bot_name: str | None = None
|
|
channel: str | None = None
|
|
payload_as_query_param: bool = False
|
|
payload_param_name: str = "payload"
|
|
extra_params: dict[str, str] = field(default_factory=dict)
|
|
use_basic_auth: bool = False
|
|
custom_headers: dict[str, str] = field(default_factory=dict)
|
|
|
|
|
|
@dataclass
|
|
class FixturelessScreenshotConfigOptions:
|
|
# These configured values for individual integrations are written
|
|
# over an object with defaults for content and topic to construct
|
|
# a FixturelessScreenshotConfig.
|
|
channel: str | None = None
|
|
image_name: str = "001.png"
|
|
image_dir: str | None = None
|
|
bot_name: str | None = None
|
|
|
|
|
|
@dataclass
|
|
class FixturelessScreenshotConfig:
|
|
message: str | list[fixtureless_integrations.MessageThread]
|
|
topic: str
|
|
channel: str | None = None
|
|
image_name: str = "001.png"
|
|
image_dir: str | None = None
|
|
bot_name: str | None = None
|
|
|
|
|
|
def get_screenshot_configs(
|
|
integration_name: str,
|
|
screenshot_config_options: list[FixturelessScreenshotConfigOptions] | None,
|
|
) -> list[FixturelessScreenshotConfig] | None:
|
|
screenshot_contents = FIXTURELESS_SCREENSHOT_CONTENT.get(integration_name, [])
|
|
if not screenshot_contents:
|
|
return None # nocoverage
|
|
return [
|
|
FixturelessScreenshotConfig(
|
|
screenshot_content["content"],
|
|
screenshot_content["topic"],
|
|
**vars(screenshot_config_option) if screenshot_config_option else {},
|
|
)
|
|
for screenshot_content, screenshot_config_option in zip_longest(
|
|
screenshot_contents, screenshot_config_options or []
|
|
)
|
|
]
|
|
|
|
|
|
class Integration:
|
|
DEFAULT_LOGO_STATIC_PATH_PNG = "images/integrations/logos/{name}.png"
|
|
DEFAULT_LOGO_STATIC_PATH_SVG = "images/integrations/logos/{name}.svg"
|
|
DEFAULT_BOT_AVATAR_PATH = "images/integrations/bot_avatars/{name}.png"
|
|
DEFAULT_DOC_PATH = "zerver/integrations/{name}.md"
|
|
|
|
def __init__(
|
|
self,
|
|
name: str,
|
|
categories: list[str],
|
|
fixtureless_screenshot_config_options: list[FixturelessScreenshotConfigOptions]
|
|
| None = None,
|
|
webhook_screenshot_configs: list[WebhookScreenshotConfig] | None = None,
|
|
client_name: str | None = None,
|
|
logo: str | None = None,
|
|
fallback_logo_path: str | None = None,
|
|
secondary_line_text: str | None = None,
|
|
display_name: str | None = None,
|
|
doc: str | None = None,
|
|
legacy: bool = False,
|
|
legacy_names: list[str] | None = None,
|
|
config_options: Sequence[WebhookConfigOption] = [],
|
|
url_options: Sequence[WebhookUrlOption] = [],
|
|
) -> None:
|
|
self.name = name
|
|
self.client_name = client_name if client_name is not None else name
|
|
self.secondary_line_text = secondary_line_text
|
|
self.legacy = legacy
|
|
self.doc = doc
|
|
self.url_options = url_options
|
|
|
|
self.screenshot_configs: (
|
|
list[WebhookScreenshotConfig] | list[FixturelessScreenshotConfig] | None
|
|
) = None
|
|
if webhook_screenshot_configs is not None:
|
|
self.screenshot_configs = webhook_screenshot_configs
|
|
elif self.name in FIXTURELESS_INTEGRATIONS_WITH_SCREENSHOTS:
|
|
self.screenshot_configs = get_screenshot_configs(
|
|
name, fixtureless_screenshot_config_options
|
|
)
|
|
|
|
# Note: Currently only incoming webhook type bots use this list for
|
|
# defining how the bot's BotConfigData should be. Embedded bots follow
|
|
# a different approach.
|
|
self.config_options = config_options
|
|
|
|
for category in categories:
|
|
if category not in CATEGORIES:
|
|
raise KeyError( # nocoverage
|
|
"INTEGRATIONS: "
|
|
+ name
|
|
+ " - category '"
|
|
+ category
|
|
+ "' is not a key in CATEGORIES.",
|
|
)
|
|
self.categories = [CATEGORIES[c] for c in categories]
|
|
|
|
self.logo_path = logo if logo is not None else self.get_logo_path(fallback_logo_path)
|
|
self.logo_url = staticfiles_storage.url(self.logo_path)
|
|
|
|
if display_name is None:
|
|
display_name = name.title()
|
|
self.display_name = display_name
|
|
|
|
if doc is None:
|
|
doc = self.DEFAULT_DOC_PATH.format(name=self.name)
|
|
self.doc = doc
|
|
|
|
def is_enabled_in_catalog(self) -> bool:
|
|
return self.name not in (
|
|
# Integrations being incrementally added
|
|
"intercom",
|
|
"notion",
|
|
# Broken integrations awaiting fixes
|
|
"hubot",
|
|
)
|
|
|
|
def get_logo_path(self, fallback_logo_path: str | None = None) -> str:
|
|
paths_to_check = [
|
|
self.DEFAULT_LOGO_STATIC_PATH_SVG.format(name=self.name),
|
|
self.DEFAULT_LOGO_STATIC_PATH_PNG.format(name=self.name),
|
|
]
|
|
if fallback_logo_path is not None:
|
|
paths_to_check.append(fallback_logo_path)
|
|
|
|
for potential_path in paths_to_check:
|
|
if os.path.isfile(static_path(potential_path)):
|
|
return potential_path
|
|
|
|
raise AssertionError(
|
|
f"Could not find a logo for integration {self.name}. Paths checked: {', '.join(paths_to_check)}"
|
|
)
|
|
|
|
def get_bot_avatar_path(self) -> str:
|
|
name = os.path.splitext(os.path.basename(self.logo_path))[0]
|
|
return self.DEFAULT_BOT_AVATAR_PATH.format(name=name)
|
|
|
|
|
|
class BotIntegration(Integration):
|
|
DEFAULT_LOGO_STATIC_PATH_PNG = "generated/bots/{name}/logo.png"
|
|
DEFAULT_LOGO_STATIC_PATH_SVG = "generated/bots/{name}/logo.svg"
|
|
ZULIP_LOGO_STATIC_PATH_PNG = "images/logo/zulip-icon-128x128.png"
|
|
DEFAULT_DOC_PATH = "{name}/doc.md"
|
|
|
|
def __init__(
|
|
self,
|
|
name: str,
|
|
categories: list[str],
|
|
fixtureless_screenshot_config_options: list[FixturelessScreenshotConfigOptions]
|
|
| None = None,
|
|
logo: str | None = None,
|
|
secondary_line_text: str | None = None,
|
|
display_name: str | None = None,
|
|
doc: str | None = None,
|
|
) -> None:
|
|
super().__init__(
|
|
name,
|
|
client_name=name,
|
|
categories=categories,
|
|
fixtureless_screenshot_config_options=fixtureless_screenshot_config_options,
|
|
secondary_line_text=secondary_line_text,
|
|
logo=logo,
|
|
fallback_logo_path=self.ZULIP_LOGO_STATIC_PATH_PNG,
|
|
)
|
|
|
|
if display_name is None:
|
|
display_name = f"{name.title()} Bot" # nocoverage
|
|
else:
|
|
display_name = f"{display_name} Bot"
|
|
self.display_name = display_name
|
|
|
|
if doc is None:
|
|
doc = self.DEFAULT_DOC_PATH.format(name=name)
|
|
self.doc = doc
|
|
|
|
|
|
class PythonAPIIntegration(Integration):
|
|
DEFAULT_DOC_PATH = "{directory_name}/doc.md"
|
|
|
|
def __init__(
|
|
self,
|
|
name: str,
|
|
categories: list[str],
|
|
fixtureless_screenshot_config_options: list[FixturelessScreenshotConfigOptions]
|
|
| None = None,
|
|
client_name: str | None = None,
|
|
logo: str | None = None,
|
|
secondary_line_text: str | None = None,
|
|
display_name: str | None = None,
|
|
directory_name: str | None = None,
|
|
doc: str | None = None,
|
|
legacy: bool = False,
|
|
) -> None:
|
|
if directory_name is None:
|
|
directory_name = name
|
|
self.directory_name = directory_name
|
|
|
|
# Assign before super(), to use self.directory_name instead of self.name
|
|
if doc is None:
|
|
doc = self.DEFAULT_DOC_PATH.format(directory_name=self.directory_name)
|
|
self.doc = doc
|
|
|
|
super().__init__(
|
|
name,
|
|
categories,
|
|
fixtureless_screenshot_config_options=fixtureless_screenshot_config_options,
|
|
client_name=client_name,
|
|
logo=logo,
|
|
secondary_line_text=secondary_line_text,
|
|
display_name=display_name,
|
|
doc=doc,
|
|
legacy=legacy,
|
|
)
|
|
|
|
|
|
class IncomingWebhookIntegration(Integration):
|
|
DEFAULT_FUNCTION_PATH = "zerver.webhooks.{dir_name}.view.api_{dir_name}_webhook"
|
|
DEFAULT_URL = "api/v1/external/{name}"
|
|
DEFAULT_CLIENT_NAME = "Zulip{name}Webhook"
|
|
DEFAULT_DOC_PATH = "{name}/doc.md"
|
|
|
|
def __init__(
|
|
self,
|
|
name: str,
|
|
categories: list[str],
|
|
webhook_screenshot_configs: list[WebhookScreenshotConfig] | None = None,
|
|
client_name: str | None = None,
|
|
logo: str | None = None,
|
|
secondary_line_text: str | None = None,
|
|
function: str | None = None,
|
|
url: str | None = None,
|
|
display_name: str | None = None,
|
|
doc: str | None = None,
|
|
legacy: bool = False,
|
|
legacy_names: list[str] | None = None,
|
|
config_options: Sequence[WebhookConfigOption] = [],
|
|
url_options: Sequence[WebhookUrlOption] = [],
|
|
dir_name: str | None = None,
|
|
) -> None:
|
|
if client_name is None:
|
|
client_name = self.DEFAULT_CLIENT_NAME.format(name=name.title())
|
|
super().__init__(
|
|
name,
|
|
categories,
|
|
webhook_screenshot_configs=webhook_screenshot_configs,
|
|
client_name=client_name,
|
|
logo=logo,
|
|
secondary_line_text=secondary_line_text,
|
|
display_name=display_name,
|
|
legacy=legacy,
|
|
config_options=config_options,
|
|
url_options=url_options,
|
|
)
|
|
|
|
if dir_name is None:
|
|
dir_name = self.name
|
|
self.dir_name = dir_name
|
|
|
|
if function is None:
|
|
function = self.DEFAULT_FUNCTION_PATH.format(dir_name=dir_name)
|
|
self.function_name = function
|
|
|
|
if url is None:
|
|
url = self.DEFAULT_URL.format(name=name)
|
|
self.url = url
|
|
|
|
self.legacy_names = legacy_names or []
|
|
self.urls = [url]
|
|
if legacy_names is not None:
|
|
for legacy_name in legacy_names:
|
|
self.urls.append(self.DEFAULT_URL.format(name=legacy_name))
|
|
|
|
if doc is None:
|
|
doc = self.DEFAULT_DOC_PATH.format(name=name)
|
|
self.doc = doc
|
|
|
|
def get_function(self) -> Callable[[HttpRequest], HttpResponseBase]:
|
|
return import_string(self.function_name)
|
|
|
|
@csrf_exempt
|
|
def view(self, request: HttpRequest) -> HttpResponseBase:
|
|
# Lazily load the real view function to improve startup performance.
|
|
function = self.get_function()
|
|
assert function.csrf_exempt # type: ignore[attr-defined] # ensure the above @csrf_exempt is justified
|
|
return function(request)
|
|
|
|
@property
|
|
def url_objects(self) -> list[URLPattern]:
|
|
return [path(url, self.view) for url in self.urls]
|
|
|
|
|
|
def split_fixture_path(path: str) -> tuple[str, str]:
|
|
path, fixture_name = os.path.split(path)
|
|
fixture_name, _ = os.path.splitext(fixture_name)
|
|
integration_name = os.path.split(os.path.dirname(path))[-1]
|
|
return integration_name, fixture_name
|
|
|
|
|
|
def get_fixture_path(
|
|
integration: IncomingWebhookIntegration, screenshot_config: WebhookScreenshotConfig
|
|
) -> str:
|
|
fixture_dir = os.path.join("zerver", "webhooks", integration.dir_name, "fixtures")
|
|
fixture_path = os.path.join(fixture_dir, screenshot_config.fixture_name)
|
|
return fixture_path
|
|
|
|
|
|
def get_image_path(
|
|
integration: Integration,
|
|
screenshot_config: WebhookScreenshotConfig | FixturelessScreenshotConfig,
|
|
) -> str:
|
|
image_dir = screenshot_config.image_dir or integration.name
|
|
image_name = screenshot_config.image_name
|
|
image_path = os.path.join("static/images/integrations", image_dir, image_name)
|
|
return image_path
|
|
|
|
|
|
class HubotIntegration(Integration):
|
|
GIT_URL_TEMPLATE = "https://github.com/hubot-archive/hubot-{}"
|
|
SECONDARY_LINE_TEXT = "(Hubot script)"
|
|
DOC_PATH = "zerver/integrations/hubot_common.md"
|
|
|
|
def __init__(
|
|
self,
|
|
name: str,
|
|
categories: list[str],
|
|
fixtureless_screenshot_config_options: list[FixturelessScreenshotConfigOptions]
|
|
| None = None,
|
|
display_name: str | None = None,
|
|
logo: str | None = None,
|
|
git_url: str | None = None,
|
|
# Hide all integrations available via Hubot from the catalog until
|
|
# the Hubot integration (https://github.com/zulip/hubot-zulip)
|
|
# becomes functional again.
|
|
legacy: bool = True,
|
|
) -> None:
|
|
if git_url is None:
|
|
git_url = self.GIT_URL_TEMPLATE.format(name)
|
|
self.hubot_docs_url = git_url
|
|
|
|
super().__init__(
|
|
name,
|
|
categories,
|
|
fixtureless_screenshot_config_options=fixtureless_screenshot_config_options,
|
|
logo=logo,
|
|
secondary_line_text=self.SECONDARY_LINE_TEXT,
|
|
display_name=display_name,
|
|
doc=self.DOC_PATH,
|
|
legacy=legacy,
|
|
)
|
|
|
|
|
|
class EmbeddedBotIntegration(Integration):
|
|
"""
|
|
This class acts as a registry for bots verified as safe
|
|
and valid such that these are capable of being deployed on the server.
|
|
"""
|
|
|
|
DEFAULT_CLIENT_NAME = "Zulip{name}EmbeddedBot"
|
|
ZULIP_LOGO_STATIC_PATH_PNG = "images/logo/zulip-icon-128x128.png"
|
|
|
|
def __init__(self, name: str, *args: Any, **kwargs: Any) -> None:
|
|
assert kwargs.get("client_name") is None
|
|
kwargs["client_name"] = self.DEFAULT_CLIENT_NAME.format(name=name.title())
|
|
kwargs["fallback_logo_path"] = self.ZULIP_LOGO_STATIC_PATH_PNG
|
|
super().__init__(name, *args, **kwargs)
|
|
|
|
@override
|
|
def is_enabled_in_catalog(self) -> bool:
|
|
# Only integrations with docs can be part of the catalog
|
|
# Embedded bots do not have docs
|
|
return False
|
|
|
|
|
|
EMBEDDED_BOTS: list[EmbeddedBotIntegration] = [
|
|
EmbeddedBotIntegration("converter", []),
|
|
EmbeddedBotIntegration("encrypt", []),
|
|
EmbeddedBotIntegration("helloworld", []),
|
|
EmbeddedBotIntegration("virtual_fs", []),
|
|
EmbeddedBotIntegration("giphy", []),
|
|
EmbeddedBotIntegration("followup", []),
|
|
]
|
|
|
|
INCOMING_WEBHOOK_INTEGRATIONS: list[IncomingWebhookIntegration] = [
|
|
IncomingWebhookIntegration(
|
|
"airbrake", ["monitoring"], [WebhookScreenshotConfig("error_message.json")]
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"airbyte", ["deployment"], [WebhookScreenshotConfig("airbyte_job_payload_success.json")]
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"alertmanager",
|
|
["monitoring"],
|
|
[
|
|
WebhookScreenshotConfig(
|
|
"alert.json", extra_params={"name": "topic", "desc": "description"}
|
|
)
|
|
],
|
|
display_name="Prometheus Alertmanager",
|
|
logo="images/integrations/logos/prometheus.svg",
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"ansibletower",
|
|
["deployment"],
|
|
[WebhookScreenshotConfig("job_successful_multiple_hosts.json")],
|
|
display_name="Ansible Tower",
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"appfollow",
|
|
["marketing", "customer-support"],
|
|
[WebhookScreenshotConfig("review.json")],
|
|
display_name="AppFollow",
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"appveyor",
|
|
["continuous-integration"],
|
|
[WebhookScreenshotConfig("appveyor_build_success.json")],
|
|
display_name="AppVeyor",
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"azuredevops",
|
|
["continuous-integration"],
|
|
[WebhookScreenshotConfig("code_push.json")],
|
|
display_name="AzureDevOps",
|
|
url_options=[WebhookUrlOption.build_preset_config(PresetUrlOption.BRANCHES)],
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"basecamp", ["project-management"], [WebhookScreenshotConfig("doc_active.json")]
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"beanstalk",
|
|
["version-control"],
|
|
[
|
|
WebhookScreenshotConfig(
|
|
"git_multiple.json",
|
|
channel="commits",
|
|
use_basic_auth=True,
|
|
payload_as_query_param=True,
|
|
)
|
|
],
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"beeminder",
|
|
["productivity"],
|
|
# The fixture's goal.losedate needs to be modified dynamically
|
|
# before uncommenting the screenshot config below.
|
|
# [WebhookScreenshotConfig("derail_worried.json")],
|
|
display_name="Beeminder",
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"bitbucket",
|
|
["version-control"],
|
|
[WebhookScreenshotConfig("issue_created.json", channel="commits")],
|
|
url_options=[WebhookUrlOption.build_preset_config(PresetUrlOption.BRANCHES)],
|
|
legacy_names=["bitbucket2"],
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"bitbucketdatacenter",
|
|
["version-control"],
|
|
[
|
|
WebhookScreenshotConfig(
|
|
"repo_push_update_single_branch.json",
|
|
bot_name="Bitbucket Data Center Bot",
|
|
channel="commits",
|
|
)
|
|
],
|
|
logo="images/integrations/logos/bitbucket.svg",
|
|
display_name="Bitbucket Data Center",
|
|
url_options=[WebhookUrlOption.build_preset_config(PresetUrlOption.BRANCHES)],
|
|
legacy_names=["bitbucket3"],
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"buildbot", ["continuous-integration"], [WebhookScreenshotConfig("started.json")]
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"canarytoken",
|
|
["monitoring"],
|
|
[WebhookScreenshotConfig("canarytoken_real.json")],
|
|
display_name="Thinkst Canarytokens",
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"circleci",
|
|
["continuous-integration"],
|
|
[WebhookScreenshotConfig("github_job_completed.json")],
|
|
display_name="CircleCI",
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"codeship",
|
|
["continuous-integration", "deployment"],
|
|
[WebhookScreenshotConfig("error_build.json")],
|
|
# TODO: Delete integration in 2027. Reached EOL Jan 2026.
|
|
# Compare payload format similarity with its replacement
|
|
# CloudBees Unify to consider conversion instead of deletion.
|
|
legacy=True,
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"crashlytics", ["monitoring"], [WebhookScreenshotConfig("issue_message.json")]
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"dbt",
|
|
["deployment"],
|
|
[WebhookScreenshotConfig("job_run_completed_errored.json")],
|
|
display_name="dbt",
|
|
url_options=[
|
|
WebhookUrlOption(name="access_url", label="dbt Access URL", input_type="text")
|
|
],
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"delighted",
|
|
["customer-support", "marketing"],
|
|
[WebhookScreenshotConfig("survey_response_updated_promoter.json")],
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"dialogflow",
|
|
["customer-support"],
|
|
[WebhookScreenshotConfig("weather_app.json", extra_params={"email": "iago@zulip.com"})],
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"dropbox", ["productivity"], [WebhookScreenshotConfig("file_updated.json")]
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"dropboxsign",
|
|
["productivity", "hr"],
|
|
[
|
|
WebhookScreenshotConfig(
|
|
"signatures_signed_by_one_signatory.json",
|
|
payload_as_query_param=True,
|
|
payload_param_name="json",
|
|
)
|
|
],
|
|
display_name="Dropbox Sign",
|
|
legacy_names=["hellosign"],
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"errbit", ["monitoring"], [WebhookScreenshotConfig("error_message.json")]
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"flock", ["communication"], [WebhookScreenshotConfig("messages.json")]
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"freshdesk",
|
|
["customer-support"],
|
|
[WebhookScreenshotConfig("ticket_created.json", image_name="004.png", use_basic_auth=True)],
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"freshping", ["monitoring"], [WebhookScreenshotConfig("freshping_check_unreachable.json")]
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"freshstatus",
|
|
["monitoring", "customer-support"],
|
|
[WebhookScreenshotConfig("freshstatus_incident_open.json")],
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"front",
|
|
["customer-support", "communication"],
|
|
[WebhookScreenshotConfig("inbound_message.json")],
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"gitea",
|
|
["version-control"],
|
|
[WebhookScreenshotConfig("pull_request__merged.json", channel="commits")],
|
|
url_options=[WebhookUrlOption.build_preset_config(PresetUrlOption.BRANCHES)],
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"github",
|
|
["version-control", "continuous-integration", "project-management"],
|
|
[WebhookScreenshotConfig("push__1_commit.json", channel="commits")],
|
|
display_name="GitHub",
|
|
url_options=[
|
|
WebhookUrlOption.build_preset_config(PresetUrlOption.BRANCHES),
|
|
WebhookUrlOption.build_preset_config(PresetUrlOption.IGNORE_PRIVATE_REPOSITORIES),
|
|
WebhookUrlOption(
|
|
name="include_repository_name",
|
|
label="Include repository name in the notifications",
|
|
input_type="checkbox",
|
|
),
|
|
WebhookUrlOption(
|
|
name="include_emoji_indicators",
|
|
label="Include emoji indicators in the notifications",
|
|
input_type="checkbox_enabled",
|
|
),
|
|
],
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"githubsponsors",
|
|
["financial"],
|
|
[WebhookScreenshotConfig("created.json", channel="github")],
|
|
display_name="GitHub Sponsors",
|
|
logo="images/integrations/logos/github.svg",
|
|
dir_name="github",
|
|
doc="github/githubsponsors.md",
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"gitlab",
|
|
["version-control"],
|
|
[
|
|
WebhookScreenshotConfig(
|
|
"push_hook__push_local_branch_without_commits.json", channel="commits"
|
|
)
|
|
],
|
|
display_name="GitLab",
|
|
url_options=[
|
|
WebhookUrlOption.build_preset_config(PresetUrlOption.BRANCHES),
|
|
WebhookUrlOption(
|
|
name="ignore_private_projects",
|
|
label="Exclude notifications from private projects",
|
|
input_type="checkbox",
|
|
),
|
|
WebhookUrlOption(
|
|
name="use_merge_request_title",
|
|
label="Include merge request titles in topics",
|
|
input_type="checkbox_enabled",
|
|
),
|
|
],
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"gocd",
|
|
["continuous-integration"],
|
|
[WebhookScreenshotConfig("pipeline_with_mixed_job_result.json")],
|
|
display_name="GoCD",
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"gogs",
|
|
["version-control"],
|
|
[WebhookScreenshotConfig("pull_request__opened.json", channel="commits")],
|
|
url_options=[WebhookUrlOption.build_preset_config(PresetUrlOption.BRANCHES)],
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"gong",
|
|
["customer-support", "marketing"],
|
|
[WebhookScreenshotConfig("call_completed.json")],
|
|
url_options=[
|
|
WebhookUrlOption(
|
|
"include_trackers",
|
|
label="Include primary trackers in the notifications",
|
|
input_type="checkbox_enabled",
|
|
),
|
|
WebhookUrlOption(
|
|
"include_topics",
|
|
label="Include primary topics in the notifications",
|
|
input_type="checkbox_enabled",
|
|
),
|
|
WebhookUrlOption(
|
|
"include_participants",
|
|
label="Include participant names in the notifications",
|
|
input_type="checkbox_enabled",
|
|
),
|
|
WebhookUrlOption(
|
|
"include_participant_contacts",
|
|
label="Include participant contact information (requires participant names)",
|
|
input_type="checkbox_enabled",
|
|
),
|
|
WebhookUrlOption(
|
|
"include_public_comments",
|
|
label="Include public comments in the notifications",
|
|
input_type="checkbox",
|
|
),
|
|
],
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"gosquared",
|
|
["marketing"],
|
|
[WebhookScreenshotConfig("traffic_spike.json")],
|
|
display_name="GoSquared",
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"grafana", ["monitoring"], [WebhookScreenshotConfig("alert_values_v11.json")]
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"greenhouse", ["hr"], [WebhookScreenshotConfig("candidate_stage_change.json")]
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"groove", ["customer-support"], [WebhookScreenshotConfig("ticket_started.json")]
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"harbor",
|
|
["deployment"],
|
|
[WebhookScreenshotConfig("scanning_completed.json")],
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"helloworld", ["misc"], [WebhookScreenshotConfig("hello.json")], display_name="Hello World"
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"heroku", ["deployment"], [WebhookScreenshotConfig("build_create.json")]
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"homeassistant",
|
|
["misc"],
|
|
[WebhookScreenshotConfig("reqwithtitle.json")],
|
|
display_name="Home Assistant",
|
|
),
|
|
IncomingWebhookIntegration("ifttt", ["meta-integration"], display_name="IFTTT"),
|
|
IncomingWebhookIntegration("intercom", ["customer-support"]),
|
|
IncomingWebhookIntegration(
|
|
"jira",
|
|
["project-management"],
|
|
[WebhookScreenshotConfig("issue_created_with_assignee.json")],
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"jotform", ["productivity"], [WebhookScreenshotConfig("screenshot_response.multipart")]
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"json",
|
|
["misc"],
|
|
[WebhookScreenshotConfig("json_github_push__1_commit.json")],
|
|
display_name="JSON formatter",
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"librato",
|
|
["monitoring"],
|
|
[WebhookScreenshotConfig("three_conditions_alert.json", payload_as_query_param=True)],
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"lidarr", ["entertainment"], [WebhookScreenshotConfig("lidarr_album_grabbed.json")]
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"linear", ["project-management"], [WebhookScreenshotConfig("issue_create_complex.json")]
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"mention", ["marketing"], [WebhookScreenshotConfig("webfeeds.json")]
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"netlify",
|
|
["deployment", "continuous-integration"],
|
|
[WebhookScreenshotConfig("deploy_building.json")],
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"newrelic",
|
|
["monitoring"],
|
|
[WebhookScreenshotConfig("incident_activated_new_default_payload.json")],
|
|
display_name="New Relic",
|
|
),
|
|
IncomingWebhookIntegration("notion", ["productivity", "project-management"]),
|
|
IncomingWebhookIntegration(
|
|
"opencollective",
|
|
["financial"],
|
|
[WebhookScreenshotConfig("one_time_donation.json")],
|
|
display_name="Open Collective",
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"openproject",
|
|
["project-management"],
|
|
[WebhookScreenshotConfig("project_created__without_parent.json")],
|
|
display_name="OpenProject",
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"opensearch",
|
|
["monitoring"],
|
|
[WebhookScreenshotConfig("example_template.txt")],
|
|
display_name="OpenSearch",
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"opsgenie",
|
|
["monitoring"],
|
|
[WebhookScreenshotConfig("addrecipient.json")],
|
|
url_options=[
|
|
WebhookUrlOption(
|
|
name="eu_region",
|
|
label="Use Opsgenie's European service region",
|
|
input_type="checkbox",
|
|
)
|
|
],
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"pagerduty",
|
|
["monitoring"],
|
|
[WebhookScreenshotConfig("trigger_v2.json")],
|
|
display_name="PagerDuty",
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"papertrail",
|
|
["monitoring"],
|
|
[WebhookScreenshotConfig("short_post.json", payload_as_query_param=True)],
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"patreon", ["financial"], [WebhookScreenshotConfig("members_pledge_create.json")]
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"pingdom", ["monitoring"], [WebhookScreenshotConfig("http_up_to_down.json")]
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"radarr", ["entertainment"], [WebhookScreenshotConfig("radarr_movie_grabbed.json")]
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"raygun", ["monitoring"], [WebhookScreenshotConfig("new_error.json")]
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"redmine", ["project-management"], [WebhookScreenshotConfig("issue_opened.json")]
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"reviewboard",
|
|
["productivity"],
|
|
[WebhookScreenshotConfig("review_request_published.json")],
|
|
display_name="Review Board",
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"rhodecode",
|
|
["version-control"],
|
|
[WebhookScreenshotConfig("push.json", channel="commits")],
|
|
display_name="RhodeCode",
|
|
url_options=[WebhookUrlOption.build_preset_config(PresetUrlOption.BRANCHES)],
|
|
),
|
|
IncomingWebhookIntegration("rundeck", ["deployment"], [WebhookScreenshotConfig("start.json")]),
|
|
IncomingWebhookIntegration(
|
|
"semaphore",
|
|
["continuous-integration", "deployment"],
|
|
[WebhookScreenshotConfig("pull_request.json")],
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"sentry", ["monitoring"], [WebhookScreenshotConfig("event_for_exception_python.json")]
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"shortcut",
|
|
["project-management"],
|
|
[WebhookScreenshotConfig("story_create.json")],
|
|
legacy_names=["clubhouse"],
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"slack",
|
|
["communication"],
|
|
url_options=[
|
|
WebhookUrlOption.build_preset_config(PresetUrlOption.CHANNEL_MAPPING),
|
|
],
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"slack_incoming",
|
|
["communication", "meta-integration"],
|
|
display_name="Slack-compatible webhook",
|
|
logo="images/integrations/logos/slack.svg",
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"sonarqube",
|
|
["continuous-integration", "monitoring"],
|
|
[WebhookScreenshotConfig("error.json")],
|
|
display_name="SonarQube",
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"sonarr", ["entertainment"], [WebhookScreenshotConfig("sonarr_episode_grabbed.json")]
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"splunk", ["monitoring"], [WebhookScreenshotConfig("search_one_result.json")]
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"statuspage",
|
|
["customer-support", "monitoring"],
|
|
[WebhookScreenshotConfig("incident_created.json")],
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"stripe", ["financial"], [WebhookScreenshotConfig("charge_succeeded__card.json")]
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"taiga", ["project-management"], [WebhookScreenshotConfig("userstory_changed_status.json")]
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"teamcity", ["continuous-integration"], [WebhookScreenshotConfig("success.json")]
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"thinkst", ["monitoring"], [WebhookScreenshotConfig("canary_consolidated_port_scan.json")]
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"transifex",
|
|
["misc"],
|
|
[
|
|
WebhookScreenshotConfig(
|
|
"",
|
|
extra_params={
|
|
"project": "Zulip Mobile",
|
|
"language": "en",
|
|
"resource": "file",
|
|
"event": "review_completed",
|
|
"reviewed": "100",
|
|
},
|
|
)
|
|
],
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"travis",
|
|
["continuous-integration"],
|
|
[WebhookScreenshotConfig("pull_request.json", payload_as_query_param=True)],
|
|
display_name="Travis CI",
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"trello", ["project-management"], [WebhookScreenshotConfig("adding_comment_to_card.json")]
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"updown", ["monitoring"], [WebhookScreenshotConfig("check_multiple_events.json")]
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"uptimerobot",
|
|
["monitoring"],
|
|
[WebhookScreenshotConfig("uptimerobot_monitor_up.json")],
|
|
display_name="UptimeRobot",
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"wekan",
|
|
["productivity", "project-management"],
|
|
[WebhookScreenshotConfig("add_comment.json")],
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"wordpress",
|
|
["marketing"],
|
|
[WebhookScreenshotConfig("publish_post.txt", "wordpress_post_created.png")],
|
|
display_name="WordPress",
|
|
),
|
|
IncomingWebhookIntegration(
|
|
"zabbix", ["monitoring"], [WebhookScreenshotConfig("zabbix_alert.json")]
|
|
),
|
|
IncomingWebhookIntegration("zapier", ["meta-integration"]),
|
|
IncomingWebhookIntegration(
|
|
"zendesk",
|
|
["customer-support"],
|
|
[
|
|
WebhookScreenshotConfig(
|
|
"",
|
|
use_basic_auth=True,
|
|
extra_params={
|
|
"ticket_title": "Hardware Ecosystem Compatibility Inquiry",
|
|
"ticket_id": "4837",
|
|
"message": "Hi, I am planning to purchase the X5000 smartphone and want to ensure compatibility with my existing devices - WDX10 wireless earbuds and Z600 smartwatch. Are there any known issues?",
|
|
},
|
|
)
|
|
],
|
|
),
|
|
]
|
|
|
|
VIDEO_CALL_INTEGRATIONS: list[Integration] = [
|
|
Integration(
|
|
"big-blue-button", ["video-calling", "communication"], display_name="BigBlueButton"
|
|
),
|
|
Integration(
|
|
"constructor-groups", ["video-calling", "communication"], display_name="Constructor Groups"
|
|
),
|
|
Integration("jitsi", ["video-calling", "communication"], display_name="Jitsi Meet"),
|
|
Integration(
|
|
"nextcloud-talk",
|
|
["video-calling", "communication"],
|
|
display_name="Nextcloud Talk",
|
|
logo="images/integrations/logos/nextcloud.svg",
|
|
),
|
|
Integration("webex", ["video-calling", "communication"]),
|
|
Integration("zoom", ["video-calling", "communication"]),
|
|
]
|
|
|
|
EMBEDDED_INTEGRATIONS: list[Integration] = [
|
|
Integration("email", ["communication"]),
|
|
Integration("giphy", ["misc"], display_name="GIPHY"),
|
|
Integration("klipy", ["misc"], display_name="KLIPY"),
|
|
Integration("tenor", ["misc"], display_name="Tenor"),
|
|
]
|
|
|
|
ZAPIER_INTEGRATIONS: list[Integration] = [
|
|
Integration("asana", ["project-management"]),
|
|
# Can be used with RSS integration too
|
|
Integration("mastodon", ["communication"]),
|
|
Integration(
|
|
"notion_via_zapier",
|
|
["productivity", "project-management"],
|
|
fixtureless_screenshot_config_options=[
|
|
FixturelessScreenshotConfigOptions(image_dir="notion")
|
|
],
|
|
display_name="Notion",
|
|
logo="images/integrations/logos/notion.svg",
|
|
doc="zerver/integrations/notion.md",
|
|
),
|
|
]
|
|
|
|
PLUGIN_INTEGRATIONS: list[Integration] = [
|
|
Integration("atolio", ["productivity"], logo="images/integrations/logos/atolio.jpeg"),
|
|
Integration("discourse", ["communication"]),
|
|
Integration(
|
|
"jenkins",
|
|
["continuous-integration"],
|
|
[FixturelessScreenshotConfigOptions(image_name="004.png")],
|
|
),
|
|
Integration("n8n", ["meta-integration"], display_name="n8n"),
|
|
Integration("nextcloud", ["productivity"]),
|
|
Integration("onyx", ["productivity"], logo="images/integrations/logos/onyx.png"),
|
|
]
|
|
|
|
# Each of these integrations have their own Zulip repository in GitHub.
|
|
STANDALONE_REPO_INTEGRATIONS: list[Integration] = [
|
|
Integration("errbot", ["meta-integration", "bots"]),
|
|
Integration(
|
|
"github-actions",
|
|
["continuous-integration"],
|
|
[FixturelessScreenshotConfigOptions(channel="github-actions updates")],
|
|
display_name="GitHub Actions",
|
|
),
|
|
Integration("hubot", ["meta-integration", "bots"]),
|
|
Integration("puppet", ["deployment"]),
|
|
]
|
|
|
|
ZULIP_SEND_INTEGRATIONS: list[Integration] = [
|
|
Integration("capistrano", ["deployment"]),
|
|
]
|
|
|
|
OTHER_INTEGRATIONS = [
|
|
*VIDEO_CALL_INTEGRATIONS,
|
|
*EMBEDDED_INTEGRATIONS,
|
|
*ZAPIER_INTEGRATIONS,
|
|
*PLUGIN_INTEGRATIONS,
|
|
*STANDALONE_REPO_INTEGRATIONS,
|
|
*ZULIP_SEND_INTEGRATIONS,
|
|
]
|
|
|
|
PYTHON_API_INTEGRATIONS: list[PythonAPIIntegration] = [
|
|
PythonAPIIntegration(
|
|
"codebase",
|
|
["version-control", "project-management"],
|
|
[FixturelessScreenshotConfigOptions(channel="commits")],
|
|
),
|
|
PythonAPIIntegration(
|
|
"git", ["version-control"], [FixturelessScreenshotConfigOptions(channel="commits")]
|
|
),
|
|
PythonAPIIntegration(
|
|
"google-calendar",
|
|
["productivity"],
|
|
[FixturelessScreenshotConfigOptions(image_name="003.png", image_dir="google/calendar")],
|
|
display_name="Google Calendar",
|
|
directory_name="google",
|
|
),
|
|
PythonAPIIntegration(
|
|
"irc", ["communication"], display_name="IRC", directory_name="bridge_with_irc"
|
|
),
|
|
PythonAPIIntegration("matrix", ["communication"], directory_name="bridge_with_matrix"),
|
|
PythonAPIIntegration(
|
|
"mercurial",
|
|
["version-control"],
|
|
[FixturelessScreenshotConfigOptions(channel="commits", image_dir="hg")],
|
|
display_name="Mercurial (hg)",
|
|
directory_name="hg",
|
|
),
|
|
PythonAPIIntegration("nagios", ["monitoring"]),
|
|
PythonAPIIntegration(
|
|
"openshift",
|
|
["deployment"],
|
|
[FixturelessScreenshotConfigOptions(channel="deployments")],
|
|
display_name="OpenShift",
|
|
),
|
|
PythonAPIIntegration(
|
|
"perforce", ["version-control"], [FixturelessScreenshotConfigOptions(channel="commits")]
|
|
),
|
|
PythonAPIIntegration("rss", ["communication"], display_name="RSS"),
|
|
PythonAPIIntegration(
|
|
"svn",
|
|
["version-control"],
|
|
[FixturelessScreenshotConfigOptions(channel="commits")],
|
|
display_name="Subversion",
|
|
),
|
|
PythonAPIIntegration("trac", ["project-management"]),
|
|
]
|
|
|
|
BOT_INTEGRATIONS: list[BotIntegration] = [
|
|
BotIntegration("github_detail", ["version-control", "bots"], display_name="GitHub Detail"),
|
|
BotIntegration("xkcd", ["bots", "entertainment"], display_name="xkcd"),
|
|
]
|
|
|
|
HUBOT_INTEGRATIONS: list[HubotIntegration] = [
|
|
HubotIntegration("assembla", ["version-control", "project-management"]),
|
|
HubotIntegration("bonusly", ["hr"]),
|
|
HubotIntegration("chartbeat", ["marketing"]),
|
|
HubotIntegration("google-translate", ["misc"], display_name="Google Translate"),
|
|
HubotIntegration(
|
|
"instagram",
|
|
["entertainment", "marketing"],
|
|
# _ needed to get around adblock plus
|
|
logo="images/integrations/logos/instagra_m.svg",
|
|
),
|
|
HubotIntegration("mailchimp", ["marketing", "communication"]),
|
|
HubotIntegration(
|
|
"youtube",
|
|
["entertainment", "marketing"],
|
|
display_name="YouTube",
|
|
# _ needed to get around adblock plus
|
|
logo="images/integrations/logos/youtub_e.svg",
|
|
),
|
|
]
|
|
|
|
|
|
INTEGRATIONS: dict[str, Integration] = {
|
|
(
|
|
# To avoid namespace collisions, use a prefix for embedded bots
|
|
f"embedded_bot_{integration.name}"
|
|
if isinstance(integration, EmbeddedBotIntegration)
|
|
else integration.name
|
|
): integration
|
|
for integration in chain(
|
|
INCOMING_WEBHOOK_INTEGRATIONS,
|
|
PYTHON_API_INTEGRATIONS,
|
|
BOT_INTEGRATIONS,
|
|
HUBOT_INTEGRATIONS,
|
|
EMBEDDED_BOTS,
|
|
OTHER_INTEGRATIONS,
|
|
)
|
|
}
|
|
|
|
hubot_integration_names = {integration.name for integration in HUBOT_INTEGRATIONS}
|
|
|
|
# Add integrations whose example screenshots are not yet automated here
|
|
INTEGRATIONS_MISSING_SCREENSHOT_CONFIG = (
|
|
# The fixture's goal.losedate needs to be modified dynamically,
|
|
# so the screenshot config is commented out.
|
|
{"beeminder"}
|
|
# Disabled integrations that are in the process of being added or rewritten.
|
|
| {"intercom", "notion"}
|
|
# Integrations that call external API endpoints.
|
|
| {"slack"}
|
|
| hubot_integration_names
|
|
)
|
|
|
|
# Add integrations that are not meant to have example screenshots here
|
|
INTEGRATIONS_WITHOUT_SCREENSHOTS = (
|
|
# Integration frameworks
|
|
{"ifttt", "n8n", "slack_incoming", "zapier"}
|
|
# Outgoing integrations
|
|
| {"atolio", "email", "onyx"}
|
|
# Video call integrations
|
|
| {"big-blue-button", "constructor-groups", "jitsi", "nextcloud-talk", "webex", "zoom"}
|
|
| {
|
|
# these integrations do not send messages
|
|
"giphy",
|
|
"klipy",
|
|
"nextcloud",
|
|
"tenor",
|
|
# the integration is planned to be removed
|
|
"twitter",
|
|
}
|
|
)
|
|
|
|
NO_SCREENSHOT_CONFIG = INTEGRATIONS_MISSING_SCREENSHOT_CONFIG | INTEGRATIONS_WITHOUT_SCREENSHOTS
|
|
|
|
|
|
def get_all_event_types_for_integration(integration: Integration) -> list[str] | None:
|
|
integration = INTEGRATIONS[integration.name]
|
|
if isinstance(integration, IncomingWebhookIntegration):
|
|
if integration.name == "githubsponsors":
|
|
return import_string("zerver.webhooks.github.view.SPONSORS_EVENT_TYPES")
|
|
function = integration.get_function()
|
|
if hasattr(function, "_all_event_types"):
|
|
return function._all_event_types
|
|
return None
|