public async Task <GeofenceRequest[]> GetTriggered(string userId, Location newLocation) { var gfs = (await _locationStore.GetActiveGeofenceRequests(userId, newLocation.Timestamp)) .Select(async(gf) => { var triggered = false; var distance = Geolocation.GeoCalculator.GetDistance(gf.Lat, gf.Lng, newLocation.Latitude, newLocation.Longitude, 5, Geolocation.DistanceUnit.Meters); if (gf.Exit) { if (distance >= gf.Radius) { await _logger.LogForFocusItem(userId, gf.FocusItemId, $"Geofence {gf.Id}/Exit triggered"); triggered = true; } } else { if (distance <= gf.Radius) { await _logger.LogForFocusItem(userId, gf.FocusItemId, $"Geofence {gf.Id}/Enter triggered"); triggered = true; } } return(new { gf, triggered }); }); return((await Task.WhenAll(gfs)).Where(v => v.triggered).Select(v => v.gf).ToArray()); }
private async Task NotifyOrInstall(string userId, FocusItemWithExternalData item) { var timeToDeparture = item.IndicateTime - DateTimeOffset.Now; if (timeToDeparture < FocusConstants.NotifyTime.Add(FocusConstants.ButlerInaccuracy)) { var notifySemaphore = _notifySempahores.GetOrAdd(userId, s => new SemaphoreSlim(1)); await notifySemaphore.WaitAsync(); try { if (!await _focusStore.FocusItemNotifiedAsync(item.Id)) { try { await _digitPushServiceClient[userId].Push.Create(new DigitPushService.Models.PushRequest() { ChannelOptions = new Dictionary <string, string>() { { "digit.notify", null } }, Payload = JsonConvert.SerializeObject(new { notification = new { tag = item.Id, title = item.CalendarEvent == null ? "Losgehen" : $"Losgehen zu {item.CalendarEvent.Subject}", body = NotificationBody(item) } }) }); } catch (Exception e) { await _logger.LogForFocusItem(userId, item.Id, $"Could notify user ({e.Message}).", logLevel : LogLevel.Error); } await _focusStore.SetFocusItemNotifiedAsync(item.Id); // always set notified for now to prevent massive notification spam } } finally { notifySemaphore.Release(); } } else { // plan the notification anyways, even if the location might be updated, in case no update is received (low phone battery for example) // it will be ignored if any of the conditions (user location, traffic, event start time) changed await _butler.InstallAsync(new WebhookRequest() { When = item.IndicateTime.Add(-FocusConstants.NotifyTime).UtcDateTime, Data = new NotifyUserRequest() { UserId = userId, FocusItemId = item.Id }, Url = options.NotifyUserCallbackUri }); } }
private async Task <DateTimeOffset?> RequestLocationForItems(string userId, FocusManageResult manageResult, DateTimeOffset now, DateTimeOffset locationTimeStamp) { var candidates = manageResult.ActiveItems.Where( v => null != v.DirectionsMetadata && null != v.Directions && v.DirectionsMetadata.TravelStatus != TravelStatus.Finished); var requirement = UpdateTimes(candidates, now, locationTimeStamp).OrderBy(v => v.Time).FirstOrDefault(); if (null != requirement) { await logger.LogForFocusItem(userId, requirement.FocusItem.Id, $"Location update for {requirement.Reason} {requirement.FocusItem.CalendarEvent?.Subject} at {requirement.Time:t}"); if (requirement.Time < now + FocusConstants.ShortestLocationUpdateTime) { return(now + FocusConstants.ShortestLocationUpdateTime); } return(requirement.Time); } return(null); }
private async Task <TransitDirections> GetFreshDirections(string userId, Event evt, FocusItem item, Location location) { string directionsKey = null; var address = await ResolveAddress(userId, evt); if (null != address && null != location) { try { var start = new Coordinate() { Lat = location.Latitude, Lng = location.Longitude }; DateTimeOffset directionRequestTime = DateTimeOffset.Now; if (item.DirectionsMetadata?.TravelStatus == TravelStatus.OnJourney) { directionRequestTime = location.Timestamp; } bool requestWithNow = evt.Start <= directionRequestTime; DirectionsResult directionsResult = null; const int preferredRoute = 0; if (!requestWithNow) { directionsResult = await travelServiceClient.Users[userId].Directions.Transit.Get(start, address, evt.Start); if (null != directionsResult.NotFound || directionsResult.TransitDirections.Routes[preferredRoute].DepatureTime < directionRequestTime) { requestWithNow = true; } } if (requestWithNow) { directionsResult = await travelServiceClient.Users[userId].Directions.Transit.Get(start, address, null, directionRequestTime); } directionsKey = directionsResult?.CacheKey; await focusStore.UpdateDirections(item.Id, directionsResult, null != directionsResult.NotFound?null : (int?)preferredRoute); item.DirectionsMetadata = new DirectionsMetadata() { Error = directionsResult.NotFound?.Reason, Key = directionsResult.CacheKey, PeferredRoute = preferredRoute }; if (null == directionsResult.NotFound) { await travelServiceClient.Directions[directionsResult.CacheKey] .Subscribe(new Uri(_options.DirectionsCallbackUri)); } return(directionsResult.TransitDirections); } catch (TravelServiceException ex) { await logger.LogForFocusItem(userId, item.Id, $"Error while retrieving directions for {evt.Subject}: {ex.Message}", logLevel : LogLevel.Error); } } // TODO maybe clear directions with focusStore.UpdateDirections return(null); }