def get_recipient_info(recipient: Recipient, sender_id: int, stream_topic: Optional[StreamTopicTarget], possibly_mentioned_user_ids: AbstractSet[int]=set(), possible_wildcard_mention: bool=True) -> RecipientInfoResult:
    stream_push_user_ids: Set[int] = set()
    stream_email_user_ids: Set[int] = set()
    wildcard_mention_user_ids: Set[int] = set()
    if (recipient.type == Recipient.PERSONAL):
        message_to_user_ids = list({recipient.type_id, sender_id})
        assert (len(message_to_user_ids) in [1, 2])
    elif (recipient.type == Recipient.STREAM):
        assert (stream_topic is not None)
        user_ids_muting_topic = stream_topic.user_ids_muting_topic()
        subscription_rows = stream_topic.get_active_subscriptions().annotate(user_profile_email_notifications=F('user_profile__enable_stream_email_notifications'), user_profile_push_notifications=F('user_profile__enable_stream_push_notifications'), user_profile_wildcard_mentions_notify=F('user_profile__wildcard_mentions_notify')).values('user_profile_id', 'push_notifications', 'email_notifications', 'wildcard_mentions_notify', 'user_profile_email_notifications', 'user_profile_push_notifications', 'user_profile_wildcard_mentions_notify', 'is_muted').order_by('user_profile_id')
        message_to_user_ids = [row['user_profile_id'] for row in subscription_rows]

        def should_send(setting: str, row: Dict[(str, Any)]) -> bool:
            if row['is_muted']:
                return False
            if (row['user_profile_id'] in user_ids_muting_topic):
                return False
            if (row[setting] is not None):
                return row[setting]
            return row[('user_profile_' + setting)]
        stream_push_user_ids = {row['user_profile_id'] for row in subscription_rows if should_send('push_notifications', row)}
        stream_email_user_ids = {row['user_profile_id'] for row in subscription_rows if should_send('email_notifications', row)}
        if possible_wildcard_mention:
            wildcard_mention_user_ids = {row['user_profile_id'] for row in subscription_rows if should_send('wildcard_mentions_notify', row)}
    elif (recipient.type == Recipient.HUDDLE):
        message_to_user_ids = get_huddle_user_ids(recipient)
    else:
        raise ValueError('Bad recipient type')
    message_to_user_id_set = set(message_to_user_ids)
    user_ids = set(message_to_user_id_set)
    user_ids |= possibly_mentioned_user_ids
    if user_ids:
        query = UserProfile.objects.filter(is_active=True).values('id', 'enable_online_push_notifications', 'is_bot', 'bot_type', 'long_term_idle')
        query = query_for_ids(query=query, user_ids=sorted(list(user_ids)), field='id')
        rows = list(query)
    else:
        rows = []

    def get_ids_for(f: Callable[([Dict[(str, Any)]], bool)]) -> Set[int]:
        'Only includes users on the explicit message to line'
        return ({row['id'] for row in rows if f(row)} & message_to_user_id_set)

    def is_service_bot(row: Dict[(str, Any)]) -> bool:
        return (row['is_bot'] and (row['bot_type'] in UserProfile.SERVICE_BOT_TYPES))
    active_user_ids = get_ids_for((lambda r: True))
    push_notify_user_ids = get_ids_for((lambda r: r['enable_online_push_notifications']))
    um_eligible_user_ids = get_ids_for((lambda r: (not is_service_bot(r))))
    long_term_idle_user_ids = get_ids_for((lambda r: r['long_term_idle']))
    default_bot_user_ids = {row['id'] for row in rows if (row['is_bot'] and (row['bot_type'] == UserProfile.DEFAULT_BOT))}
    service_bot_tuples = [(row['id'], row['bot_type']) for row in rows if is_service_bot(row)]
    info: RecipientInfoResult = dict(active_user_ids=active_user_ids, push_notify_user_ids=push_notify_user_ids, stream_push_user_ids=stream_push_user_ids, stream_email_user_ids=stream_email_user_ids, wildcard_mention_user_ids=wildcard_mention_user_ids, um_eligible_user_ids=um_eligible_user_ids, long_term_idle_user_ids=long_term_idle_user_ids, default_bot_user_ids=default_bot_user_ids, service_bot_tuples=service_bot_tuples)
    return info
