def test_subscriptions(self) -> None: now = timezone_now() user = [self.example_user('hamlet')] stream = [self.make_stream('test_stream')] acting_user = self.example_user('iago') bulk_add_subscriptions(stream, user, acting_user=acting_user) subscription_creation_logs = RealmAuditLog.objects.filter( event_type=RealmAuditLog.SUBSCRIPTION_CREATED, event_time__gte=now, acting_user=acting_user, modified_user=user[0], modified_stream=stream[0]) self.assertEqual(subscription_creation_logs.count(), 1) self.assertEqual(subscription_creation_logs[0].modified_stream.id, stream[0].id) self.assertEqual(subscription_creation_logs[0].modified_user, user[0]) bulk_remove_subscriptions(user, stream, get_client("website"), acting_user=acting_user) subscription_deactivation_logs = RealmAuditLog.objects.filter( event_type=RealmAuditLog.SUBSCRIPTION_DEACTIVATED, event_time__gte=now, acting_user=acting_user, modified_user=user[0], modified_stream=stream[0]) self.assertEqual(subscription_deactivation_logs.count(), 1) self.assertEqual(subscription_deactivation_logs[0].modified_stream.id, stream[0].id) self.assertEqual(subscription_deactivation_logs[0].modified_user, user[0])
def test_subscriptions(self) -> None: now = timezone_now() user = self.example_user("hamlet") stream = self.make_stream("test_stream") acting_user = self.example_user("iago") bulk_add_subscriptions(user.realm, [stream], [user], acting_user=acting_user) subscription_creation_logs = RealmAuditLog.objects.filter( event_type=RealmAuditLog.SUBSCRIPTION_CREATED, event_time__gte=now, acting_user=acting_user, modified_user=user, modified_stream=stream, ) self.assertEqual(subscription_creation_logs.count(), 1) self.assertEqual(subscription_creation_logs[0].modified_stream.id, stream.id) self.assertEqual(subscription_creation_logs[0].modified_user, user) bulk_remove_subscriptions([user], [stream], get_client("website"), acting_user=acting_user) subscription_deactivation_logs = RealmAuditLog.objects.filter( event_type=RealmAuditLog.SUBSCRIPTION_DEACTIVATED, event_time__gte=now, acting_user=acting_user, modified_user=user, modified_stream=stream, ) self.assertEqual(subscription_deactivation_logs.count(), 1) self.assertEqual(subscription_deactivation_logs[0].modified_stream.id, stream.id) self.assertEqual(subscription_deactivation_logs[0].modified_user, user)
def handle(self, *args: Any, **options: str) -> None: realm = self.get_realm(options) assert realm is not None # Should be ensured by parser stream_to_keep = get_stream(options["stream_to_keep"], realm) stream_to_destroy = get_stream(options["stream_to_destroy"], realm) recipient_to_destroy = stream_to_destroy.recipient recipient_to_keep = stream_to_keep.recipient # The high-level approach here is to move all the messages to # the surviving stream, deactivate all the subscriptions on # the stream to be removed and deactivate the stream, and add # new subscriptions to the stream to keep for any users who # were only on the now-deactivated stream. # Move the messages, and delete the old copies from caches. message_ids_to_clear = list( Message.objects.filter(recipient=recipient_to_destroy).values_list( "id", flat=True)) count = Message.objects.filter(recipient=recipient_to_destroy).update( recipient=recipient_to_keep) print(f"Moved {count} messages") bulk_delete_cache_keys(message_ids_to_clear) # Move the Subscription objects. This algorithm doesn't # preserve any stream settings/colors/etc. from the stream # being destroyed, but it's convenient. existing_subs = Subscription.objects.filter( recipient=recipient_to_keep) users_already_subscribed = { sub.user_profile_id: sub.active for sub in existing_subs } subs_to_deactivate = Subscription.objects.filter( recipient=recipient_to_destroy, active=True) users_to_activate = [ sub.user_profile for sub in subs_to_deactivate if not users_already_subscribed.get(sub.user_profile_id, False) ] if len(subs_to_deactivate) > 0: print(f"Deactivating {len(subs_to_deactivate)} subscriptions") bulk_remove_subscriptions( realm, [sub.user_profile for sub in subs_to_deactivate], [stream_to_destroy], acting_user=None, ) do_deactivate_stream(stream_to_destroy, acting_user=None) if len(users_to_activate) > 0: print(f"Adding {len(users_to_activate)} subscriptions") bulk_add_subscriptions(realm, [stream_to_keep], users_to_activate, acting_user=None)
def handle(self, *args, **options): # type: (*Any, **str) -> None string_id = options['realm'] encoding = sys.getfilesystemencoding() realm = get_realm(force_text(string_id, encoding)) stream_to_keep = get_stream(options["stream_to_keep"], realm) stream_to_destroy = get_stream(options["stream_to_destroy"], realm) recipient_to_destroy = get_recipient(Recipient.STREAM, stream_to_destroy.id) recipient_to_keep = get_recipient(Recipient.STREAM, stream_to_keep.id) # The high-level approach here is to move all the messages to # the surviving stream, deactivate all the subscriptions on # the stream to be removed and deactivate the stream, and add # new subscriptions to the stream to keep for any users who # were only on the now-deactivated stream. # Move the messages, and delete the old copies from caches. message_ids_to_clear = list( Message.objects.filter(recipient=recipient_to_destroy).values_list( "id", flat=True)) count = Message.objects.filter(recipient=recipient_to_destroy).update( recipient=recipient_to_keep) print("Moved %s messages" % (count, )) bulk_delete_cache_keys(message_ids_to_clear) # Move the Subscription objects. This algorithm doesn't # preserve any stream settings/colors/etc. from the stream # being destroyed, but it's convenient. existing_subs = Subscription.objects.filter( recipient=recipient_to_keep) users_already_subscribed = dict( (sub.user_profile_id, sub.active) for sub in existing_subs) subs_to_deactivate = Subscription.objects.filter( recipient=recipient_to_destroy, active=True) users_to_activate = [ sub.user_profile for sub in subs_to_deactivate if not users_already_subscribed.get(sub.user_profile_id, False) ] if len(subs_to_deactivate) > 0: print("Deactivating %s subscriptions" % (len(subs_to_deactivate), )) bulk_remove_subscriptions( [sub.user_profile for sub in subs_to_deactivate], [stream_to_destroy]) do_deactivate_stream(stream_to_destroy) if len(users_to_activate) > 0: print("Adding %s subscriptions" % (len(users_to_activate), )) bulk_add_subscriptions([stream_to_keep], users_to_activate)
def remove_subscriptions_backend(request, user_profile, streams_raw = REQ("subscriptions", validator=check_list(check_string)), principals = REQ(validator=check_list(check_string), default=None)): removing_someone_else = principals and \ set(principals) != set((user_profile.email,)) if removing_someone_else and not user_profile.is_realm_admin: # You can only unsubscribe other people from a stream if you are a realm # admin. return json_error("This action requires administrative rights") streams, _ = list_to_streams(streams_raw, user_profile) for stream in streams: if removing_someone_else and stream.invite_only and \ not subscribed_to_stream(user_profile, stream): # Even as an admin, you can't remove other people from an # invite-only stream you're not on. return json_error("Cannot administer invite-only streams this way") if principals: people_to_unsub = set(principal_to_user_profile( user_profile, principal) for principal in principals) else: people_to_unsub = set([user_profile]) result = dict(removed=[], not_subscribed=[]) # type: Dict[str, List[str]] (removed, not_subscribed) = bulk_remove_subscriptions(people_to_unsub, streams) for (subscriber, stream) in removed: result["removed"].append(stream.name) for (subscriber, stream) in not_subscribed: result["not_subscribed"].append(stream.name) return json_success(result)
def remove_subscriptions_backend( request: HttpRequest, user_profile: UserProfile, streams_raw: Iterable[str]=REQ("subscriptions", validator=remove_subscriptions_schema), principals: Optional[Union[List[str], List[int]]]=REQ(validator=check_principals, default=None), ) -> HttpResponse: removing_someone_else = check_if_removing_someone_else(user_profile, principals) streams_as_dict: List[StreamDict] = [] for stream_name in streams_raw: streams_as_dict.append({"name": stream_name.strip()}) streams, __ = list_to_streams(streams_as_dict, user_profile, admin_access_required=removing_someone_else) if principals: people_to_unsub = {principal_to_user_profile( user_profile, principal) for principal in principals} else: people_to_unsub = {user_profile} result: Dict[str, List[str]] = dict(removed=[], not_removed=[]) (removed, not_subscribed) = bulk_remove_subscriptions(people_to_unsub, streams, request.client, acting_user=user_profile) for (subscriber, removed_stream) in removed: result["removed"].append(removed_stream.name) for (subscriber, not_subscribed_stream) in not_subscribed: result["not_removed"].append(not_subscribed_stream.name) return json_success(result)
def handle(self, **options): # type: (*Any, **Any) -> None if options["domain"] is None or options["stream"] is None or \ (options["users"] is None and options["all_users"] is None): self.print_help("./manage.py", "remove_users_from_stream") exit(1) realm = get_realm(options["domain"]) stream_name = options["stream"].strip() stream = get_stream(stream_name, realm) if options["all_users"]: user_profiles = UserProfile.objects.filter(realm=realm) else: emails = set([email.strip() for email in options["users"].split(",")]) user_profiles = [] for email in emails: user_profiles.append(get_user_profile_by_email(email)) result = bulk_remove_subscriptions(user_profiles, [stream]) not_subscribed = result[1] not_subscribed_users = {tup[0] for tup in not_subscribed} for user_profile in user_profiles: if user_profile in not_subscribed_users: print("%s was not subscribed" % (user_profile.email,)) else: print("Removed %s from %s" % (user_profile.email, stream_name))
def test_subscriptions(self) -> None: now = timezone_now() user = [self.example_user('hamlet')] stream = [self.make_stream('test_stream')] bulk_add_subscriptions(stream, user) subscription_creation_logs = RealmAuditLog.objects.filter(event_type=RealmAuditLog.SUBSCRIPTION_CREATED, event_time__gte=now) self.assertEqual(subscription_creation_logs.count(), 1) self.assertEqual(subscription_creation_logs[0].modified_stream.id, stream[0].id) self.assertEqual(subscription_creation_logs[0].modified_user, user[0]) bulk_remove_subscriptions(user, stream, get_client("website")) subscription_deactivation_logs = RealmAuditLog.objects.filter(event_type=RealmAuditLog.SUBSCRIPTION_DEACTIVATED, event_time__gte=now) self.assertEqual(subscription_deactivation_logs.count(), 1) self.assertEqual(subscription_deactivation_logs[0].modified_stream.id, stream[0].id) self.assertEqual(subscription_deactivation_logs[0].modified_user, user[0])
def handle(self, *args: Any, **options: str) -> None: realm = self.get_realm(options) assert realm is not None # Should be ensured by parser stream_to_keep = get_stream(options["stream_to_keep"], realm) stream_to_destroy = get_stream(options["stream_to_destroy"], realm) recipient_to_destroy = get_stream_recipient(stream_to_destroy.id) recipient_to_keep = get_stream_recipient(stream_to_keep.id) # The high-level approach here is to move all the messages to # the surviving stream, deactivate all the subscriptions on # the stream to be removed and deactivate the stream, and add # new subscriptions to the stream to keep for any users who # were only on the now-deactivated stream. # Move the messages, and delete the old copies from caches. message_ids_to_clear = list(Message.objects.filter( recipient=recipient_to_destroy).values_list("id", flat=True)) count = Message.objects.filter(recipient=recipient_to_destroy).update(recipient=recipient_to_keep) print("Moved %s messages" % (count,)) bulk_delete_cache_keys(message_ids_to_clear) # Move the Subscription objects. This algorithm doesn't # preserve any stream settings/colors/etc. from the stream # being destroyed, but it's convenient. existing_subs = Subscription.objects.filter(recipient=recipient_to_keep) users_already_subscribed = dict((sub.user_profile_id, sub.active) for sub in existing_subs) subs_to_deactivate = Subscription.objects.filter(recipient=recipient_to_destroy, active=True) users_to_activate = [ sub.user_profile for sub in subs_to_deactivate if not users_already_subscribed.get(sub.user_profile_id, False) ] if len(subs_to_deactivate) > 0: print("Deactivating %s subscriptions" % (len(subs_to_deactivate),)) bulk_remove_subscriptions([sub.user_profile for sub in subs_to_deactivate], [stream_to_destroy], self.get_client()) do_deactivate_stream(stream_to_destroy) if len(users_to_activate) > 0: print("Adding %s subscriptions" % (len(users_to_activate),)) bulk_add_subscriptions([stream_to_keep], users_to_activate)
def test_subscriptions(self): # type: () -> None now = timezone_now() user = [self.example_user('hamlet')] stream = [self.make_stream('test_stream')] bulk_add_subscriptions(stream, user) subscription_creation_logs = RealmAuditLog.objects.filter(event_type='subscription_created', event_time__gte=now) self.assertEqual(subscription_creation_logs.count(), 1) self.assertEqual(subscription_creation_logs[0].modified_stream.id, stream[0].id) self.assertEqual(subscription_creation_logs[0].modified_user, user[0]) bulk_remove_subscriptions(user, stream) subscription_deactivation_logs = RealmAuditLog.objects.filter(event_type='subscription_deactivated', event_time__gte=now) self.assertEqual(subscription_deactivation_logs.count(), 1) self.assertEqual(subscription_deactivation_logs[0].modified_stream.id, stream[0].id) self.assertEqual(subscription_deactivation_logs[0].modified_user, user[0])
def remove_subscriptions_backend( request: HttpRequest, user_profile: UserProfile, streams_raw: Iterable[str] = REQ("subscriptions", validator=check_list(check_string)), principals: Optional[Union[List[str], List[int]]] = REQ( validator=check_variable_type( [check_list(check_string), check_list(check_int)]), default=None), ) -> HttpResponse: # TODO: Extract this as a function to improve readability removing_someone_else = False if principals is not None and len(principals) > 0: if len(principals) == 1: if isinstance(principals[0], int): removing_someone_else = principals[0] != user_profile.id else: removing_someone_else = principals[0] != user_profile.email else: # nocoverage # TODO: Fix me. removing_someone_else = True if removing_someone_else and not user_profile.is_realm_admin: # You can only unsubscribe other people from a stream if you are a realm # admin (whether the stream is public or private). return json_error(_("This action requires administrative rights")) streams_as_dict = [] for stream_name in streams_raw: streams_as_dict.append({"name": stream_name.strip()}) streams, __ = list_to_streams(streams_as_dict, user_profile) if principals: people_to_unsub = { principal_to_user_profile(user_profile, principal) for principal in principals } else: people_to_unsub = {user_profile} result: Dict[str, List[str]] = dict(removed=[], not_removed=[]) (removed, not_subscribed) = bulk_remove_subscriptions(people_to_unsub, streams, request.client, acting_user=user_profile) for (subscriber, removed_stream) in removed: result["removed"].append(removed_stream.name) for (subscriber, not_subscribed_stream) in not_subscribed: result["not_removed"].append(not_subscribed_stream.name) return json_success(result)
def test_subscriptions(self) -> None: now = timezone_now() user = [self.example_user('hamlet')] stream = [self.make_stream('test_stream')] bulk_add_subscriptions(stream, user) subscription_creation_logs = RealmAuditLog.objects.filter( event_type='subscription_created', event_time__gte=now) self.assertEqual(subscription_creation_logs.count(), 1) self.assertEqual(subscription_creation_logs[0].modified_stream.id, stream[0].id) self.assertEqual(subscription_creation_logs[0].modified_user, user[0]) bulk_remove_subscriptions(user, stream) subscription_deactivation_logs = RealmAuditLog.objects.filter( event_type='subscription_deactivated', event_time__gte=now) self.assertEqual(subscription_deactivation_logs.count(), 1) self.assertEqual(subscription_deactivation_logs[0].modified_stream.id, stream[0].id) self.assertEqual(subscription_deactivation_logs[0].modified_user, user[0])
def remove_subscriptions_backend( request: HttpRequest, user_profile: UserProfile, streams_raw: Iterable[Text] = REQ("subscriptions", validator=check_list(check_string)), principals: Optional[Iterable[Text]] = REQ( validator=check_list(check_string), default=None), ) -> HttpResponse: removing_someone_else = principals and \ set(principals) != set((user_profile.email,)) if removing_someone_else and not user_profile.is_realm_admin: # You can only unsubscribe other people from a stream if you are a realm # admin. return json_error(_("This action requires administrative rights")) streams_as_dict = [] for stream_name in streams_raw: streams_as_dict.append({"name": stream_name.strip()}) streams, __ = list_to_streams(streams_as_dict, user_profile) for stream in streams: if removing_someone_else and stream.invite_only and \ not subscribed_to_stream(user_profile, stream.id): # Even as an admin, you can't remove other people from an # invite-only stream you're not on. return json_error( _("Cannot administer invite-only streams this way")) if principals: people_to_unsub = set( principal_to_user_profile(user_profile, principal) for principal in principals) else: people_to_unsub = set([user_profile]) result = dict(removed=[], not_subscribed=[]) # type: Dict[str, List[Text]] (removed, not_subscribed) = bulk_remove_subscriptions(people_to_unsub, streams, acting_user=user_profile) for (subscriber, removed_stream) in removed: result["removed"].append(removed_stream.name) for (subscriber, not_subscribed_stream) in not_subscribed: result["not_subscribed"].append(not_subscribed_stream.name) return json_success(result)
def handle(self, **options: Any) -> None: realm = self.get_realm(options) assert realm is not None # Should be ensured by parser user_profiles = self.get_users(options, realm) stream_name = options["stream"].strip() stream = get_stream(stream_name, realm) result = bulk_remove_subscriptions(user_profiles, [stream], self.get_client(), acting_user=None) not_subscribed = result[1] not_subscribed_users = {tup[0] for tup in not_subscribed} for user_profile in user_profiles: if user_profile in not_subscribed_users: print(f"{user_profile.delivery_email} was not subscribed") else: print(f"Removed {user_profile.delivery_email} from {stream_name}")
def handle(self, **options: Any) -> None: realm = self.get_realm(options) assert realm is not None # Should be ensured by parser user_profiles = self.get_users(options, realm) stream_name = options["stream"].strip() stream = get_stream(stream_name, realm) result = bulk_remove_subscriptions(user_profiles, [stream], self.get_client()) not_subscribed = result[1] not_subscribed_users = {tup[0] for tup in not_subscribed} for user_profile in user_profiles: if user_profile in not_subscribed_users: print("%s was not subscribed" % (user_profile.email,)) else: print("Removed %s from %s" % (user_profile.email, stream_name))
def handle(self, **options): # type: (**Any) -> None realm = self.get_realm(options) user_profiles = self.get_users(options, realm) stream_name = options["stream"].strip() stream = get_stream(stream_name, realm) result = bulk_remove_subscriptions(user_profiles, [stream]) not_subscribed = result[1] not_subscribed_users = {tup[0] for tup in not_subscribed} for user_profile in user_profiles: if user_profile in not_subscribed_users: print("%s was not subscribed" % (user_profile.email, )) else: print("Removed %s from %s" % (user_profile.email, stream_name))
def handle(self, **options: Any) -> None: realm = self.get_realm(options) assert realm is not None # Should be ensured by parser user_profiles = self.get_users(options, realm) stream_name = options["stream"].strip() stream = get_stream(stream_name, realm) result = bulk_remove_subscriptions(user_profiles, [stream]) not_subscribed = result[1] not_subscribed_users = {tup[0] for tup in not_subscribed} for user_profile in user_profiles: if user_profile in not_subscribed_users: print("%s was not subscribed" % (user_profile.email,)) else: print("Removed %s from %s" % (user_profile.email, stream_name))
def remove_subscriptions_backend( request: HttpRequest, user_profile: UserProfile, streams_raw: Iterable[str] = REQ("subscriptions", validator=remove_subscriptions_schema), principals: Optional[Union[List[str], List[int]]] = REQ(validator=check_principals, default=None), ) -> HttpResponse: removing_someone_else = check_if_removing_someone_else( user_profile, principals) if removing_someone_else and not user_profile.is_realm_admin: # You can only unsubscribe other people from a stream if you are a realm # admin (whether the stream is public or private). return json_error(_("This action requires administrative rights")) streams_as_dict = [] for stream_name in streams_raw: streams_as_dict.append({"name": stream_name.strip()}) streams, __ = list_to_streams(streams_as_dict, user_profile) if principals: people_to_unsub = { principal_to_user_profile(user_profile, principal) for principal in principals } else: people_to_unsub = {user_profile} result: Dict[str, List[str]] = dict(removed=[], not_removed=[]) (removed, not_subscribed) = bulk_remove_subscriptions(people_to_unsub, streams, request.client, acting_user=user_profile) for (subscriber, removed_stream) in removed: result["removed"].append(removed_stream.name) for (subscriber, not_subscribed_stream) in not_subscribed: result["not_removed"].append(not_subscribed_stream.name) return json_success(result)
def remove_subscriptions_backend( request: HttpRequest, user_profile: UserProfile, streams_raw: Iterable[Text]=REQ("subscriptions", validator=check_list(check_string)), principals: Optional[Iterable[Text]]=REQ(validator=check_list(check_string), default=None), ) -> HttpResponse: removing_someone_else = principals and \ set(principals) != set((user_profile.email,)) if removing_someone_else and not user_profile.is_realm_admin: # You can only unsubscribe other people from a stream if you are a realm # admin. return json_error(_("This action requires administrative rights")) streams_as_dict = [] for stream_name in streams_raw: streams_as_dict.append({"name": stream_name.strip()}) streams, __ = list_to_streams(streams_as_dict, user_profile) for stream in streams: if removing_someone_else and stream.invite_only and \ not subscribed_to_stream(user_profile, stream.id): # Even as an admin, you can't remove other people from an # invite-only stream you're not on. return json_error(_("Cannot administer invite-only streams this way")) if principals: people_to_unsub = set(principal_to_user_profile( user_profile, principal) for principal in principals) else: people_to_unsub = set([user_profile]) result = dict(removed=[], not_subscribed=[]) # type: Dict[str, List[Text]] (removed, not_subscribed) = bulk_remove_subscriptions(people_to_unsub, streams, acting_user=user_profile) for (subscriber, removed_stream) in removed: result["removed"].append(removed_stream.name) for (subscriber, not_subscribed_stream) in not_subscribed: result["not_subscribed"].append(not_subscribed_stream.name) return json_success(result)
def unsubscribe(self, user_profile: UserProfile, stream_name: str) -> None: client = get_client("website") stream = get_stream(stream_name, user_profile.realm) bulk_remove_subscriptions([user_profile], [stream], client)
def unsubscribe_from_stream(self, email, stream_name): # type: (Text, Text) -> None user_profile = get_user_profile_by_email(email) stream = get_stream(stream_name, user_profile.realm) bulk_remove_subscriptions([user_profile], [stream])
def remove_subscriptions_backend( request: HttpRequest, user_profile: UserProfile, streams_raw: Iterable[str]=REQ("subscriptions", validator=check_list(check_string)), principals: Optional[Iterable[str]]=REQ(validator=check_list(check_string), default=None), ) -> HttpResponse: removing_someone_else = principals and \ set(principals) != set((user_profile.email,)) if removing_someone_else and not user_profile.is_realm_admin: # You can only unsubscribe other people from a stream if you are a realm # admin (whether the stream is public or private). return json_error(_("This action requires administrative rights")) streams_as_dict = [] for stream_name in streams_raw: streams_as_dict.append({"name": stream_name.strip()}) streams, __ = list_to_streams(streams_as_dict, user_profile) if principals: people_to_unsub = set(principal_to_user_profile( user_profile, principal) for principal in principals) else: people_to_unsub = set([user_profile]) result = dict(removed=[], not_subscribed=[]) # type: Dict[str, List[str]] (removed, not_subscribed) = bulk_remove_subscriptions(people_to_unsub, streams, request.client, acting_user=user_profile) # Notify affected user when unsubscribed by the realm admin email_to_user_profile = dict() # type: Dict[str, UserProfile] notifications = [] notify_stream_names = [] for (subscriber, removed_stream) in removed: if(user_profile.email != subscriber.email): result["removed"].append(removed_stream.name) email_to_user_profile[subscriber.email] = subscriber notify_stream_names.append("".join(removed_stream.name)) msg = you_were_just_unsubscribed_message( acting_user=user_profile, stream_names=notify_stream_names, ) # Send notification using the NOTIFICATION_BOT sender = get_system_bot(settings.NOTIFICATION_BOT) notifications.append(internal_prep_private_message( realm=user_profile.realm, sender=sender, recipient_user=email_to_user_profile[subscriber.email], content=msg)) for (subscriber, not_subscribed_stream) in not_subscribed: result["not_subscribed"].append(not_subscribed_stream.name) if len(notifications) > 0: do_send_messages(notifications) return json_success(result)
def unsubscribe(self, user_profile, stream_name): # type: (UserProfile, Text) -> None stream = get_stream(stream_name, user_profile.realm) bulk_remove_subscriptions([user_profile], [stream])
def unsubscribe(self, user_profile: UserProfile, stream_name: Text) -> None: stream = get_stream(stream_name, user_profile.realm) bulk_remove_subscriptions([user_profile], [stream])
def unsubscribe(self, user_profile: UserProfile, stream_name: str) -> None: stream = get_stream(stream_name, user_profile.realm) bulk_remove_subscriptions([user_profile], [stream])