mirror of
https://github.com/zulip/zulip.git
synced 2025-11-04 22:13:26 +00:00
Add bots to page_params and send events on bot creation
(imported from commit ce418b4f056576d57f82d26af621473c730c12d8)
This commit is contained in:
@@ -15,9 +15,9 @@ from zerver.models import Realm, RealmEmoji, Stream, UserProfile, UserActivity,
|
||||
get_stream_cache_key, to_dict_cache_key_id, is_super_user, \
|
||||
UserActivityInterval, get_active_user_dicts_in_realm, get_active_streams, \
|
||||
realm_filters_for_domain, RealmFilter, receives_offline_notifications, \
|
||||
ScheduledJob, realm_filters_for_domain, RealmFilter
|
||||
ScheduledJob, realm_filters_for_domain, RealmFilter, get_active_bot_dicts_in_realm
|
||||
|
||||
from zerver.lib.avatar import get_avatar_url
|
||||
from zerver.lib.avatar import get_avatar_url, avatar_url
|
||||
from guardian.shortcuts import assign_perm, remove_perm
|
||||
|
||||
from django.db import transaction, IntegrityError
|
||||
@@ -96,6 +96,15 @@ def stream_user_ids(stream):
|
||||
|
||||
return [sub['user_profile_id'] for sub in subscriptions.values('user_profile_id')]
|
||||
|
||||
def bot_owner_userids(user_profile):
|
||||
is_private_bot = (
|
||||
user_profile.default_sending_stream and user_profile.default_sending_stream.invite_only or
|
||||
user_profile.default_events_register_stream and user_profile.default_events_register_stream.invite_only)
|
||||
if is_private_bot:
|
||||
return (user_profile.bot_owner_id,)
|
||||
else:
|
||||
return active_user_ids(user_profile.realm)
|
||||
|
||||
def notify_created_user(user_profile):
|
||||
event = dict(type="realm_user", op="add",
|
||||
person=dict(email=user_profile.email,
|
||||
@@ -104,6 +113,27 @@ def notify_created_user(user_profile):
|
||||
is_bot=user_profile.is_bot))
|
||||
send_event(event, active_user_ids(user_profile.realm))
|
||||
|
||||
def notify_created_bot(user_profile):
|
||||
|
||||
def stream_name(stream):
|
||||
if not stream:
|
||||
return None
|
||||
return stream.name
|
||||
|
||||
default_sending_stream_name = stream_name(user_profile.default_sending_stream)
|
||||
default_events_register_stream_name = stream_name(user_profile.default_events_register_stream)
|
||||
|
||||
event = dict(type="realm_bot", op="add",
|
||||
bot=dict(email=user_profile.email,
|
||||
full_name=user_profile.full_name,
|
||||
api_key=user_profile.api_key,
|
||||
default_sending_stream=default_sending_stream_name,
|
||||
default_events_register_stream=default_events_register_stream_name,
|
||||
default_all_public_streams=user_profile.default_all_public_streams,
|
||||
avatar_url=avatar_url(user_profile),
|
||||
))
|
||||
send_event(event, bot_owner_userids(user_profile))
|
||||
|
||||
def do_create_user(email, password, realm, full_name, short_name,
|
||||
active=True, bot=False, bot_owner=None,
|
||||
avatar_source=UserProfile.AVATAR_FROM_GRAVATAR,
|
||||
@@ -129,6 +159,8 @@ def do_create_user(email, password, realm, full_name, short_name,
|
||||
default_all_public_streams=default_all_public_streams)
|
||||
|
||||
notify_created_user(user_profile)
|
||||
if bot:
|
||||
notify_created_bot(user_profile)
|
||||
return user_profile
|
||||
|
||||
def user_sessions(user_profile):
|
||||
@@ -2131,6 +2163,16 @@ def get_realm_user_dicts(user_profile):
|
||||
'full_name' : userdict['full_name']}
|
||||
for userdict in get_active_user_dicts_in_realm(user_profile.realm)]
|
||||
|
||||
def get_realm_bot_dicts(user_profile):
|
||||
return [{'email' : botdict['email'],
|
||||
'full_name' : botdict['full_name'],
|
||||
'api_key' : botdict['api_key'],
|
||||
'default_sending_stream': botdict['default_sending_stream__name'],
|
||||
'default_events_register_stream': botdict['default_events_register_stream__name'],
|
||||
'default_all_public_streams': botdict['default_all_public_streams'],
|
||||
'avatar_url': get_avatar_url(botdict['avatar_source'], botdict['email']),
|
||||
}
|
||||
for botdict in get_active_bot_dicts_in_realm(user_profile.realm)]
|
||||
|
||||
# Fetch initial data. When event_types is not specified, clients want
|
||||
# all event types. Whenever you add new code to this function, you
|
||||
@@ -2181,6 +2223,9 @@ def fetch_initial_state_data(user_profile, event_types, queue_id):
|
||||
if want('realm_user'):
|
||||
state['realm_users'] = get_realm_user_dicts(user_profile)
|
||||
|
||||
if want('realm_bot'):
|
||||
state['realm_bots'] = get_realm_bot_dicts(user_profile)
|
||||
|
||||
if want('referral'):
|
||||
state['referrals'] = {'granted': user_profile.invites_granted,
|
||||
'used': user_profile.invites_used}
|
||||
@@ -2221,6 +2266,11 @@ def apply_events(state, events, user_profile):
|
||||
for p in state['realm_users']:
|
||||
if our_person(p):
|
||||
p.update(person)
|
||||
|
||||
elif event['type'] == 'realm_bot':
|
||||
if event['op'] == 'add':
|
||||
state['realm_bots'].append(event['bot'])
|
||||
|
||||
elif event['type'] == 'stream':
|
||||
if event['op'] == 'update':
|
||||
# For legacy reasons, we call stream data 'subscriptions' in
|
||||
|
||||
@@ -5,6 +5,7 @@ from functools import wraps
|
||||
from django.core.cache import cache as djcache
|
||||
from django.core.cache import get_cache
|
||||
from django.conf import settings
|
||||
from django.db.models import Q
|
||||
|
||||
from zerver.lib.utils import statsd, statsd_key, make_safe_digest
|
||||
import time
|
||||
@@ -239,6 +240,9 @@ def cache_save_user_profile(user_profile):
|
||||
def active_user_dicts_in_realm_cache_key(realm):
|
||||
return "active_user_dicts_in_realm:%s" % (realm.id,)
|
||||
|
||||
def active_bot_dicts_in_realm_cache_key(realm):
|
||||
return "active_bot_dicts_in_realm:%s" % (realm.id,)
|
||||
|
||||
def get_stream_cache_key(stream_name, realm):
|
||||
from zerver.models import Realm
|
||||
if isinstance(realm, Realm):
|
||||
@@ -267,6 +271,13 @@ def flush_user_profile(sender, **kwargs):
|
||||
len(set(['full_name', 'short_name', 'email', 'is_active']) & set(kwargs['update_fields'])) > 0:
|
||||
cache_delete(active_user_dicts_in_realm_cache_key(user_profile.realm))
|
||||
|
||||
# Invalidate our active_bots_in_realm info dict if any bot has changed
|
||||
bot_fields = {'full_name', 'api_key', 'avatar_source',
|
||||
'default_all_public_streams', 'is_active',
|
||||
'default_sending_stream', 'default_events_register_stream'}
|
||||
if user_profile.is_bot and (kwargs['update_fields'] is None or bot_fields & set(kwargs['update_fields'])):
|
||||
cache_delete(active_bot_dicts_in_realm_cache_key(user_profile.realm))
|
||||
|
||||
# Invalidate realm-wide alert words cache if any user in the realm has changed
|
||||
# alert words
|
||||
if kwargs['update_fields'] is None or "alert_words" in kwargs['update_fields']:
|
||||
@@ -282,6 +293,7 @@ def flush_realm(sender, **kwargs):
|
||||
|
||||
if realm.deactivated:
|
||||
cache_delete(active_user_dicts_in_realm_cache_key(realm))
|
||||
cache_delete(active_bot_dicts_in_realm_cache_key(realm))
|
||||
cache_delete(realm_alert_words_cache_key(realm))
|
||||
|
||||
def realm_alert_words_cache_key(realm):
|
||||
@@ -290,7 +302,15 @@ def realm_alert_words_cache_key(realm):
|
||||
# Called by models.py to flush the stream cache whenever we save a stream
|
||||
# object.
|
||||
def flush_stream(sender, **kwargs):
|
||||
from zerver.models import UserProfile
|
||||
stream = kwargs['instance']
|
||||
items_for_memcached = {}
|
||||
items_for_memcached[get_stream_cache_key(stream.name, stream.realm)] = (stream,)
|
||||
cache_set_many(items_for_memcached)
|
||||
|
||||
if kwargs['update_fields'] is None or 'name' in kwargs['update_fields'] and \
|
||||
UserProfile.objects.filter(
|
||||
Q(default_sending_stream=stream) |
|
||||
Q(default_events_register_stream=stream)
|
||||
).exists():
|
||||
cache_delete(active_bot_dicts_in_realm_cache_key(stream.realm))
|
||||
|
||||
@@ -41,6 +41,14 @@ def check_bool(var_name, val):
|
||||
return '%s is not a boolean' % (var_name,)
|
||||
return None
|
||||
|
||||
def check_none_or(sub_validator):
|
||||
def f(var_name, val):
|
||||
if val is None:
|
||||
return
|
||||
else:
|
||||
return sub_validator(var_name, val)
|
||||
return f
|
||||
|
||||
def check_list(sub_validator, length=None):
|
||||
def f(var_name, val):
|
||||
if not isinstance(val, list):
|
||||
|
||||
@@ -9,7 +9,8 @@ from zerver.lib.cache import cache_with_key, flush_user_profile, flush_realm, \
|
||||
user_profile_by_id_cache_key, user_profile_by_email_cache_key, \
|
||||
generic_bulk_cached_fetch, cache_set, flush_stream, \
|
||||
display_recipient_cache_key, cache_delete, \
|
||||
get_stream_cache_key, active_user_dicts_in_realm_cache_key
|
||||
get_stream_cache_key, active_user_dicts_in_realm_cache_key, \
|
||||
active_bot_dicts_in_realm_cache_key
|
||||
from zerver.lib.utils import make_safe_digest, generate_random_token
|
||||
from django.db import transaction, IntegrityError
|
||||
from zerver.lib.avatar import gravatar_hash, get_avatar_url
|
||||
@@ -1034,6 +1035,17 @@ def get_active_user_dicts_in_realm(realm):
|
||||
return UserProfile.objects.filter(realm=realm, is_active=True) \
|
||||
.values('id', 'full_name', 'short_name', 'email', 'is_bot')
|
||||
|
||||
@cache_with_key(active_bot_dicts_in_realm_cache_key, timeout=3600*24*7)
|
||||
def get_active_bot_dicts_in_realm(realm):
|
||||
return UserProfile.objects.filter(realm=realm, is_active=True, is_bot=True) \
|
||||
.values('id', 'full_name', 'short_name',
|
||||
'email', 'default_sending_stream__name',
|
||||
'default_events_register_stream__name',
|
||||
'default_all_public_streams', 'api_key',
|
||||
'avatar_source') \
|
||||
.select_related('default_sending_stream',
|
||||
'default_events_register_stream')
|
||||
|
||||
def get_prereg_user_by_email(email):
|
||||
# A user can be invited many times, so only return the result of the latest
|
||||
# invite.
|
||||
|
||||
@@ -23,6 +23,7 @@ from zerver.lib.actions import (
|
||||
do_set_muted_topics,
|
||||
do_set_realm_name,
|
||||
do_update_pointer,
|
||||
do_create_user,
|
||||
fetch_initial_state_data,
|
||||
)
|
||||
|
||||
@@ -30,7 +31,7 @@ from zerver.lib.event_queue import allocate_client_descriptor
|
||||
from zerver.lib.test_helpers import AuthedTestCase, POSTRequestMock
|
||||
from zerver.lib.validator import (
|
||||
check_bool, check_dict, check_int, check_list, check_string,
|
||||
equals,
|
||||
equals, check_none_or
|
||||
)
|
||||
|
||||
from zerver.views import _default_all_public_streams, _default_narrow
|
||||
@@ -202,6 +203,8 @@ class EventsRegisterTest(AuthedTestCase):
|
||||
state['realm_users'] = {u['email']: u for u in state['realm_users']}
|
||||
state['subscriptions'] = {u['name']: u for u in state['subscriptions']}
|
||||
state['unsubscribed'] = {u['name']: u for u in state['unsubscribed']}
|
||||
if 'realm_bots' in state:
|
||||
state['realm_bots'] = {u['email']: u for u in state['realm_bots']}
|
||||
normalize(state1)
|
||||
normalize(state2)
|
||||
self.assertEqual(state1, state2)
|
||||
@@ -243,6 +246,27 @@ class EventsRegisterTest(AuthedTestCase):
|
||||
"https://realm.com/my_realm_filter/%(id)s"))
|
||||
self.do_test(lambda: do_remove_realm_filter(get_realm("zulip.com"), "#[123]"))
|
||||
|
||||
def test_create_bot(self):
|
||||
bot_created_checker = check_dict([
|
||||
('type', equals('realm_bot')),
|
||||
('op', equals('add')),
|
||||
('bot', check_dict([
|
||||
('email', check_string),
|
||||
('full_name', check_string),
|
||||
('api_key', check_string),
|
||||
('default_sending_stream', check_none_or(check_string)),
|
||||
('default_events_register_stream', check_none_or(check_string)),
|
||||
('default_all_public_streams', check_bool),
|
||||
('avatar_url', check_string),
|
||||
])),
|
||||
])
|
||||
|
||||
action = lambda: do_create_user('test-bot@zulip.com', '123', get_realm('zulip.com'),
|
||||
'Test Bot', 'test', bot=True, bot_owner=self.user_profile)
|
||||
events = self.do_test(action)
|
||||
error = bot_created_checker('events[1]', events[1])
|
||||
self.assert_on_error(error)
|
||||
|
||||
def test_rename_stream(self):
|
||||
realm = get_realm('zulip.com')
|
||||
stream, _ = create_stream_if_needed(realm, 'old_name')
|
||||
@@ -354,7 +378,6 @@ class EventsRegisterTest(AuthedTestCase):
|
||||
error = stream_update_schema_checker('events[0]', events[0])
|
||||
self.assert_on_error(error)
|
||||
|
||||
|
||||
from zerver.lib.event_queue import EventQueue
|
||||
class EventQueueTest(TestCase):
|
||||
def test_one_event(self):
|
||||
|
||||
@@ -361,9 +361,28 @@ class BotTest(AuthedTestCase):
|
||||
def test_add_bot(self):
|
||||
self.login("hamlet@zulip.com")
|
||||
self.assert_num_bots_equal(0)
|
||||
self.create_bot()
|
||||
events = []
|
||||
with tornado_redirected_to_list(events):
|
||||
result = self.create_bot()
|
||||
self.assert_num_bots_equal(1)
|
||||
|
||||
event = [e for e in events if e['event']['type'] == 'realm_bot'][0]
|
||||
self.assertEqual(
|
||||
dict(
|
||||
type='realm_bot',
|
||||
op='add',
|
||||
bot=dict(email='hambot-bot@zulip.com',
|
||||
full_name='The Bot of Hamlet',
|
||||
api_key=result['api_key'],
|
||||
avatar_url=result['avatar_url'],
|
||||
default_sending_stream=None,
|
||||
default_events_register_stream=None,
|
||||
default_all_public_streams=False,
|
||||
)
|
||||
),
|
||||
event['event']
|
||||
)
|
||||
|
||||
def test_add_bot_with_default_sending_stream(self):
|
||||
self.login("hamlet@zulip.com")
|
||||
self.assert_num_bots_equal(0)
|
||||
@@ -392,6 +411,8 @@ class BotTest(AuthedTestCase):
|
||||
do_make_stream_private(user_profile.realm, "Denmark")
|
||||
|
||||
self.assert_num_bots_equal(0)
|
||||
events = []
|
||||
with tornado_redirected_to_list(events):
|
||||
result = self.create_bot(default_sending_stream='Denmark')
|
||||
self.assert_num_bots_equal(1)
|
||||
self.assertEqual(result['default_sending_stream'], 'Denmark')
|
||||
@@ -399,6 +420,24 @@ class BotTest(AuthedTestCase):
|
||||
profile = get_user_profile_by_email('hambot-bot@zulip.com')
|
||||
self.assertEqual(profile.default_sending_stream.name, 'Denmark')
|
||||
|
||||
event = [e for e in events if e['event']['type'] == 'realm_bot'][0]
|
||||
self.assertEqual(
|
||||
dict(
|
||||
type='realm_bot',
|
||||
op='add',
|
||||
bot=dict(email='hambot-bot@zulip.com',
|
||||
full_name='The Bot of Hamlet',
|
||||
api_key=result['api_key'],
|
||||
avatar_url=result['avatar_url'],
|
||||
default_sending_stream='Denmark',
|
||||
default_events_register_stream=None,
|
||||
default_all_public_streams=False,
|
||||
)
|
||||
),
|
||||
event['event']
|
||||
)
|
||||
self.assertEqual(event['users'], (user_profile.id,))
|
||||
|
||||
def test_add_bot_with_default_sending_stream_private_denied(self):
|
||||
self.login("hamlet@zulip.com")
|
||||
user_profile = get_user_profile_by_email("hamlet@zulip.com")
|
||||
@@ -432,6 +471,8 @@ class BotTest(AuthedTestCase):
|
||||
do_make_stream_private(user_profile.realm, "Denmark")
|
||||
|
||||
self.assert_num_bots_equal(0)
|
||||
events = []
|
||||
with tornado_redirected_to_list(events):
|
||||
result = self.create_bot(default_events_register_stream='Denmark')
|
||||
self.assert_num_bots_equal(1)
|
||||
self.assertEqual(result['default_events_register_stream'], 'Denmark')
|
||||
@@ -439,6 +480,24 @@ class BotTest(AuthedTestCase):
|
||||
profile = get_user_profile_by_email('hambot-bot@zulip.com')
|
||||
self.assertEqual(profile.default_events_register_stream.name, 'Denmark')
|
||||
|
||||
event = [e for e in events if e['event']['type'] == 'realm_bot'][0]
|
||||
self.assertEqual(
|
||||
dict(
|
||||
type='realm_bot',
|
||||
op='add',
|
||||
bot=dict(email='hambot-bot@zulip.com',
|
||||
full_name='The Bot of Hamlet',
|
||||
api_key=result['api_key'],
|
||||
avatar_url=result['avatar_url'],
|
||||
default_sending_stream=None,
|
||||
default_events_register_stream='Denmark',
|
||||
default_all_public_streams=False,
|
||||
)
|
||||
),
|
||||
event['event']
|
||||
)
|
||||
self.assertEqual(event['users'], (user_profile.id,))
|
||||
|
||||
def test_add_bot_with_default_events_register_stream_private_denied(self):
|
||||
self.login("hamlet@zulip.com")
|
||||
user_profile = get_user_profile_by_email("hamlet@zulip.com")
|
||||
|
||||
@@ -870,6 +870,7 @@ def home(request):
|
||||
unsubbed_info = register_ret['unsubscribed'],
|
||||
email_dict = register_ret['email_dict'],
|
||||
people_list = register_ret['realm_users'],
|
||||
bot_list = register_ret['realm_bots'],
|
||||
initial_pointer = register_ret['pointer'],
|
||||
initial_presences = register_ret['presences'],
|
||||
initial_servertime = time.time(), # Used for calculating relative presence age
|
||||
|
||||
Reference in New Issue
Block a user