models: Extract zerver.models.bots.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
Anders Kaseorg 2023-12-15 12:00:29 -08:00 committed by Tim Abbott
parent 27c0b507af
commit 1f1b2f9a68
8 changed files with 96 additions and 91 deletions

View File

@ -41,8 +41,8 @@ from zerver.models import (
Subscription, Subscription,
UserGroupMembership, UserGroupMembership,
UserProfile, UserProfile,
get_bot_services,
) )
from zerver.models.bots import get_bot_services
from zerver.models.realms import get_fake_email_domain from zerver.models.realms import get_fake_email_domain
from zerver.models.users import ( from zerver.models.users import (
active_non_guest_user_ids, active_non_guest_user_ids,

View File

@ -19,7 +19,8 @@ from zerver.lib.outgoing_http import OutgoingSession
from zerver.lib.queue import retry_event from zerver.lib.queue import retry_event
from zerver.lib.topic import get_topic_from_message_info from zerver.lib.topic import get_topic_from_message_info
from zerver.lib.url_encoding import near_message_url from zerver.lib.url_encoding import near_message_url
from zerver.models import GENERIC_INTERFACE, SLACK_INTERFACE, Realm, Service, UserProfile from zerver.models import Realm, Service, UserProfile
from zerver.models.bots import GENERIC_INTERFACE, SLACK_INTERFACE
from zerver.models.clients import get_client from zerver.models.clients import get_client
from zerver.models.users import get_user_profile_by_id from zerver.models.users import get_user_profile_by_id

View File

@ -1,4 +1,4 @@
from typing import Dict, List, Tuple, TypeVar, Union from typing import List, Tuple, TypeVar, Union
from django.db import models from django.db import models
from django.db.backends.base.base import BaseDatabaseWrapper from django.db.backends.base.base import BaseDatabaseWrapper
@ -13,6 +13,9 @@ from zerver.lib.cache import (
realm_alert_words_automaton_cache_key, realm_alert_words_automaton_cache_key,
realm_alert_words_cache_key, realm_alert_words_cache_key,
) )
from zerver.models.bots import BotConfigData as BotConfigData
from zerver.models.bots import BotStorageData as BotStorageData
from zerver.models.bots import Service as Service
from zerver.models.clients import Client as Client from zerver.models.clients import Client as Client
from zerver.models.custom_profile_fields import CustomProfileField as CustomProfileField from zerver.models.custom_profile_fields import CustomProfileField as CustomProfileField
from zerver.models.custom_profile_fields import CustomProfileFieldValue as CustomProfileFieldValue from zerver.models.custom_profile_fields import CustomProfileFieldValue as CustomProfileFieldValue
@ -130,83 +133,6 @@ def query_for_ids(
return query return query
# Interfaces for services
# They provide additional functionality like parsing message to obtain query URL, data to be sent to URL,
# and parsing the response.
GENERIC_INTERFACE = "GenericService"
SLACK_INTERFACE = "SlackOutgoingWebhookService"
# A Service corresponds to either an outgoing webhook bot or an embedded bot.
# The type of Service is determined by the bot_type field of the referenced
# UserProfile.
#
# If the Service is an outgoing webhook bot:
# - name is any human-readable identifier for the Service
# - base_url is the address of the third-party site
# - token is used for authentication with the third-party site
#
# If the Service is an embedded bot:
# - name is the canonical name for the type of bot (e.g. 'xkcd' for an instance
# of the xkcd bot); multiple embedded bots can have the same name, but all
# embedded bots with the same name will run the same code
# - base_url and token are currently unused
class Service(models.Model):
name = models.CharField(max_length=UserProfile.MAX_NAME_LENGTH)
# Bot user corresponding to the Service. The bot_type of this user
# determines the type of service. If non-bot services are added later,
# user_profile can also represent the owner of the Service.
user_profile = models.ForeignKey(UserProfile, on_delete=CASCADE)
base_url = models.TextField()
token = models.TextField()
# Interface / API version of the service.
interface = models.PositiveSmallIntegerField(default=1)
# Valid interfaces are {generic, zulip_bot_service, slack}
GENERIC = 1
SLACK = 2
ALLOWED_INTERFACE_TYPES = [
GENERIC,
SLACK,
]
# N.B. If we used Django's choice=... we would get this for free (kinda)
_interfaces: Dict[int, str] = {
GENERIC: GENERIC_INTERFACE,
SLACK: SLACK_INTERFACE,
}
def interface_name(self) -> str:
# Raises KeyError if invalid
return self._interfaces[self.interface]
def get_bot_services(user_profile_id: int) -> List[Service]:
return list(Service.objects.filter(user_profile_id=user_profile_id))
def get_service_profile(user_profile_id: int, service_name: str) -> Service:
return Service.objects.get(user_profile_id=user_profile_id, name=service_name)
class BotStorageData(models.Model):
bot_profile = models.ForeignKey(UserProfile, on_delete=CASCADE)
key = models.TextField(db_index=True)
value = models.TextField()
class Meta:
unique_together = ("bot_profile", "key")
class BotConfigData(models.Model):
bot_profile = models.ForeignKey(UserProfile, on_delete=CASCADE)
key = models.TextField(db_index=True)
value = models.TextField()
class Meta:
unique_together = ("bot_profile", "key")
class AlertWord(models.Model): class AlertWord(models.Model):
# Realm isn't necessary, but it's a nice denormalization. Users # Realm isn't necessary, but it's a nice denormalization. Users
# never move to another realm, so it's static, and having Realm # never move to another realm, so it's static, and having Realm

82
zerver/models/bots.py Normal file
View File

@ -0,0 +1,82 @@
from typing import Dict, List
from django.db import models
from django.db.models import CASCADE
from zerver.models.users import UserProfile
# Interfaces for services
# They provide additional functionality like parsing message to obtain query URL, data to be sent to URL,
# and parsing the response.
GENERIC_INTERFACE = "GenericService"
SLACK_INTERFACE = "SlackOutgoingWebhookService"
# A Service corresponds to either an outgoing webhook bot or an embedded bot.
# The type of Service is determined by the bot_type field of the referenced
# UserProfile.
#
# If the Service is an outgoing webhook bot:
# - name is any human-readable identifier for the Service
# - base_url is the address of the third-party site
# - token is used for authentication with the third-party site
#
# If the Service is an embedded bot:
# - name is the canonical name for the type of bot (e.g. 'xkcd' for an instance
# of the xkcd bot); multiple embedded bots can have the same name, but all
# embedded bots with the same name will run the same code
# - base_url and token are currently unused
class Service(models.Model):
name = models.CharField(max_length=UserProfile.MAX_NAME_LENGTH)
# Bot user corresponding to the Service. The bot_type of this user
# determines the type of service. If non-bot services are added later,
# user_profile can also represent the owner of the Service.
user_profile = models.ForeignKey(UserProfile, on_delete=CASCADE)
base_url = models.TextField()
token = models.TextField()
# Interface / API version of the service.
interface = models.PositiveSmallIntegerField(default=1)
# Valid interfaces are {generic, zulip_bot_service, slack}
GENERIC = 1
SLACK = 2
ALLOWED_INTERFACE_TYPES = [
GENERIC,
SLACK,
]
# N.B. If we used Django's choice=... we would get this for free (kinda)
_interfaces: Dict[int, str] = {
GENERIC: GENERIC_INTERFACE,
SLACK: SLACK_INTERFACE,
}
def interface_name(self) -> str:
# Raises KeyError if invalid
return self._interfaces[self.interface]
def get_bot_services(user_profile_id: int) -> List[Service]:
return list(Service.objects.filter(user_profile_id=user_profile_id))
def get_service_profile(user_profile_id: int, service_name: str) -> Service:
return Service.objects.get(user_profile_id=user_profile_id, name=service_name)
class BotStorageData(models.Model):
bot_profile = models.ForeignKey(UserProfile, on_delete=CASCADE)
key = models.TextField(db_index=True)
value = models.TextField()
class Meta:
unique_together = ("bot_profile", "key")
class BotConfigData(models.Model):
bot_profile = models.ForeignKey(UserProfile, on_delete=CASCADE)
key = models.TextField(db_index=True)
value = models.TextField()
class Meta:
unique_together = ("bot_profile", "key")

View File

@ -17,14 +17,8 @@ from zerver.lib.bot_lib import get_bot_handler
from zerver.lib.integrations import EMBEDDED_BOTS, WebhookIntegration from zerver.lib.integrations import EMBEDDED_BOTS, WebhookIntegration
from zerver.lib.test_classes import UploadSerializeMixin, ZulipTestCase from zerver.lib.test_classes import UploadSerializeMixin, ZulipTestCase
from zerver.lib.test_helpers import avatar_disk_path, get_test_image_file from zerver.lib.test_helpers import avatar_disk_path, get_test_image_file
from zerver.models import ( from zerver.models import Realm, RealmUserDefault, Service, Subscription, UserProfile
Realm, from zerver.models.bots import get_bot_services
RealmUserDefault,
Service,
Subscription,
UserProfile,
get_bot_services,
)
from zerver.models.realms import get_realm from zerver.models.realms import get_realm
from zerver.models.streams import get_stream from zerver.models.streams import get_stream
from zerver.models.users import get_user, is_cross_realm_bot_email from zerver.models.users import get_user, is_cross_realm_bot_email

View File

@ -6,7 +6,8 @@ from typing_extensions import override
from zerver.lib.bot_lib import EmbeddedBotQuitError from zerver.lib.bot_lib import EmbeddedBotQuitError
from zerver.lib.display_recipient import get_display_recipient from zerver.lib.display_recipient import get_display_recipient
from zerver.lib.test_classes import ZulipTestCase from zerver.lib.test_classes import ZulipTestCase
from zerver.models import UserProfile, get_service_profile from zerver.models import UserProfile
from zerver.models.bots import get_service_profile
from zerver.models.realms import get_realm from zerver.models.realms import get_realm
from zerver.models.users import get_user from zerver.models.users import get_user

View File

@ -12,7 +12,8 @@ from zerver.lib.outgoing_webhook import get_service_interface_class, process_suc
from zerver.lib.test_classes import ZulipTestCase from zerver.lib.test_classes import ZulipTestCase
from zerver.lib.timestamp import datetime_to_timestamp from zerver.lib.timestamp import datetime_to_timestamp
from zerver.lib.topic import TOPIC_NAME from zerver.lib.topic import TOPIC_NAME
from zerver.models import SLACK_INTERFACE, Message from zerver.models import Message
from zerver.models.bots import SLACK_INTERFACE
from zerver.models.realms import get_realm from zerver.models.realms import get_realm
from zerver.models.scheduled_jobs import NotificationTriggers from zerver.models.scheduled_jobs import NotificationTriggers
from zerver.models.streams import get_stream from zerver.models.streams import get_stream

View File

@ -104,8 +104,8 @@ from zerver.models import (
Stream, Stream,
UserMessage, UserMessage,
UserProfile, UserProfile,
get_bot_services,
) )
from zerver.models.bots import get_bot_services
from zerver.models.clients import get_client from zerver.models.clients import get_client
from zerver.models.prereg_users import filter_to_valid_prereg_users from zerver.models.prereg_users import filter_to_valid_prereg_users
from zerver.models.users import get_system_bot, get_user_profile_by_id from zerver.models.users import get_system_bot, get_user_profile_by_id