zulip/zerver/lib/integrations.py

829 lines
32 KiB
Python

import os
from dataclasses import dataclass, field
from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple
from django.contrib.staticfiles.storage import staticfiles_storage
from django.urls import URLResolver, path
from django.utils.functional import Promise
from django.utils.module_loading import import_string
from django.utils.translation import gettext as gettext_lazy
from zerver.lib.storage import static_path
"""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
WebhookIntegration class is also used to generate the URLs in
`zproject/urls.py` for webhook integrations.
To add a new non-webhook integration, add code to the INTEGRATIONS
dictionary below.
To add a new webhook integration, declare a WebhookIntegration in the
WEBHOOK_INTEGRATIONS list below (it will be automatically added to
INTEGRATIONS).
To add a new integration category, add to the CATEGORIES dict.
Over time, we expect this registry to grow additional convenience
features for writing and configuring integrations efficiently.
"""
OptionValidator = Callable[[str, str], Optional[str]]
CATEGORIES: Dict[str, Promise] = {
"meta-integration": gettext_lazy("Integration frameworks"),
"continuous-integration": gettext_lazy("Continuous integration"),
"customer-support": gettext_lazy("Customer support"),
"deployment": gettext_lazy("Deployment"),
"entertainment": gettext_lazy("Entertainment"),
"communication": gettext_lazy("Communication"),
"financial": gettext_lazy("Financial"),
"hr": gettext_lazy("HR"),
"marketing": gettext_lazy("Marketing"),
"misc": gettext_lazy("Miscellaneous"),
"monitoring": gettext_lazy("Monitoring tools"),
"project-management": gettext_lazy("Project management"),
"productivity": gettext_lazy("Productivity"),
"version-control": gettext_lazy("Version control"),
"bots": gettext_lazy("Interactive bots"),
}
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"
def __init__(
self,
name: str,
client_name: str,
categories: List[str],
logo: Optional[str] = None,
secondary_line_text: Optional[str] = None,
display_name: Optional[str] = None,
doc: Optional[str] = None,
stream_name: Optional[str] = None,
legacy: bool = False,
config_options: Sequence[Tuple[str, str, OptionValidator]] = [],
) -> None:
self.name = name
self.client_name = client_name
self.secondary_line_text = secondary_line_text
self.legacy = legacy
self.doc = doc
# 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 = list(map((lambda c: CATEGORIES[c]), categories))
self.logo_path = logo if logo is not None else self.get_logo_path()
self.logo_url = self.get_logo_url()
if display_name is None:
display_name = name.title()
self.display_name = display_name
if stream_name is None:
stream_name = self.name
self.stream_name = stream_name
def is_enabled(self) -> bool:
return True
def get_logo_path(self) -> Optional[str]:
logo_file_path_svg = self.DEFAULT_LOGO_STATIC_PATH_SVG.format(name=self.name)
logo_file_path_png = self.DEFAULT_LOGO_STATIC_PATH_PNG.format(name=self.name)
if os.path.isfile(static_path(logo_file_path_svg)):
return logo_file_path_svg
elif os.path.isfile(static_path(logo_file_path_png)):
return logo_file_path_png
return None
def get_bot_avatar_path(self) -> Optional[str]:
if self.logo_path is not None:
name = os.path.splitext(os.path.basename(self.logo_path))[0]
return self.DEFAULT_BOT_AVATAR_PATH.format(name=name)
return None
def get_logo_url(self) -> Optional[str]:
if self.logo_path is not None:
return staticfiles_storage.url(self.logo_path)
return None
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],
logo: Optional[str] = None,
secondary_line_text: Optional[str] = None,
display_name: Optional[str] = None,
doc: Optional[str] = None,
) -> None:
super().__init__(
name,
client_name=name,
categories=categories,
secondary_line_text=secondary_line_text,
)
if logo is None:
self.logo_url = self.get_logo_url()
if self.logo_url is None:
# TODO: Add a test for this by initializing one in a test.
logo = staticfiles_storage.url(self.ZULIP_LOGO_STATIC_PATH_PNG) # nocoverage
else:
self.logo_url = staticfiles_storage.url(logo)
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 WebhookIntegration(Integration):
DEFAULT_FUNCTION_PATH = "zerver.webhooks.{name}.view.api_{name}_webhook"
DEFAULT_URL = "api/v1/external/{name}"
DEFAULT_CLIENT_NAME = "Zulip{name}Webhook"
DEFAULT_DOC_PATH = "{name}/doc.{ext}"
def __init__(
self,
name: str,
categories: List[str],
client_name: Optional[str] = None,
logo: Optional[str] = None,
secondary_line_text: Optional[str] = None,
function: Optional[str] = None,
url: Optional[str] = None,
display_name: Optional[str] = None,
doc: Optional[str] = None,
stream_name: Optional[str] = None,
legacy: bool = False,
config_options: Sequence[Tuple[str, str, OptionValidator]] = [],
) -> None:
if client_name is None:
client_name = self.DEFAULT_CLIENT_NAME.format(name=name.title())
super().__init__(
name,
client_name,
categories,
logo=logo,
secondary_line_text=secondary_line_text,
display_name=display_name,
stream_name=stream_name,
legacy=legacy,
config_options=config_options,
)
if function is None:
function = self.DEFAULT_FUNCTION_PATH.format(name=name)
if isinstance(function, str):
function = import_string(function)
self.function = function
if url is None:
url = self.DEFAULT_URL.format(name=name)
self.url = url
if doc is None:
doc = self.DEFAULT_DOC_PATH.format(name=name, ext="md")
self.doc = doc
@property
def url_object(self) -> URLResolver:
assert self.function is not None
return path(self.url, self.function)
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
@dataclass
class BaseScreenshotConfig:
fixture_name: str
image_name: str = "001.png"
image_dir: Optional[str] = None
bot_name: Optional[str] = None
@dataclass
class ScreenshotConfig(BaseScreenshotConfig):
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)
def get_fixture_and_image_paths(
integration: Integration, screenshot_config: BaseScreenshotConfig
) -> Tuple[str, str]:
if isinstance(integration, WebhookIntegration):
fixture_dir = os.path.join("zerver", "webhooks", integration.name, "fixtures")
else:
fixture_dir = os.path.join("zerver", "integration_fixtures", integration.name)
fixture_path = os.path.join(fixture_dir, screenshot_config.fixture_name)
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 fixture_path, image_path
class HubotIntegration(Integration):
GIT_URL_TEMPLATE = "https://github.com/hubot-scripts/hubot-{}"
def __init__(
self,
name: str,
categories: List[str],
display_name: Optional[str] = None,
logo: Optional[str] = None,
logo_alt: Optional[str] = None,
git_url: Optional[str] = None,
legacy: bool = False,
) -> None:
if logo_alt is None:
logo_alt = f"{name.title()} logo"
self.logo_alt = logo_alt
if git_url is None:
git_url = self.GIT_URL_TEMPLATE.format(name)
self.hubot_docs_url = git_url
super().__init__(
name,
name,
categories,
logo=logo,
display_name=display_name,
doc="zerver/integrations/hubot_common.md",
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"
def __init__(self, name: str, *args: Any, **kwargs: Any) -> None:
assert kwargs.get("client_name") is None
client_name = self.DEFAULT_CLIENT_NAME.format(name=name.title())
super().__init__(name, client_name, *args, **kwargs)
EMBEDDED_BOTS: List[EmbeddedBotIntegration] = [
EmbeddedBotIntegration("converter", []),
EmbeddedBotIntegration("encrypt", []),
EmbeddedBotIntegration("helloworld", []),
EmbeddedBotIntegration("virtual_fs", []),
EmbeddedBotIntegration("giphy", []),
EmbeddedBotIntegration("followup", []),
]
WEBHOOK_INTEGRATIONS: List[WebhookIntegration] = [
WebhookIntegration("airbrake", ["monitoring"]),
WebhookIntegration(
"alertmanager",
["monitoring"],
display_name="Prometheus Alertmanager",
logo="images/integrations/logos/prometheus.svg",
),
WebhookIntegration("ansibletower", ["deployment"], display_name="Ansible Tower"),
WebhookIntegration("appfollow", ["customer-support"], display_name="AppFollow"),
WebhookIntegration("appveyor", ["continuous-integration"], display_name="AppVeyor"),
WebhookIntegration("beanstalk", ["version-control"], stream_name="commits"),
WebhookIntegration("basecamp", ["project-management"]),
WebhookIntegration("beeminder", ["misc"], display_name="Beeminder"),
WebhookIntegration(
"bitbucket3",
["version-control"],
logo="images/integrations/logos/bitbucket.svg",
display_name="Bitbucket Server",
stream_name="bitbucket",
),
WebhookIntegration(
"bitbucket2",
["version-control"],
logo="images/integrations/logos/bitbucket.svg",
display_name="Bitbucket",
stream_name="bitbucket",
),
WebhookIntegration(
"bitbucket",
["version-control"],
display_name="Bitbucket",
secondary_line_text="(Enterprise)",
stream_name="commits",
legacy=True,
),
WebhookIntegration("buildbot", ["continuous-integration"], display_name="Buildbot"),
WebhookIntegration("canarytoken", ["monitoring"], display_name="Thinkst Canarytokens"),
WebhookIntegration("circleci", ["continuous-integration"], display_name="CircleCI"),
WebhookIntegration("clubhouse", ["project-management"]),
WebhookIntegration("codeship", ["continuous-integration", "deployment"]),
WebhookIntegration("crashlytics", ["monitoring"]),
WebhookIntegration("dialogflow", ["customer-support"], display_name="Dialogflow"),
WebhookIntegration("delighted", ["customer-support", "marketing"], display_name="Delighted"),
WebhookIntegration(
"deskdotcom",
["customer-support"],
logo="images/integrations/logos/deskcom.png",
display_name="Desk.com",
stream_name="desk",
),
WebhookIntegration("dropbox", ["productivity"], display_name="Dropbox"),
WebhookIntegration("errbit", ["monitoring"], display_name="Errbit"),
WebhookIntegration("flock", ["customer-support"], display_name="Flock"),
WebhookIntegration("freshdesk", ["customer-support"]),
WebhookIntegration("freshping", ["monitoring"], display_name="Freshping"),
WebhookIntegration(
"freshstatus", ["monitoring", "customer-support"], display_name="Freshstatus"
),
WebhookIntegration("front", ["customer-support"], display_name="Front"),
WebhookIntegration("gitea", ["version-control"], stream_name="commits"),
WebhookIntegration(
"github",
["version-control"],
display_name="GitHub",
logo="images/integrations/logos/github.svg",
function="zerver.webhooks.github.view.api_github_webhook",
stream_name="github",
),
WebhookIntegration("gitlab", ["version-control"], display_name="GitLab"),
WebhookIntegration("gocd", ["continuous-integration"], display_name="GoCD"),
WebhookIntegration("gogs", ["version-control"], stream_name="commits"),
WebhookIntegration("gosquared", ["marketing"], display_name="GoSquared"),
WebhookIntegration("grafana", ["monitoring"], display_name="Grafana"),
WebhookIntegration("greenhouse", ["hr"], display_name="Greenhouse"),
WebhookIntegration("groove", ["customer-support"], display_name="Groove"),
WebhookIntegration("harbor", ["deployment", "productivity"], display_name="Harbor"),
WebhookIntegration("hellosign", ["productivity", "hr"], display_name="HelloSign"),
WebhookIntegration("helloworld", ["misc"], display_name="Hello World"),
WebhookIntegration("heroku", ["deployment"], display_name="Heroku"),
WebhookIntegration("homeassistant", ["misc"], display_name="Home Assistant"),
WebhookIntegration(
"ifttt",
["meta-integration"],
function="zerver.webhooks.ifttt.view.api_iftt_app_webhook",
display_name="IFTTT",
),
WebhookIntegration("insping", ["monitoring"], display_name="Insping"),
WebhookIntegration("intercom", ["customer-support"], display_name="Intercom"),
WebhookIntegration("jira", ["project-management"], display_name="Jira"),
WebhookIntegration("jotform", ["misc"], display_name="Jotform"),
WebhookIntegration("json", ["misc"], display_name="JSON formatter"),
WebhookIntegration("librato", ["monitoring"]),
WebhookIntegration("lidarr", ["entertainment"]),
WebhookIntegration("mention", ["marketing"], display_name="Mention"),
WebhookIntegration("netlify", ["continuous-integration", "deployment"], display_name="Netlify"),
WebhookIntegration("newrelic", ["monitoring"], display_name="New Relic"),
WebhookIntegration(
"opbeat",
["monitoring"],
display_name="Opbeat",
stream_name="opbeat",
function="zerver.webhooks.opbeat.view.api_opbeat_webhook",
),
WebhookIntegration("opencollective", ["communication"], display_name="Open Collective"),
WebhookIntegration("opsgenie", ["meta-integration", "monitoring"]),
WebhookIntegration("pagerduty", ["monitoring"], display_name="PagerDuty"),
WebhookIntegration("papertrail", ["monitoring"]),
WebhookIntegration("pingdom", ["monitoring"]),
WebhookIntegration("pivotal", ["project-management"], display_name="Pivotal Tracker"),
WebhookIntegration("radarr", ["entertainment"], display_name="Radarr"),
WebhookIntegration("raygun", ["monitoring"], display_name="Raygun"),
WebhookIntegration("reviewboard", ["version-control"], display_name="Review Board"),
WebhookIntegration("semaphore", ["continuous-integration", "deployment"]),
WebhookIntegration("sentry", ["monitoring"]),
WebhookIntegration(
"slack_incoming",
["communication", "meta-integration"],
display_name="Slack-compatible webhook",
logo="images/integrations/logos/slack.svg",
),
WebhookIntegration("slack", ["communication"]),
WebhookIntegration("solano", ["continuous-integration"], display_name="Solano Labs"),
WebhookIntegration("sonarqube", ["continuous-integration"], display_name="SonarQube"),
WebhookIntegration("sonarr", ["entertainment"], display_name="Sonarr"),
WebhookIntegration("splunk", ["monitoring"], display_name="Splunk"),
WebhookIntegration("statuspage", ["customer-support"], display_name="Statuspage"),
WebhookIntegration("stripe", ["financial"], display_name="Stripe"),
WebhookIntegration("taiga", ["project-management"]),
WebhookIntegration("teamcity", ["continuous-integration"]),
WebhookIntegration("thinkst", ["monitoring"]),
WebhookIntegration("transifex", ["misc"]),
WebhookIntegration("travis", ["continuous-integration"], display_name="Travis CI"),
WebhookIntegration("trello", ["project-management"]),
WebhookIntegration("updown", ["monitoring"]),
WebhookIntegration("uptimerobot", ["monitoring"], display_name="UptimeRobot"),
WebhookIntegration(
"yo",
["communication"],
function="zerver.webhooks.yo.view.api_yo_app_webhook",
display_name="Yo",
),
WebhookIntegration("wordpress", ["marketing"], display_name="WordPress"),
WebhookIntegration("zapier", ["meta-integration"]),
WebhookIntegration("zendesk", ["customer-support"]),
WebhookIntegration("zabbix", ["monitoring"], display_name="Zabbix"),
WebhookIntegration("gci", ["misc"], display_name="Google Code-in", stream_name="gci"),
]
INTEGRATIONS: Dict[str, Integration] = {
"asana": Integration(
"asana", "asana", ["project-management"], doc="zerver/integrations/asana.md"
),
"big-blue-button": Integration(
"big-blue-button",
"big-blue-button",
["communication"],
logo="images/integrations/logos/bigbluebutton.svg",
display_name="BigBlueButton",
doc="zerver/integrations/big-blue-button.md",
),
"capistrano": Integration(
"capistrano",
"capistrano",
["deployment"],
display_name="Capistrano",
doc="zerver/integrations/capistrano.md",
),
"codebase": Integration(
"codebase", "codebase", ["version-control"], doc="zerver/integrations/codebase.md"
),
"discourse": Integration(
"discourse", "discourse", ["communication"], doc="zerver/integrations/discourse.md"
),
"email": Integration("email", "email", ["communication"], doc="zerver/integrations/email.md"),
"errbot": Integration(
"errbot", "errbot", ["meta-integration", "bots"], doc="zerver/integrations/errbot.md"
),
"giphy": Integration(
"giphy",
"giphy",
display_name="GIPHY",
categories=["misc"],
doc="zerver/integrations/giphy.md",
logo="images/GIPHY_big_logo.png",
),
"git": Integration(
"git", "git", ["version-control"], stream_name="commits", doc="zerver/integrations/git.md"
),
"github-actions": Integration(
"github-actions",
"github-actions",
["continuous-integration"],
display_name="GitHub Actions",
doc="zerver/integrations/github-actions.md",
),
"google-calendar": Integration(
"google-calendar",
"google-calendar",
["productivity"],
display_name="Google Calendar",
doc="zerver/integrations/google-calendar.md",
),
"hubot": Integration(
"hubot", "hubot", ["meta-integration", "bots"], doc="zerver/integrations/hubot.md"
),
"irc": Integration(
"irc", "irc", ["communication"], display_name="IRC", doc="zerver/integrations/irc.md"
),
"jenkins": Integration(
"jenkins",
"jenkins",
["continuous-integration"],
secondary_line_text="(or Hudson)",
doc="zerver/integrations/jenkins.md",
),
"jira-plugin": Integration(
"jira-plugin",
"jira-plugin",
["project-management"],
logo="images/integrations/logos/jira.svg",
secondary_line_text="(locally installed)",
display_name="Jira",
doc="zerver/integrations/jira-plugin.md",
stream_name="jira",
legacy=True,
),
"jitsi": Integration(
"jitsi",
"jitsi",
["communication"],
logo="images/integrations/logos/jitsi.svg",
display_name="Jitsi Meet",
doc="zerver/integrations/jitsi.md",
),
"matrix": Integration(
"matrix", "matrix", ["communication"], doc="zerver/integrations/matrix.md"
),
"mercurial": Integration(
"mercurial",
"mercurial",
["version-control"],
display_name="Mercurial (hg)",
doc="zerver/integrations/mercurial.md",
stream_name="commits",
),
"nagios": Integration("nagios", "nagios", ["monitoring"], doc="zerver/integrations/nagios.md"),
"openshift": Integration(
"openshift",
"openshift",
["deployment"],
display_name="OpenShift",
doc="zerver/integrations/openshift.md",
stream_name="deployments",
),
"perforce": Integration(
"perforce", "perforce", ["version-control"], doc="zerver/integrations/perforce.md"
),
"phabricator": Integration(
"phabricator", "phabricator", ["version-control"], doc="zerver/integrations/phabricator.md"
),
"puppet": Integration("puppet", "puppet", ["deployment"], doc="zerver/integrations/puppet.md"),
"redmine": Integration(
"redmine", "redmine", ["project-management"], doc="zerver/integrations/redmine.md"
),
"rss": Integration(
"rss", "rss", ["communication"], display_name="RSS", doc="zerver/integrations/rss.md"
),
"svn": Integration("svn", "svn", ["version-control"], doc="zerver/integrations/svn.md"),
"trac": Integration("trac", "trac", ["project-management"], doc="zerver/integrations/trac.md"),
"trello-plugin": Integration(
"trello-plugin",
"trello-plugin",
["project-management"],
logo="images/integrations/logos/trello.svg",
secondary_line_text="(legacy)",
display_name="Trello",
doc="zerver/integrations/trello-plugin.md",
stream_name="trello",
legacy=True,
),
"twitter": Integration(
"twitter",
"twitter",
["customer-support", "marketing"],
# _ needed to get around adblock plus
logo="images/integrations/logos/twitte_r.svg",
doc="zerver/integrations/twitter.md",
),
"zoom": Integration(
"zoom",
"zoom",
["communication"],
logo="images/integrations/logos/zoom.svg",
display_name="Zoom",
doc="zerver/integrations/zoom.md",
),
}
BOT_INTEGRATIONS: List[BotIntegration] = [
BotIntegration("github_detail", ["version-control", "bots"], display_name="GitHub Detail"),
BotIntegration(
"xkcd", ["bots", "misc"], display_name="xkcd", logo="images/integrations/logos/xkcd.png"
),
]
HUBOT_INTEGRATIONS: List[HubotIntegration] = [
HubotIntegration(
"assembla",
["version-control", "project-management"],
display_name="Assembla",
logo_alt="Assembla",
),
HubotIntegration("bonusly", ["hr"]),
HubotIntegration("chartbeat", ["marketing"], display_name="Chartbeat"),
HubotIntegration("darksky", ["misc"], display_name="Dark Sky", logo_alt="Dark Sky logo"),
HubotIntegration(
"instagram",
["misc"],
display_name="Instagram",
# _ needed to get around adblock plus
logo="images/integrations/logos/instagra_m.svg",
),
HubotIntegration("mailchimp", ["communication", "marketing"], display_name="Mailchimp"),
HubotIntegration(
"google-translate",
["misc"],
display_name="Google Translate",
logo_alt="Google Translate logo",
),
HubotIntegration(
"youtube",
["misc"],
display_name="YouTube",
# _ needed to get around adblock plus
logo="images/integrations/logos/youtub_e.svg",
),
]
for hubot_integration in HUBOT_INTEGRATIONS:
INTEGRATIONS[hubot_integration.name] = hubot_integration
for webhook_integration in WEBHOOK_INTEGRATIONS:
INTEGRATIONS[webhook_integration.name] = webhook_integration
for bot_integration in BOT_INTEGRATIONS:
INTEGRATIONS[bot_integration.name] = bot_integration
# Add integrations that don't have automated screenshots here
NO_SCREENSHOT_WEBHOOKS = {
"beeminder", # FIXME: fixture's goal.losedate needs to be modified dynamically
"ifttt", # Docs don't have a screenshot
"slack_incoming", # Docs don't have a screenshot
"zapier", # Docs don't have a screenshot
}
DOC_SCREENSHOT_CONFIG: Dict[str, List[BaseScreenshotConfig]] = {
"airbrake": [ScreenshotConfig("error_message.json")],
"alertmanager": [
ScreenshotConfig("alert.json", extra_params={"name": "topic", "desc": "description"})
],
"ansibletower": [ScreenshotConfig("job_successful_multiple_hosts.json")],
"appfollow": [ScreenshotConfig("review.json")],
"appveyor": [ScreenshotConfig("appveyor_build_success.json")],
"basecamp": [ScreenshotConfig("doc_active.json")],
"beanstalk": [
ScreenshotConfig("git_multiple.json", use_basic_auth=True, payload_as_query_param=True)
],
# 'beeminder': [ScreenshotConfig('derail_worried.json')],
"bitbucket": [
ScreenshotConfig("push.json", "002.png", use_basic_auth=True, payload_as_query_param=True)
],
"bitbucket2": [
ScreenshotConfig("issue_created.json", "003.png", "bitbucket", bot_name="Bitbucket Bot")
],
"bitbucket3": [
ScreenshotConfig(
"repo_push_update_single_branch.json",
"004.png",
"bitbucket",
bot_name="Bitbucket Server Bot",
)
],
"buildbot": [ScreenshotConfig("started.json")],
"canarytoken": [ScreenshotConfig("canarytoken_real.json")],
"circleci": [
ScreenshotConfig(
"github_bionic_production_build_success_multiple_parties.json", image_name="001.png"
),
ScreenshotConfig("bitbucket_private_repo_pull_request_failure.json", image_name="002.png"),
],
"clubhouse": [ScreenshotConfig("story_create.json")],
"codeship": [ScreenshotConfig("error_build.json")],
"crashlytics": [ScreenshotConfig("issue_message.json")],
"delighted": [ScreenshotConfig("survey_response_updated_promoter.json")],
"deskdotcom": [ScreenshotConfig("static_text.txt", "009.png", "desk", use_basic_auth=True)],
"dialogflow": [ScreenshotConfig("weather_app.json", extra_params={"email": "iago@zulip.com"})],
"dropbox": [ScreenshotConfig("file_updated.json")],
"errbit": [ScreenshotConfig("error_message.json")],
"flock": [ScreenshotConfig("messages.json")],
"freshdesk": [
ScreenshotConfig("ticket_created.json", image_name="004.png", use_basic_auth=True)
],
"freshping": [ScreenshotConfig("freshping_check_unreachable.json")],
"freshstatus": [ScreenshotConfig("freshstatus_incident_open.json")],
"front": [ScreenshotConfig("inbound_message.json")],
"gci": [ScreenshotConfig("task_abandoned_by_student.json")],
"gitea": [ScreenshotConfig("pull_request__merged.json")],
"github": [ScreenshotConfig("push__1_commit.json")],
"gitlab": [ScreenshotConfig("push_hook__push_local_branch_without_commits.json")],
"gocd": [ScreenshotConfig("pipeline.json")],
"gogs": [ScreenshotConfig("pull_request__opened.json")],
"gosquared": [ScreenshotConfig("traffic_spike.json", image_name="000.png")],
"grafana": [ScreenshotConfig("alert.json")],
"greenhouse": [ScreenshotConfig("candidate_stage_change.json", image_name="000.png")],
"groove": [ScreenshotConfig("ticket_started.json")],
"harbor": [ScreenshotConfig("scanning_completed.json")],
"hellosign": [
ScreenshotConfig(
"signatures_signed_by_one_signatory.json",
payload_as_query_param=True,
payload_param_name="json",
)
],
"helloworld": [ScreenshotConfig("hello.json")],
"heroku": [ScreenshotConfig("deploy.txt")],
"homeassistant": [ScreenshotConfig("reqwithtitle.json", image_name="003.png")],
"insping": [ScreenshotConfig("website_state_available.json")],
"intercom": [ScreenshotConfig("conversation_admin_replied.json")],
"jira": [ScreenshotConfig("created_v1.json")],
"jotform": [ScreenshotConfig("response.json")],
"json": [ScreenshotConfig("json_github_push__1_commit.json")],
"librato": [ScreenshotConfig("three_conditions_alert.json", payload_as_query_param=True)],
"lidarr": [ScreenshotConfig("lidarr_tracks_grabbed.json")],
"mention": [ScreenshotConfig("webfeeds.json")],
"nagios": [BaseScreenshotConfig("service_notify.json")],
"netlify": [ScreenshotConfig("deploy_building.json")],
"newrelic": [
ScreenshotConfig("incident_opened.json", "001.png"),
ScreenshotConfig("incident_acknowledged.json", "002.png"),
ScreenshotConfig("incident_closed.json", "003.png"),
],
"opbeat": [ScreenshotConfig("error_reopen.json")],
"opencollective": [ScreenshotConfig("one_time_donation.json")],
"opsgenie": [ScreenshotConfig("addrecipient.json", image_name="000.png")],
"pagerduty": [ScreenshotConfig("trigger_v2.json")],
"papertrail": [ScreenshotConfig("short_post.json", payload_as_query_param=True)],
"pingdom": [ScreenshotConfig("http_up_to_down.json", image_name="002.png")],
"pivotal": [ScreenshotConfig("v5_type_changed.json")],
"radarr": [ScreenshotConfig("radarr_movie_grabbed.json")],
"raygun": [ScreenshotConfig("new_error.json")],
"reviewboard": [ScreenshotConfig("review_request_published.json")],
"semaphore": [ScreenshotConfig("pull_request.json")],
"sentry": [
ScreenshotConfig("event_for_exception_python.json"),
ScreenshotConfig("issue_assigned_to_team.json", "002.png"),
],
"slack": [ScreenshotConfig("message_info.txt")],
"solano": [ScreenshotConfig("build_001.json")],
"sonarqube": [ScreenshotConfig("error.json")],
"sonarr": [ScreenshotConfig("sonarr_episode_grabbed.json")],
"splunk": [ScreenshotConfig("search_one_result.json")],
"statuspage": [ScreenshotConfig("incident_created.json")],
"stripe": [ScreenshotConfig("charge_succeeded__card.json")],
"taiga": [ScreenshotConfig("userstory_changed_status.json")],
"teamcity": [ScreenshotConfig("success.json"), ScreenshotConfig("personal.json", "002.png")],
"thinkst": [ScreenshotConfig("canary_consolidated_port_scan.json")],
"transifex": [
ScreenshotConfig(
"",
extra_params={
"project": "Zulip Mobile",
"language": "en",
"resource": "file",
"reviewed": "100",
},
)
],
"travis": [ScreenshotConfig("build.json", payload_as_query_param=True)],
"trello": [ScreenshotConfig("adding_comment_to_card.json")],
"updown": [ScreenshotConfig("check_multiple_events.json")],
"uptimerobot": [ScreenshotConfig("uptimerobot_monitor_up.json")],
"wordpress": [ScreenshotConfig("publish_post.txt", "wordpress_post_created.png")],
"yo": [
ScreenshotConfig(
"",
"002.png",
"yo-app",
extra_params={"email": "iago@zulip.com", "username": "Cordelia"},
)
],
"zabbix": [ScreenshotConfig("zabbix_alert.json")],
"zendesk": [
ScreenshotConfig(
"",
"007.png",
use_basic_auth=True,
extra_params={
"ticket_title": "Test ticket",
"ticket_id": "4",
"message": "Test message",
},
)
],
}