mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-04 14:03:30 +00:00 
			
		
		
		
	This commit was split by tabbott; this piece covers the vast majority
of files in Zulip, but excludes scripts/, tools/, and puppet/ to help
ensure we at least show the right error messages for Xenial systems.
We can likely further refine the remaining pieces with some testing.
Generated by com2ann, with whitespace fixes and various manual fixes
for runtime issues:
-    invoiced_through: Optional[LicenseLedger] = models.ForeignKey(
+    invoiced_through: Optional["LicenseLedger"] = models.ForeignKey(
-_apns_client: Optional[APNsClient] = None
+_apns_client: Optional["APNsClient"] = None
-    notifications_stream: Optional[Stream] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
-    signup_notifications_stream: Optional[Stream] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
+    notifications_stream: Optional["Stream"] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
+    signup_notifications_stream: Optional["Stream"] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
-    author: Optional[UserProfile] = models.ForeignKey('UserProfile', blank=True, null=True, on_delete=CASCADE)
+    author: Optional["UserProfile"] = models.ForeignKey('UserProfile', blank=True, null=True, on_delete=CASCADE)
-    bot_owner: Optional[UserProfile] = models.ForeignKey('self', null=True, on_delete=models.SET_NULL)
+    bot_owner: Optional["UserProfile"] = models.ForeignKey('self', null=True, on_delete=models.SET_NULL)
-    default_sending_stream: Optional[Stream] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
-    default_events_register_stream: Optional[Stream] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
+    default_sending_stream: Optional["Stream"] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
+    default_events_register_stream: Optional["Stream"] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
-descriptors_by_handler_id: Dict[int, ClientDescriptor] = {}
+descriptors_by_handler_id: Dict[int, "ClientDescriptor"] = {}
-worker_classes: Dict[str, Type[QueueProcessingWorker]] = {}
-queues: Dict[str, Dict[str, Type[QueueProcessingWorker]]] = {}
+worker_classes: Dict[str, Type["QueueProcessingWorker"]] = {}
+queues: Dict[str, Dict[str, Type["QueueProcessingWorker"]]] = {}
-AUTH_LDAP_REVERSE_EMAIL_SEARCH: Optional[LDAPSearch] = None
+AUTH_LDAP_REVERSE_EMAIL_SEARCH: Optional["LDAPSearch"] = None
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
		
	
		
			
				
	
	
		
			493 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			493 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
import os
 | 
						|
 | 
						|
from typing import Dict, List, Optional, Any, Tuple
 | 
						|
from django.conf.urls import url
 | 
						|
from django.contrib.staticfiles.storage import staticfiles_storage
 | 
						|
from django.urls.resolvers import RegexPattern
 | 
						|
from django.utils.module_loading import import_string
 | 
						|
from django.utils.translation import ugettext as _
 | 
						|
from zerver.lib.storage import static_path
 | 
						|
from zerver.lib.types import Validator
 | 
						|
 | 
						|
 | 
						|
"""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.
 | 
						|
"""
 | 
						|
 | 
						|
CATEGORIES: Dict[str, str] = {
 | 
						|
    'meta-integration': _('Integration frameworks'),
 | 
						|
    'continuous-integration': _('Continuous integration'),
 | 
						|
    'customer-support': _('Customer support'),
 | 
						|
    'deployment': _('Deployment'),
 | 
						|
    'communication': _('Communication'),
 | 
						|
    'financial': _('Financial'),
 | 
						|
    'hr': _('HR'),
 | 
						|
    'marketing': _('Marketing'),
 | 
						|
    'misc': _('Miscellaneous'),
 | 
						|
    'monitoring': _('Monitoring tools'),
 | 
						|
    'project-management': _('Project management'),
 | 
						|
    'productivity': _('Productivity'),
 | 
						|
    'version-control': _('Version control'),
 | 
						|
    'bots': _('Interactive bots'),
 | 
						|
}
 | 
						|
 | 
						|
class Integration:
 | 
						|
    DEFAULT_LOGO_STATIC_PATH_PNG = 'images/integrations/logos/{name}.png'
 | 
						|
    DEFAULT_LOGO_STATIC_PATH_SVG = 'images/integrations/logos/{name}.svg'
 | 
						|
 | 
						|
    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: Optional[bool]=False,
 | 
						|
                 config_options: List[Tuple[str, str, Validator]]=[]) -> 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))
 | 
						|
 | 
						|
        if logo is None:
 | 
						|
            self.logo_url = self.get_logo_url()
 | 
						|
        else:
 | 
						|
            self.logo_url = staticfiles_storage.url(logo)
 | 
						|
 | 
						|
        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_url(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 staticfiles_storage.url(logo_file_path_svg)
 | 
						|
        elif os.path.isfile(static_path(logo_file_path_png)):
 | 
						|
            return staticfiles_storage.url(logo_file_path_png)
 | 
						|
 | 
						|
        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 = "{} Bot".format(name.title())  # nocoverage
 | 
						|
        else:
 | 
						|
            display_name = "{} Bot".format(display_name)
 | 
						|
        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: Optional[bool]=None,
 | 
						|
                 config_options: List[Tuple[str, str, Validator]]=[]) -> 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) -> RegexPattern:
 | 
						|
        return url(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
 | 
						|
 | 
						|
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 = "{} logo".format(name.title())
 | 
						|
        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('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('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('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('librato', ['monitoring']),
 | 
						|
    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('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('raygun', ['monitoring'], display_name="Raygun"),
 | 
						|
    WebhookIntegration('reviewboard', ['version-control'], display_name="ReviewBoard"),
 | 
						|
    WebhookIntegration('semaphore', ['continuous-integration', 'deployment']),
 | 
						|
    WebhookIntegration('sentry', ['monitoring']),
 | 
						|
    WebhookIntegration('slack', ['communication']),
 | 
						|
    WebhookIntegration('solano', ['continuous-integration'], display_name='Solano Labs'),
 | 
						|
    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('transifex', ['misc']),
 | 
						|
    WebhookIntegration('travis', ['continuous-integration'], display_name='Travis CI'),
 | 
						|
    WebhookIntegration('trello', ['project-management']),
 | 
						|
    WebhookIntegration('updown', ['monitoring']),
 | 
						|
    WebhookIntegration(
 | 
						|
        'yo',
 | 
						|
        ['communication'],
 | 
						|
        function='zerver.webhooks.yo.view.api_yo_app_webhook',
 | 
						|
        display_name='Yo App'
 | 
						|
    ),
 | 
						|
    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'),
 | 
						|
    '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'),
 | 
						|
    'git': Integration('git', 'git', ['version-control'],
 | 
						|
                       stream_name='commits', doc='zerver/integrations/git.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
 | 
						|
    ),
 | 
						|
    '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'),
 | 
						|
}
 | 
						|
 | 
						|
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('google-hangouts', ['communication'], display_name='Google Hangouts',
 | 
						|
                     logo_alt='Google Hangouts 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
 |