2016-07-26 23:26:39 +02:00
|
|
|
import datetime
|
2017-03-06 12:25:37 +01:00
|
|
|
|
|
|
|
from django.conf import settings
|
2019-01-21 19:06:03 +01:00
|
|
|
from typing import Any, Dict, Optional
|
2016-07-26 23:26:39 +02:00
|
|
|
|
|
|
|
from django.http import HttpRequest, HttpResponse
|
2017-04-16 21:32:57 +02:00
|
|
|
from django.utils.timezone import now as timezone_now
|
2016-07-26 23:26:39 +02:00
|
|
|
from django.utils.translation import ugettext as _
|
|
|
|
|
2017-11-04 00:59:22 +01:00
|
|
|
from zerver.decorator import human_users_only
|
2018-12-17 22:04:07 +01:00
|
|
|
from zerver.lib.actions import (
|
2019-01-21 18:19:59 +01:00
|
|
|
do_update_user_status,
|
2018-12-17 22:04:07 +01:00
|
|
|
update_user_presence,
|
|
|
|
)
|
2020-02-06 16:35:22 +01:00
|
|
|
from zerver.lib.presence import (
|
|
|
|
get_presence_response,
|
2020-02-06 17:52:12 +01:00
|
|
|
get_presence_for_user,
|
2020-02-06 16:35:22 +01:00
|
|
|
)
|
2016-07-26 23:26:39 +02:00
|
|
|
from zerver.lib.request import has_request_variables, REQ, JsonableError
|
|
|
|
from zerver.lib.response import json_success, json_error
|
2017-03-06 12:25:37 +01:00
|
|
|
from zerver.lib.timestamp import datetime_to_timestamp
|
2019-01-21 19:06:03 +01:00
|
|
|
from zerver.lib.validator import check_bool, check_capped_string
|
2018-12-07 00:05:57 +01:00
|
|
|
from zerver.models import UserActivity, UserPresence, UserProfile, \
|
2020-03-18 15:19:03 +01:00
|
|
|
get_active_user
|
2016-07-26 23:26:39 +02:00
|
|
|
|
2017-10-27 02:18:49 +02:00
|
|
|
def get_presence_backend(request: HttpRequest, user_profile: UserProfile,
|
2018-04-24 03:47:28 +02:00
|
|
|
email: str) -> HttpResponse:
|
2020-02-02 17:29:05 +01:00
|
|
|
# This isn't used by the webapp; it's available for API use by
|
|
|
|
# bots and other clients. We may want to add slim_presence
|
|
|
|
# support for it (or just migrate its API wholesale) later.
|
2017-02-11 08:38:16 +01:00
|
|
|
try:
|
2020-03-18 15:19:03 +01:00
|
|
|
target = get_active_user(email, user_profile.realm)
|
2017-02-11 08:38:16 +01:00
|
|
|
except UserProfile.DoesNotExist:
|
|
|
|
return json_error(_('No such user'))
|
|
|
|
if target.is_bot:
|
2017-04-12 20:50:23 +02:00
|
|
|
return json_error(_('Presence is not supported for bot users.'))
|
2017-02-11 08:38:16 +01:00
|
|
|
|
2020-02-06 17:52:12 +01:00
|
|
|
presence_dict = get_presence_for_user(target.id)
|
2017-02-11 08:38:16 +01:00
|
|
|
if len(presence_dict) == 0:
|
2020-03-18 15:19:03 +01:00
|
|
|
return json_error(_('No presence data for %s') % (email,))
|
2017-02-11 08:38:16 +01:00
|
|
|
|
|
|
|
# For initial version, we just include the status and timestamp keys
|
|
|
|
result = dict(presence=presence_dict[target.email])
|
2017-03-06 12:25:37 +01:00
|
|
|
aggregated_info = result['presence']['aggregated']
|
2017-04-16 21:32:57 +02:00
|
|
|
aggr_status_duration = datetime_to_timestamp(timezone_now()) - aggregated_info['timestamp']
|
2017-03-06 12:25:37 +01:00
|
|
|
if aggr_status_duration > settings.OFFLINE_THRESHOLD_SECS:
|
|
|
|
aggregated_info['status'] = 'offline'
|
2017-02-11 08:38:16 +01:00
|
|
|
for val in result['presence'].values():
|
2017-03-02 09:52:17 +01:00
|
|
|
val.pop('client', None)
|
|
|
|
val.pop('pushable', None)
|
2017-02-11 08:38:16 +01:00
|
|
|
return json_success(result)
|
|
|
|
|
2018-12-17 22:04:07 +01:00
|
|
|
@human_users_only
|
|
|
|
@has_request_variables
|
|
|
|
def update_user_status_backend(request: HttpRequest,
|
|
|
|
user_profile: UserProfile,
|
2019-01-21 19:06:03 +01:00
|
|
|
away: Optional[bool]=REQ(validator=check_bool, default=None),
|
|
|
|
status_text: Optional[str]=REQ(str_validator=check_capped_string(60),
|
|
|
|
default=None),
|
2018-12-17 22:04:07 +01:00
|
|
|
) -> HttpResponse:
|
2019-01-21 18:19:59 +01:00
|
|
|
|
2019-01-21 19:06:03 +01:00
|
|
|
if status_text is not None:
|
|
|
|
status_text = status_text.strip()
|
|
|
|
|
|
|
|
if (away is None) and (status_text is None):
|
|
|
|
return json_error(_('Client did not pass any new values.'))
|
|
|
|
|
2019-01-21 18:19:59 +01:00
|
|
|
do_update_user_status(
|
|
|
|
user_profile=user_profile,
|
|
|
|
away=away,
|
2019-01-21 19:06:03 +01:00
|
|
|
status_text=status_text,
|
|
|
|
client_id=request.client.id,
|
2019-01-21 18:19:59 +01:00
|
|
|
)
|
2018-12-17 22:04:07 +01:00
|
|
|
|
2019-01-02 20:06:20 +01:00
|
|
|
return json_success()
|
2018-12-17 22:04:07 +01:00
|
|
|
|
2017-04-15 20:51:51 +02:00
|
|
|
@human_users_only
|
2016-07-26 23:26:39 +02:00
|
|
|
@has_request_variables
|
2017-12-29 14:34:49 +01:00
|
|
|
def update_active_status_backend(request: HttpRequest, user_profile: UserProfile,
|
|
|
|
status: str=REQ(),
|
|
|
|
ping_only: bool=REQ(validator=check_bool, default=False),
|
2020-02-02 17:29:05 +01:00
|
|
|
new_user_input: bool=REQ(validator=check_bool, default=False),
|
|
|
|
slim_presence: bool=REQ(validator=check_bool, default=False)
|
2017-12-29 14:34:49 +01:00
|
|
|
) -> HttpResponse:
|
2016-07-26 23:26:39 +02:00
|
|
|
status_val = UserPresence.status_from_string(status)
|
|
|
|
if status_val is None:
|
2017-01-29 00:08:08 +01:00
|
|
|
raise JsonableError(_("Invalid status: %s") % (status,))
|
2020-05-01 20:39:26 +02:00
|
|
|
elif user_profile.presence_enabled:
|
2017-04-16 21:32:57 +02:00
|
|
|
update_user_presence(user_profile, request.client, timezone_now(),
|
2016-11-06 02:49:37 +01:00
|
|
|
status_val, new_user_input)
|
2016-07-26 23:26:39 +02:00
|
|
|
|
2017-03-31 01:46:45 +02:00
|
|
|
if ping_only:
|
python: Convert assignment type annotations to Python 3.6 style.
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>
2020-04-22 01:09:50 +02:00
|
|
|
ret: Dict[str, Any] = {}
|
2017-03-31 01:46:45 +02:00
|
|
|
else:
|
2020-02-06 15:39:58 +01:00
|
|
|
ret = get_presence_response(user_profile, slim_presence)
|
2017-03-31 01:46:45 +02:00
|
|
|
|
2016-07-27 01:45:29 +02:00
|
|
|
if user_profile.realm.is_zephyr_mirror_realm:
|
2016-09-13 23:45:27 +02:00
|
|
|
# In zephyr mirroring realms, users can't see the presence of other
|
|
|
|
# users, but each user **is** interested in whether their mirror bot
|
|
|
|
# (running as their user) has been active.
|
2016-07-26 23:26:39 +02:00
|
|
|
try:
|
|
|
|
activity = UserActivity.objects.get(user_profile = user_profile,
|
2018-07-13 13:10:12 +02:00
|
|
|
query="get_events",
|
2016-07-26 23:26:39 +02:00
|
|
|
client__name="zephyr_mirror")
|
|
|
|
|
|
|
|
ret['zephyr_mirror_active'] = \
|
2017-04-16 21:32:57 +02:00
|
|
|
(activity.last_visit > timezone_now() - datetime.timedelta(minutes=5))
|
2016-07-26 23:26:39 +02:00
|
|
|
except UserActivity.DoesNotExist:
|
|
|
|
ret['zephyr_mirror_active'] = False
|
|
|
|
|
|
|
|
return json_success(ret)
|
2018-10-14 19:22:04 +02:00
|
|
|
|
|
|
|
def get_statuses_for_realm(request: HttpRequest, user_profile: UserProfile) -> HttpResponse:
|
2020-02-02 17:29:05 +01:00
|
|
|
# This isn't used by the webapp; it's available for API use by
|
|
|
|
# bots and other clients. We may want to add slim_presence
|
|
|
|
# support for it (or just migrate its API wholesale) later.
|
2020-02-06 15:39:58 +01:00
|
|
|
return json_success(get_presence_response(user_profile, slim_presence=False))
|