def get_recipient_info(recipient: Recipient, sender_id: int, stream_topic: Optional[StreamTopicTarget], possibly_mentioned_user_ids: AbstractSet[int]=set(), possible_wildcard_mention: bool=True) -> RecipientInfoResult:
    stream_push_user_ids: Set[int] = set()
    stream_email_user_ids: Set[int] = set()
    wildcard_mention_user_ids: Set[int] = set()
    if (recipient.type == Recipient.PERSONAL):
        message_to_user_ids = list({recipient.type_id, sender_id})
        assert (len(message_to_user_ids) in [1, 2])
    elif (recipient.type == Recipient.STREAM):
        assert (stream_topic is not None)
        user_ids_muting_topic = stream_topic.user_ids_muting_topic()
        subscription_rows = stream_topic.get_active_subscriptions().annotate(user_profile_email_notifications=F('user_profile__enable_stream_email_notifications'), user_profile_push_notifications=F('user_profile__enable_stream_push_notifications'), user_profile_wildcard_mentions_notify=F('user_profile__wildcard_mentions_notify')).values('user_profile_id', 'push_notifications', 'email_notifications', 'wildcard_mentions_notify', 'user_profile_email_notifications', 'user_profile_push_notifications', 'user_profile_wildcard_mentions_notify', 'is_muted').order_by('user_profile_id')
        message_to_user_ids = [row['user_profile_id'] for row in subscription_rows]

        def should_send(setting: str, row: Dict[(str, Any)]) -> bool:
            if row['is_muted']:
                return False
            if (row['user_profile_id'] in user_ids_muting_topic):
                return False
            if (row[setting] is not None):
                return row[setting]
            return row[('user_profile_' + setting)]
        stream_push_user_ids = {row['user_profile_id'] for row in subscription_rows if should_send('push_notifications', row)}
        stream_email_user_ids = {row['user_profile_id'] for row in subscription_rows if should_send('email_notifications', row)}
        if possible_wildcard_mention:
            wildcard_mention_user_ids = {row['user_profile_id'] for row in subscription_rows if should_send('wildcard_mentions_notify', row)}
    elif (recipient.type == Recipient.HUDDLE):
        message_to_user_ids = get_huddle_user_ids(recipient)
    else:
        raise ValueError('Bad recipient type')
    message_to_user_id_set = set(message_to_user_ids)
    user_ids = set(message_to_user_id_set)
    user_ids |= possibly_mentioned_user_ids
    if user_ids:
        query = UserProfile.objects.filter(is_active=True).values('id', 'enable_online_push_notifications', 'is_bot', 'bot_type', 'long_term_idle')
        query = query_for_ids(query=query, user_ids=sorted(list(user_ids)), field='id')
        rows = list(query)
    else:
        rows = []

    def get_ids_for(f: Callable[([Dict[(str, Any)]], bool)]) -> Set[int]:
        'Only includes users on the explicit message to line'
        return ({row['id'] for row in rows if f(row)} & message_to_user_id_set)

    def is_service_bot(row: Dict[(str, Any)]) -> bool:
        return (row['is_bot'] and (row['bot_type'] in UserProfile.SERVICE_BOT_TYPES))
    active_user_ids = get_ids_for((lambda r: True))
    push_notify_user_ids = get_ids_for((lambda r: r['enable_online_push_notifications']))
    um_eligible_user_ids = get_ids_for((lambda r: (not is_service_bot(r))))
    long_term_idle_user_ids = get_ids_for((lambda r: r['long_term_idle']))
    default_bot_user_ids = {row['id'] for row in rows if (row['is_bot'] and (row['bot_type'] == UserProfile.DEFAULT_BOT))}
    service_bot_tuples = [(row['id'], row['bot_type']) for row in rows if is_service_bot(row)]
    info: RecipientInfoResult = dict(active_user_ids=active_user_ids, push_notify_user_ids=push_notify_user_ids, stream_push_user_ids=stream_push_user_ids, stream_email_user_ids=stream_email_user_ids, wildcard_mention_user_ids=wildcard_mention_user_ids, um_eligible_user_ids=um_eligible_user_ids, long_term_idle_user_ids=long_term_idle_user_ids, default_bot_user_ids=default_bot_user_ids, service_bot_tuples=service_bot_tuples)
    return info