Example #1
0
        /// <inheritdoc />
        public async Task <Error> AddAsync(
            string userId,
            BusPrediction prediction,
            CancellationToken cancellationToken = default
            )
        {
            prediction.User = new MongoDBRef("users", userId);

            Error error;

            try
            {
                await _collection.InsertOneAsync(prediction, cancellationToken : cancellationToken)
                .ConfigureAwait(false);

                error = null;
            }
            catch (MongoWriteException e)
                when(e.WriteError.Category == ServerErrorCategory.DuplicateKey &&
                     e.WriteError.Message.Contains($" index: ")
                     )
                {
                    string index = Regex.Match(e.WriteError.Message, @" index: (\w+) ", RegexOptions.IgnoreCase).Value;

                    error = new Error("data.duplicate_key", $@"Duplicate key ""{index}""");
                }

            return(error);
        }
Example #2
0
        /// <inheritdoc />
        public async Task <BusPrediction> GetByIdAsync(
            string id,
            CancellationToken cancellationToken = default
            )
        {
            var filter = Filter.Eq("_id", ObjectId.Parse(id));

            BusPrediction busPrediction = await _collection
                                          .Find(filter)
                                          .SingleOrDefaultAsync(cancellationToken)
                                          .ConfigureAwait(false);

            return(busPrediction);
        }
        public async Task HandleAsync(IUpdateContext context, UpdateDelegate next, CancellationToken cancellationToken)
        {
            var userchat = context.Update.ToUserchat();

            var cachedCtx = await GetCachedContextsAsync(context, userchat, cancellationToken)
                            .ConfigureAwait(false);

            if (cachedCtx.Bus != null && cachedCtx.Location != null)
            {
                _logger.LogTrace("Bus route and location is provided. Sending bus predictions.");

                var busStop = await _predictionsService.FindClosestBusStopAsync(
                    cachedCtx.Profile.DefaultAgencyTag,
                    cachedCtx.Bus.RouteTag,
                    cachedCtx.Bus.DirectionTag,
                    cachedCtx.Location.Longitude,
                    cachedCtx.Location.Latitude,
                    cancellationToken
                    ).ConfigureAwait(false);

                var predictions = await _predictionsService.GetPredictionsAsync(
                    cachedCtx.Profile.DefaultAgencyTag,
                    cachedCtx.Bus.RouteTag,
                    busStop.Tag,
                    cancellationToken
                    ).ConfigureAwait(false);

                IReplyMarkup locationReplyMarkup;
                if (string.IsNullOrWhiteSpace(cachedCtx.Bus.Origin))
                {
                    locationReplyMarkup = new ReplyKeyboardRemove();
                }
                else
                {
                    locationReplyMarkup = new InlineKeyboardMarkup(
                        InlineKeyboardButton.WithCallbackData("🚩 Remember this location", "loc/save")
                        );
                }

                await context.Bot.Client.MakeRequestWithRetryAsync(
                    new SendLocationRequest(userchat.ChatId, busStop.Latitude, busStop.Longitude)
                {
                    ReplyToMessageId = cachedCtx.Location.LocationMessageId,
                    ReplyMarkup      = locationReplyMarkup,
                },
                    cancellationToken
                    ).ConfigureAwait(false);

                string text = "👆 That's the nearest bus stop";
                if (!string.IsNullOrWhiteSpace(busStop.Title))
                {
                    text += $", *{busStop.Title}*";
                }
                text += " 🚏.";

                string message = RouteMessageFormatter.FormatBusPredictionsReplyText(predictions.Predictions);
                text += "\n\n" + message;

                var predictionsMessage = await context.Bot.Client.MakeRequestWithRetryAsync(
                    new SendMessageRequest(userchat.ChatId, text)
                {
                    ParseMode   = ParseMode.Markdown,
                    ReplyMarkup = (InlineKeyboardMarkup)InlineKeyboardButton.WithCallbackData("Update", "pred/"),
                },
                    cancellationToken
                    ).ConfigureAwait(false);

                var prediction = new BusPrediction
                {
                    AgencyTag    = cachedCtx.Profile.DefaultAgencyTag,
                    RouteTag     = cachedCtx.Bus.RouteTag,
                    DirectionTag = cachedCtx.Bus.DirectionTag,
                    BusStopTag   = busStop.Tag,
                    Origin       = cachedCtx.Bus.Origin,
                    UserLocation = new GeoJsonPoint <GeoJson2DCoordinates>(
                        new GeoJson2DCoordinates(cachedCtx.Location.Longitude, cachedCtx.Location.Latitude)),
                };

                await _predictionRepo.AddAsync(cachedCtx.Profile.Id, prediction, cancellationToken)
                .ConfigureAwait(false);

                await context.Bot.Client.MakeRequestWithRetryAsync(
                    new EditMessageReplyMarkupRequest(
                        predictionsMessage.Chat,
                        predictionsMessage.MessageId,
                        InlineKeyboardButton.WithCallbackData("Update", $"pred/id:{prediction.Id}")
                        ), cancellationToken
                    ).ConfigureAwait(false);
            }
            else if (cachedCtx.Bus != null)
            {
                _logger.LogTrace("Location is missing. Asking user to send his location.");

                var route = await _routeRepo.GetByTagAsync(
                    cachedCtx.Profile.DefaultAgencyTag,
                    cachedCtx.Bus.RouteTag,
                    cancellationToken
                    ).ConfigureAwait(false);

                var direction = route.Directions.Single(d => d.Tag == cachedCtx.Bus.DirectionTag);

                string text = _routeMessageFormatter.GetMessageTextForRouteDirection(route, direction);

                text += "\n\n*Send your current location* so I can find you the nearest bus stop 🚏 " +
                        "and get the bus predictions for it.";
                int replyToMessage = context.Update.Message?.MessageId
                                     ?? context.Update.CallbackQuery?.Message?.MessageId
                                     ?? 0;
                await context.Bot.Client.MakeRequestWithRetryAsync(
                    new SendMessageRequest(userchat.ChatId, text)
                {
                    ParseMode        = ParseMode.Markdown,
                    ReplyToMessageId = replyToMessage,
                    ReplyMarkup      = new ReplyKeyboardMarkup(new[]
                    {
                        KeyboardButton.WithRequestLocation("Share my location")
                    }, true, true)
                },
                    cancellationToken
                    ).ConfigureAwait(false);
            }
            else if (cachedCtx.Location != null)
            {
                _logger.LogTrace("Bus route and direction are missing. Asking user to provide them.");

                await context.Bot.Client.MakeRequestWithRetryAsync(
                    new SendMessageRequest(
                        userchat.ChatId,
                        "There you are! What's the bus you want to catch?\n" +
                        "Send me using 👉 /bus command."
                        )
                {
                    ReplyToMessageId = context.Update.Message.MessageId,
                    ReplyMarkup      = new ReplyKeyboardRemove()
                },
                    cancellationToken
                    ).ConfigureAwait(false);
            }

            // ToDo: Remove keyboard if that was set in /bus command
//
//            var userchat = context.Update.ToUserchat();
//
//            if (context.Update.Message?.Location != null)
//            {
//                await HandleLocationUpdateAsync(
//                    context.Bot,
//                    userchat,
//                    context.Update.Message.Location,
//                    context.Update.Message.MessageId,
//                    cancellationToken
//                ).ConfigureAwait(false);
//            }
//            else if (context.Update.Message?.Text != null)
//            {
//                _logger.LogTrace("Checking if this text message has location coordinates.");
//
//                var result = _locationService.TryParseLocation(context.Update.Message.Text);
//                if (result.Successful)
//                {
//                    _logger.LogTrace("Location is shared from text");
//                    await HandleLocationUpdateAsync(
//                        context.Bot,
//                        userchat,
//                        new Location { Latitude = result.Lat, Longitude = result.Lon },
//                        context.Update.Message.MessageId,
//                        cancellationToken
//                    ).ConfigureAwait(false);
//                }
//                else
//                {
//                    _logger.LogTrace("Message text does not have a location. Ignoring the update.");
//                }
//            }
//
//            var locationTuple = _locationsManager.TryParseLocation(context.Update);
//
//            if (locationTuple.Successful)
//            {
//                _locationsManager.AddLocationToCache(userchat, locationTuple.Location);
//
//                await _predictionsManager
//                    .TryReplyWithPredictionsAsync(context.Bot, userchat, context.Update.Message.MessageId)
//                    .ConfigureAwait(false);
//            }
//            else
//            {
//                // todo : if saved location available, offer it as keyboard
//                await context.Bot.Client.SendTextMessageAsync(
//                    context.Update.Message.Chat.Id,
//                    "_Invalid location!_",
//                    ParseMode.Markdown,
//                    replyToMessageId: context.Update.Message.MessageId
//                ).ConfigureAwait(false);
//            }
        }