Esempio n. 1
0
        public async Task <IDictionary <string, object> > Handle(IDictionary <string, object> status, ControllerContext context, CancellationToken cancellationToken = default)
        {
            status["now"]                  = myClock.GetCurrentZonedDateTime().ToDateTimeOffset();
            status["defaultTimeZone"]      = $"{myClock.Zone.Id}, {myClock.Zone.GetZoneInterval(myClock.GetCurrentInstant())}";
            status["assetsRoot"]           = myUrlHelper.AssetsContent("~");
            status["lastAppliedMigration"] = (await myDB.Database.GetAppliedMigrationsAsync(cancellationToken)).LastOrDefault();
            status["polls"]                = await myDB.Set <Poll>().LongCountAsync(cancellationToken);

            status["IGNs"] = await myDB.Set <Player>().Where(_ => _.Nickname != null).LongCountAsync(cancellationToken);

            status["FCs"] = await myDB.Set <Player>().Where(_ => _.FriendCode != null).LongCountAsync(cancellationToken);

            return(status);
        }
    public async Task Execute(CancellationToken cancellationToken)
    {
        var toNotify = await myDB.Set <ReplyNotification>()
                       .Where(n => n.MessageId == null)
                       .AsTracking()
                       .ToListAsync(cancellationToken);

        foreach (var batch in toNotify.GroupBy(n => new { n.FromChatId, n.FromMessageId }))
        {
            var counter = 0;
            using var notificationOperation = myTelemetryClient.StartOperation(
                      new DependencyTelemetry(GetType().Name, null, "ChannelNotification", batch.Key.ToString()));
            foreach (var notification in batch)
            {
                try
                {
                    if (notification.BotId is { } botId&& myBots.TryGetValue(botId, out var bot))
                    {
                        var message = await bot.ForwardMessageAsync(notification.ChatId, notification.FromChatId, notification.FromMessageId, cancellationToken : cancellationToken);

                        notification.MessageId = message.MessageId;
                    }
                    else
                    {
                        // unknown bot, do not try more
                        notification.MessageId = -1;
                    }
                }
        public async Task <bool?> Handle(MessageEntityEx entity, PollMessage context = default, CancellationToken cancellationToken = default)
        {
            if (!this.ShouldProcess(entity, context))
            {
                return(null);
            }

            var from  = myClock.GetCurrentInstant().ToDateTimeOffset() - myJitterOffset;
            var polls = await myDB.Set <Poll>()
                        .Where(poll => poll.Time >= from)
                        .Where(poll => poll.Votes.Any(vote => vote.UserId == entity.Message.From.Id))
                        .IncludeRelatedData()
                        .ToListAsync(cancellationToken);

            var builder = new TextBuilder();

            if (polls.Count == 0)
            {
                builder.Append($"No upcoming raids.");
            }
            else
            {
                foreach (var poll in polls.OrderBy(poll => poll.Time))
                {
                    builder.Bold(b =>
                                 b.Code(bb => bb.AppendFormat("{0:t} ", poll.Time)));
                    poll.GetTitle(builder).NewLine();
                }
            }

            var content = builder.ToTextMessageContent(disableWebPreview: true);
            await myBot.SendTextMessageAsync(entity.Message.Chat, content, true, cancellationToken : cancellationToken);

            return(false);
        }
Esempio n. 4
0
        public async Task <Portal> Get(string guid, Location location = default, CancellationToken cancellationToken = default)
        {
            if (string.IsNullOrEmpty(guid))
            {
                return(null);
            }

            var portalSet = myContext.Set <Portal>();
            // check local & remote
            var portal = await portalSet.FindAsync(new object[]  { guid }, cancellationToken);

            var now = myClock.GetCurrentInstant().ToDateTimeOffset();

            if ((now - portal?.Modified)?.TotalDays < 1) // refresh every day
            {
                return(portal);
            }

            var portals = await Search(guid, location ?? myDefaultLocation, near : false, cancellationToken);

            var remotePortal = portals.FirstOrDefault(p => p.Guid == guid);

            if (remotePortal != null && portal != null)
            {
                myContext.Entry(portal).SetNotNullProperties(remotePortal);
            }
            return(remotePortal);
        }
Esempio n. 5
0
        public async Task <IDictionary <string, object> > Handle(IDictionary <string, object> status, ControllerContext context, CancellationToken cancellationToken = default)
        {
            status["assetsRoot"]           = myUrlHelper.AssetsContent("~");
            status["lastAppliedMigration"] = (await myDB.Database.GetAppliedMigrationsAsync(cancellationToken)).LastOrDefault();
            status["polls"] = await myDB.Set <Poll>().LongCountAsync(cancellationToken);

            return(status);
        }
        public async Task ApproveFriendship(User host, User user, CancellationToken cancellationToken = default)
        {
            var friendshipDB = myDB.Set <Friendship>();
            Expression <Func <Friendship, bool> > findFriendPredicate = friendship =>
                                                                        friendship.Id == host.Id && friendship.FriendId == user.Id ||
                                                                        friendship.Id == user.Id && friendship.FriendId == host.Id;
            var friendship = friendshipDB.Local.SingleOrDefault(findFriendPredicate.Compile()) ??
                             await friendshipDB.FirstOrDefaultAsync(findFriendPredicate, cancellationToken);

            if (friendship == null)
            {
                friendship = new Friendship {
                    Id = host.Id, FriendId = user.Id
                };
                friendshipDB.Add(friendship);
            }
            friendship.Type = FriendshipType.Approved;
            await myDB.SaveChangesAsync(cancellationToken);
        }
Esempio n. 7
0
        public async Task <IActionResult> OnGetAsync(int raidId, CancellationToken cancellationToken = default)
        {
            Raid = await myDbContext
                   .Set <Raid>()
                   .FindAsync(new object[] { raidId }, cancellationToken);

            if (Raid == null)
            {
                return(NotFound($"Raid {raidId} not found."));
            }

            return(Page());
        }
Esempio n. 8
0
        public async Task <IActionResult> OnGetDataAsync(string bbox, CancellationToken cancellationToken)
        {
            var parts   = bbox.Split(',', 4);
            var latLow  = decimal.Parse(parts[0], NumberStyles.Currency, CultureInfo.InvariantCulture);
            var lonLow  = decimal.Parse(parts[1], NumberStyles.Currency, CultureInfo.InvariantCulture);
            var latHigh = decimal.Parse(parts[2], NumberStyles.Currency, CultureInfo.InvariantCulture);
            var lonHigh = decimal.Parse(parts[3], NumberStyles.Currency, CultureInfo.InvariantCulture);

            var now   = myClock.GetCurrentInstant().ToDateTimeOffset();;
            var polls = await myDb
                        .Set <Poll>()
                        .IncludeRelatedData()
                        .Where(_ => _.Raid.RaidBossEndTime > now)
                        .Where(_ => _.Raid.EggRaidId == null) // no eggs if boss is already known
                        .Where(_ => _.Raid.Lat >= latLow && _.Raid.Lat <= latHigh)
                        .Where(_ => _.Raid.Lon >= lonLow && _.Raid.Lon <= lonHigh)
                        .DecompileAsync()
                        .ToArrayAsync(cancellationToken);

            var response = new
            {
                type     = "FeatureCollection",
                features = polls.Select(p => new
                {
                    type     = "Feature",
                    id       = p.Id,
                    geometry = new
                    {
                        type        = "Point",
                        coordinates = new [] { p.Raid.Lat.GetValueOrDefault(), p.Raid.Lon.GetValueOrDefault() }
                    },
                    properties = new
                    {
                        id          = p.Id,
                        raidLevel   = $"R{p.Raid.RaidBossLevel}",
                        name        = p.Raid.Name,
                        img         = p.GetThumbUrl(Url),
                        title       = p.GetTitle(),
                        description = $"{p.Raid.Description}",
                    },
                    options = new
                    {
                        preset    = p.Raid.IsEgg ? "islands#icon" : "islands#dotIcon",
                        iconColor = p.Raid.GetEggColor() is Color c ? $"#{c.R:X2}{c.G:X2}{c.B:X2}" : null,
                    }
                })
            };

            return(new OkObjectResult(response));
        }
Esempio n. 9
0
        public async Task <IActionResult> OnGetAsync(int raidId, CancellationToken cancellationToken = default)
        {
            Raid = await myDbContext
                   .Set <Raid>()
                   .Where(_ => _.Id == raidId)
                   .FirstOrDefaultAsync(cancellationToken);

            if (Raid == null)
            {
                return(NotFound($"Raid {raidId} not found."));
            }

            return(Page());
        }
        public async Task <bool> ProcessPoll(ITelegramBotClient bot, long targetChatId, int?replyMessageId, Func <CancellationToken, Task <Poll> > getPoll, Func <TextBuilder> getInitialText, CancellationToken cancellationToken = default)
        {
            var timeZoneSettings = await myDB.Set <TimeZoneSettings>().Where(settings => settings.ChatId == targetChatId).ToListAsync(cancellationToken);

            if (timeZoneSettings.Count == 0)
            {
                return(false);
            }

            var poll = await getPoll(cancellationToken);

            if (poll?.Time is not {
            } datetime)
            {
                return(default); // no poll or without time
Esempio n. 11
0
        public async Task <IActionResult> gyms(CancellationToken cancellationToken = default)
        {
            var portals = await myDB.Set <Portal>().ToListAsync(cancellationToken);

            return(Json(new
            {
                gyms = portals
                       .Select(portal => new { guid = portal.Guid.ToString(), lat = portal.Latitude, lng = portal.Longitude, name = portal.Name, image = portal.Image })
                       .Aggregate(new ExpandoObject(), (obj, _) =>
                {
                    obj.TryAdd(_.guid, _);
                    return obj;
                })
            }));
        }
Esempio n. 12
0
        public async Task <bool?> Handle(MessageEntityEx entity, PollMessage context = default, CancellationToken cancellationToken = default)
        {
            if (entity.Message.Chat.Type != ChatType.Private)
            {
                return(false);
            }

            var commandText = entity.AfterValue.Trim();

            switch (entity.Command.ToString().ToLowerInvariant())
            {
            case "/ign":
            case "/nick":
            case "/nickname":
                var author = entity.Message.From;
                var player = await myContext.Set <Player>().Where(p => p.UserId == author.Id).FirstOrDefaultAsync(cancellationToken);

                var nickname = commandText.ToString();
                if (string.IsNullOrEmpty(nickname))
                {
                    if (player != null)
                    {
                        myContext.Remove(player);
                    }
                }
                else
                {
                    if (player == null)
                    {
                        player = new Player
                        {
                            UserId = author.Id
                        };
                        myContext.Add(player);
                    }

                    player.Nickname = nickname;
                }
                await myContext.SaveChangesAsync(cancellationToken);

                return(true);

            default:
                return(false);
            }
        }
Esempio n. 13
0
        public async Task <bool?> Process(User user, string nickname, CancellationToken cancellationToken = default)
        {
            var player = await myContext.Set <Player>().Get(user, cancellationToken);

            if (!string.IsNullOrEmpty(nickname))
            {
                if (player == null)
                {
                    player = new Player
                    {
                        UserId = user.Id
                    };
                    myContext.Add(player);
                }

                player.Nickname = nickname;
                await myContext.SaveChangesAsync(cancellationToken);
            }

            IReplyMarkup?replyMarkup = null;
            var          builder     = new TextBuilder();

            if (!string.IsNullOrEmpty(player?.Nickname))
            {
                builder
                .Append($"Your IGN is {player.Nickname:bold}")
                .NewLine()
                .NewLine();

                replyMarkup = new InlineKeyboardMarkup(InlineKeyboardButton.WithCallbackData("Clear IGN",
                                                                                             $"{PlayerCallbackQueryHandler.ID}:{PlayerCallbackQueryHandler.Commands.ClearIGN}"));
            }

            builder
            .Append($"To set up your in-game-name reply with it to this message.").NewLine()
            .Append($"Or use /{COMMAND} command.").NewLine()
            .Code("/ign your-in-game-name");

            await myBot.SendTextMessageAsync(user.Id, builder.ToTextMessageContent(), cancellationToken : cancellationToken,
                                             replyMarkup : replyMarkup ?? new ForceReplyMarkup {
                InputFieldPlaceholder = "in-game-name"
            });

            return(false); // processed, but not pollMessage
        }
Esempio n. 14
0
        public async Task <bool?> Handle(MessageEntityEx data, PollMessage context = default, CancellationToken cancellationToken = default)
        {
            if (!this.ShouldProcess(data, context))
            {
                return(null);
            }

            var player = await myDB.Set <Player>().Get(data.Message.From, cancellationToken);

            var content = new TextBuilder("When someone is asking for Friendship")
                          .ToTextMessageContent();

            var replyMarkup = GetInlineKeyboardMarkup(player);

            await myBot.SendTextMessageAsync(data.Message.Chat.Id, content, replyMarkup : replyMarkup, cancellationToken : cancellationToken);

            return(false); // processed, but not pollMessage
        }
Esempio n. 15
0
        public async Task <Portal> Get(string guid, Location location = default, CancellationToken cancellationToken = default)
        {
            if (string.IsNullOrEmpty(guid))
            {
                return(null);
            }
            var portalSet = myContext.Set <Portal>();
            var portal    = await portalSet.FindAsync(new object[] { guid }, cancellationToken);

            if ((myClock.GetCurrentInstant().ToDateTimeOffset() - portal?.Modified)?.TotalDays < 1) // refresh every day
            {
                return(portal);
            }

            var portals = await Search(guid, location ?? myDefaultLocation, cancellationToken);

            return(portals.FirstOrDefault(p => p.Guid == guid));
        }
        public async Task <bool?> Handle(MessageEntityEx entity, PollMessage context = default, CancellationToken cancellationToken = default)
        {
            if (!this.ShouldProcess(entity, context))
            {
                return(null);
            }

            var author   = entity.Message.From;
            var settings = await myContext.Set <UserSettings>().Get(author, cancellationToken);

            var content = await GetMessage(settings, cancellationToken);

            var sentMessage = await myBot.SendTextMessageAsync(entity.Message.Chat, content, cancellationToken : cancellationToken,
                                                               replyMarkup : new ReplyKeyboardMarkup(KeyboardButton.WithRequestLocation("Send a location to set up your home place and timezone"))
            {
                ResizeKeyboard = true, OneTimeKeyboard = true
            });

            myCache.GetOrCreate(this[sentMessage.MessageId], _ => true);
            return(false); // processed, but not pollMessage
        }
        public async Task <(string, bool, string)> Handle(CallbackQuery data, object context = default, CancellationToken cancellationToken = default)
        {
            var callback = data.Data.Split(':');

            if (callback[0] != "clone")
            {
                return(null, false, null);
            }

            if (!int.TryParse(callback.ElementAtOrDefault(1) ?? "", NumberStyles.Integer, CultureInfo.InvariantCulture, out var pollId))
            {
                return("Poll is publishing. Try later.", true, null);
            }

            var poll = await myContext
                       .Set <Poll>()
                       .Where(_ => _.Id == pollId)
                       .IncludeRelatedData()
                       .FirstOrDefaultAsync(cancellationToken);

            if (poll == null)
            {
                return("Poll is not found", true, null);
            }

            var pollMessage = new PollMessage
            {
                ChatId           = data.From.Id,
                ChatType         = ChatType.Private,
                UserId           = data.From.Id,
                InlineMesssageId = data.InlineMessageId,
                Poll             = poll
            };
            await myRaidService.AddPollMessage(pollMessage, myUrlHelper, cancellationToken);

            var botUser = await myBot.GetMeAsync(cancellationToken);

            return(null, false, $"https://t.me/{botUser.Username}?start={pollMessage.GetExtendedPollId()}");
        }
        public async Task <bool?> Handle(ChosenInlineResult data, object context = default, CancellationToken cancellationToken = default)
        {
            var resultParts = data.ResultId.Split(':');

            switch (resultParts[0])
            {
            case PREFIX:
                if (myTimeZoneNotifyService.DecodeId(resultParts.Skip(1).FirstOrDefault(), out var chatId, out var messageId) &&
                    resultParts.Skip(2).FirstOrDefault() is {} timeZoneId&& myDateTimeZoneProvider.GetZoneOrNull(timeZoneId) is {})
                {
                    if (!await CheckRights(chatId, data.From, cancellationToken))
                    {
                        await myBot.EditMessageTextAsync(data.InlineMessageId !, new InputTextMessageContent("You have no rights"), cancellationToken : cancellationToken);

                        return(false);
                    }

                    var settings = myDB.Set <TimeZoneSettings>();
                    if (await EntityFrameworkQueryableExtensions.FirstOrDefaultAsync(settings.Where(s => s.ChatId == chatId && s.TimeZone == timeZoneId), cancellationToken) == null)
                    {
                        settings.Add(new TimeZoneSettings {
                            ChatId = chatId, TimeZone = timeZoneId
                        });
                        await myDB.SaveChangesAsync(cancellationToken);
                    }

                    var(content, replyMarkup) = await myTimeZoneNotifyService.GetSettingsMessage(new Chat { Id = chatId, Type = ChatType.Sender }, cancellationToken : cancellationToken);

                    await myBot.EditMessageTextAsync(data.InlineMessageId !, content, replyMarkup, cancellationToken);

                    if (messageId != 0)
                    {
                        await myBot.DeleteMessageAsync(chatId, messageId, cancellationToken);
                    }

                    return(true);
                }
                break;
            }
        public async Task <(string text, bool showAlert, string url)> Handle(CallbackQuery data, object context = default, CancellationToken cancellationToken = default)
        {
            var callback = data.Data?.Split(':');

            if (callback?[0] != ID)
            {
                return(null, false, null);
            }

            var player = await myDb.Set <Player>().Get(data.From, cancellationToken);

            switch (callback.Skip(1).FirstOrDefault()?.ToLowerInvariant())
            {
            case Commands.ClearIGN:
                if (player != null)
                {
                    player.Nickname = null;
                    await myDb.SaveChangesAsync(cancellationToken);
                }

                await myBot.EditMessageReplyMarkupAsync(data, InlineKeyboardMarkup.Empty(), cancellationToken);

                return("Your IGN is removed", false, null);

            case Commands.ClearFriendCode:
                if (player != null)
                {
                    player.FriendCode = null;
                    await myDb.SaveChangesAsync(cancellationToken);
                }

                await myBot.EditMessageReplyMarkupAsync(data, InlineKeyboardMarkup.Empty(), cancellationToken);

                return("Your Friend Code is removed", false, null);
            }

            return(null, false, null);
        }
Esempio n. 20
0
        public async Task <DateTimeZone> GetTimeZone(InlineQuery inlineQuery, CancellationToken cancellationToken = default)
        {
            if (inlineQuery.Location is {} userLocation)
            {
                if (TimeZoneLookup.GetTimeZone(userLocation.Latitude, userLocation.Longitude).Result is {} timeZoneId)
                {
                    if (myDateTimeZoneProvider.GetZoneOrNull(timeZoneId) is { } timeZone)
                    {
                        return(timeZone);
                    }
                }
            }

            {
                var userSettings = await myDB.Set <UserSettings>().Get(inlineQuery.From, cancellationToken);

                if (userSettings?.TimeZoneId is { } timeZoneId&& myDateTimeZoneProvider.GetZoneOrNull(timeZoneId) is { } timeZone)
                {
                    return(timeZone);
                }
            }

            return(myDefaultDateTimeZone);
        }
Esempio n. 21
0
        public async Task <PollMessage> GetOrCreatePollAndMessage(PollMessage pollMessage, IUrlHelper urlHelper, VoteEnum?format = null, CancellationToken cancellationToken = default)
        {
            bool exRaidGym = false;
            var  pollId    = pollMessage.PollId;

            if (pollId < 0)
            {
                pollId    = pollMessage.PollId = -pollId;
                exRaidGym = true;
            }
            var poll = await myContext
                       .Set <Poll>()
                       .Where(_ => _.Id == pollId)
                       .IncludeRelatedData()
                       .FirstOrDefaultAsync(cancellationToken);

            if (poll != null)
            {
                var existingMessage = poll.Messages.FirstOrDefault(_ => _.InlineMesssageId == pollMessage.InlineMesssageId && _.ChatId == pollMessage.ChatId && _.MesssageId == pollMessage.MesssageId);
                if (existingMessage != null)
                {
                    return(existingMessage);
                }

                pollMessage.Poll = poll;
                return(await AddPollMessage(pollMessage, urlHelper, cancellationToken));
            }

            var pollData = GetTemporaryPoll(pollId);

            if (pollData == null)
            {
                return(null);
            }

            pollMessage.Poll = new Poll
            {
                Id           = pollId,
                Title        = pollData.Title,
                AllowedVotes = format,
                Owner        = pollData.Owner,
                Portal       = pollData.Portal,
                ExRaidGym    = exRaidGym,
                Votes        = new List <Vote>()
            };
            myContext.Set <Poll>().Attach(pollMessage.Poll).State = EntityState.Added;
            if (pollData.Portal is Portal portal)
            {
                var portalSet      = myContext.Set <Portal>();
                var existingPortal = await portalSet.AsTracking().FirstOrDefaultAsync(p => p.Guid == portal.Guid, cancellationToken);

                if (existingPortal == null)
                {
                    portalSet.Attach(portal).State = EntityState.Added;
                }
                else
                {
                    existingPortal.Guid            = portal.Guid;
                    existingPortal.Name            = portal.Name;
                    existingPortal.Address         = portal.Address;
                    existingPortal.Image           = portal.Image;
                    existingPortal.Latitude        = portal.Latitude;
                    existingPortal.Longitude       = portal.Longitude;
                    portalSet.Attach(portal).State = EntityState.Modified;
                }
            }

            return(await AddPollMessage(pollMessage, urlHelper, cancellationToken, withLog : true));
        }
Esempio n. 22
0
        public async Task <bool?> Handle(InlineQuery data, object context = default, CancellationToken cancellationToken = default)
        {
            var location = await myDb.Set <UserSettings>().GetLocation(data, cancellationToken);

            var queryParts = data.Query.Split(' ', StringSplitOptions.RemoveEmptyEntries);

            var poll = default(Poll);
            List <VoteLimit> limits = default;
            var pollQuery           = new List <string>(queryParts.Length);
            var searchQuery         = new List <string>(queryParts.Length);
            var query = pollQuery;

            foreach (var queryPart in queryParts)
            {
                switch (queryPart)
                {
                case { } locationPart when OpenLocationCode.IsValid(locationPart):
                    location = OpenLocationCode.Decode(locationPart) is var code
              ? new Location
                    {
                        Longitude = (float)code.CenterLongitude, Latitude = (float)code.CenterLatitude
                    }

              : location;
                    break;

                case { } part when Regex.Match(part, PATTERN) is
                    {
                        Success: true
                    }

match:
                    if (int.TryParse(match.Groups["pollId"].ValueSpan, out var pollId))
                    {
                        poll = myRaidService
                               .GetTemporaryPoll(pollId)
                               .InitImplicitVotes(data.From, myBot.BotId);
                    }
                    query = searchQuery;
                    break;

                case { } part when myGeneralInlineQueryHandler.ProcessLimitQueryString(ref limits, part):
                    break;

                default:
                    query.Add(queryPart);
                    break;
                }
            }

            Portal[] portals;
            if (searchQuery.Count == 0)
            {
                portals = await myIngressClient.GetPortals(0.200, location, cancellationToken);
            }
            else
            {
                portals = await myIngressClient.Search(searchQuery, location, near : true, cancellationToken);
            }

            var results = new List <InlineQueryResult>(Math.Min(portals.Length, MAX_PORTALS_PER_RESPONSE) + 2);

            if (poll == null && pollQuery.Count != 0)
            {
                var voteFormat = await myDb.Set <Settings>().GetFormat(data.From.Id, cancellationToken);

                poll = await new Poll(data)
                {
                    Title        = string.Join("  ", pollQuery),
                    AllowedVotes = voteFormat,
                    ExRaidGym    = false,
                    Limits       = limits
                }.DetectRaidTime(myTimeZoneService, () => Task.FromResult(location), async ct => myClock.GetCurrentInstant().InZone(await myGeoCoder.GetTimeZone(data, ct)), cancellationToken);

                for (var i = 0; i < portals.Length && i < MAX_PORTALS_PER_RESPONSE; i++)
                {
                    var portalPoll = new Poll(poll)
                    {
                        Portal = portals[i],
                        Limits = poll.Limits ?? limits
                    }.InitImplicitVotes(data.From, myBot.BotId);
                    await myRaidService.GetPollId(portalPoll, data.From, cancellationToken);

                    results.Add(myGeneralInlineQueryHandler.GetInlineResult(portalPoll));

                    if (i == 0)
                    {
                        portalPoll.Id        = -portalPoll.Id;
                        portalPoll.ExRaidGym = true;
                        results.Add(myGeneralInlineQueryHandler.GetInlineResult(portalPoll));
                    }
                }
            }
            else
            {
                for (var i = 0; i < portals.Length && i < MAX_PORTALS_PER_RESPONSE; i++)
                {
                    var portal = portals[i];
                    var title  = portal.Name is { } name&& !string.IsNullOrWhiteSpace(name) ? name : portal.Guid;
                    if (portal.EncodeGuid() is { } portalGuid)
                    {
                        InlineQueryResultArticle Init(InlineQueryResultArticle article, InlineKeyboardButton createButton)
                        {
                            const int thumbnailSize = 64;

                            article.Description = portal.Address;
                            article.ReplyMarkup = new InlineKeyboardMarkup(createButton);
                            article.ThumbUrl    = portal.GetImage(myUrlHelper, thumbnailSize)?.AbsoluteUri;
                            article.ThumbHeight = thumbnailSize;
                            article.ThumbWidth  = thumbnailSize;
                            return(article);
                        }

                        var portalContent = new TextBuilder()
                                            .Bold(builder => builder.Sanitize(portal.Name)).NewLine()
                                            .Sanitize(portal.Address)
                                            .Link("\u200B", portal.Image)
                                            .ToTextMessageContent();
                        results.Add(Init(
                                        new InlineQueryResultArticle($"portal:{portal.Guid}", title, portalContent),
                                        InlineKeyboardButton.WithSwitchInlineQuery("Create a poll", $"{PREFIX}{portalGuid} {poll?.Title}")));

                        if (i == 0)
                        {
                            var exRaidPortalContent = new TextBuilder()
                                                      .Sanitize("☆ ")
                                                      .Bold(builder => builder.Sanitize(portal.Name))
                                                      .Sanitize(" (EX Raid Gym)").NewLine()
                                                      .Sanitize(portal.Address)
                                                      .Link("\u200B", portal.Image)
                                                      .ToTextMessageContent();
                            results.Add(Init(
                                            new InlineQueryResultArticle($"portal:{portal.Guid}+", $"☆ {title} (EX Raid Gym)", exRaidPortalContent),
                                            InlineKeyboardButton.WithSwitchInlineQuery("Create a poll ☆ (EX Raid Gym)", $"{PREFIX}{portalGuid}+ {poll?.Title}")));
                        }
                    }
                }
            }

            if (searchQuery.Count == 0)
            {
                results.Add(
                    new InlineQueryResultArticle("EnterGymName", "Enter a Gym's Title", new InputTextMessageContent("Enter a Gym's Title to search"))
                {
                    Description = "to search",
                    ThumbUrl    = default(Portal).GetImage(myUrlHelper)?.AbsoluteUri
                });
            }

            if (results.Count == 0)
            {
                var search = string.Join(" ", searchQuery);
                results.Add(new InlineQueryResultArticle("NothingFound", "Nothing found",
                                                         new TextBuilder($"Nothing found by request ").Code(builder => builder.Sanitize(search)).ToTextMessageContent())
                {
                    Description = $"Request {search}",
                    ThumbUrl    = myUrlHelper.AssetsContent(@"static_assets/png/btn_close_normal.png").AbsoluteUri
                });
            }

            await myBot.AnswerInlineQueryWithValidationAsync(data.Id, results, cacheTime : 0, isPersonal : true, cancellationToken : cancellationToken);

            await myDb.SaveChangesAsync(cancellationToken);

            return(true);
        }
Esempio n. 23
0
        public async Task <((decimal?lat, decimal?lon) location, string gym, string distance)> ProcessGym(Raid raid, StringBuilder description, int?precision = null, MidpointRounding?rounding = null, CancellationToken cancellationToken = default)
        {
            var    location = (lat : raid.Lat, lon : raid.Lon);
            string distance = default;
            var    gym      = raid.Gym ?? raid.PossibleGym;

            if (gym == null)
            {
                raid.PossibleGym = await myDbContext.Set <Raid>()
                                   .FindKnownGym((decimal)raid.Lat, (decimal)raid.Lon, precision, rounding)
                                   .Select(_ => _.Gym ?? _.PossibleGym)
                                   .FirstOrDefaultAsync(cancellationToken);
            }

            //if (!string.IsNullOrEmpty(gym))
            //{
            //  description.Append(gym);
            //}

            //var geoCode = await myGeoCoder.Decode(lon, lat, cancellationToken: cancellationToken,
            //    parameters: new Dictionary<string, string>
            //    {
            //        {"results", "1"},
            //        {"kind", "metro"}
            //    });
            //var metro = geoCode?.featureMember?.FirstOrDefault()?.GeoObject;

            try
            {
                var destination = new Location((double)location.lat, (double)location.lon);
                var geoRequest  = new PlacesNearByRequest
                {
                    Location = destination,
                    Type     = "subway_station",
                    RankBy   = RankBy.Distance,
                    ApiKey   = myGeoCoderOptions.GoogleKey
                };

                var geoResponse = await GoogleMaps.PlacesNearBy.QueryAsync(geoRequest, myTelemetryClient, cancellationToken);

                Result foundAddress = null;
                Action <StringBuilder> postProcessor = null;
                foreach (var address in geoResponse.Results)
                {
                    if (address.Types.Contains("subway_station"))
                    {
                        var distanceMatrixRequest = new DistanceMatrixRequest
                        {
                            Origins      = new[] { $"place_id:{address.PlaceId}" },
                            Destinations = new[] { destination.LocationString },
                            Mode         = DistanceMatrixTravelModes.walking,
                            ApiKey       = myGeoCoderOptions.GoogleKey
                        };
                        var distanceMatrixResponse = await GoogleMaps.DistanceMatrix.QueryAsync(distanceMatrixRequest, myTelemetryClient, cancellationToken);

                        var distanceElement = distanceMatrixResponse.Rows.FirstOrDefault()?.Elements.FirstOrDefault();

                        if (distanceElement?.Distance.Value <= myGeoCoderOptions.MaxDistanceToMetro)
                        {
                            foundAddress  = address;
                            postProcessor = descr => descr.Sanitize(distance = $" ∙ {distanceElement.Distance.Text} ∙ {distanceElement.Duration.Text}");
                            break;
                        }
                    }
                }

                string placeId;
                string name;
                if (foundAddress == null)
                {
                    var geoCodingRequest = new GeocodingRequest
                    {
                        Location = destination,
                        ApiKey   = myGeoCoderOptions.GoogleKey
                    };
                    var geoCodingResults = (await GoogleMaps.Geocode.QueryAsync(geoCodingRequest, myTelemetryClient, cancellationToken)).Results;
                    var result           = geoCodingResults.FirstOrDefault(_ => _.Types.Contains("locality"));
                    placeId = result?.PlaceId;
                    name    = result?.FormattedAddress;
                }
                else
                {
                    placeId = foundAddress.PlaceId;
                    name    = foundAddress.Name;
                }

                raid.NearByPlaceId = placeId;
                raid.NearByAddress = name;

                if (!string.IsNullOrEmpty(name))
                {
                    description
                    .Append(description.Length > 0 ? " ∙ " : "")
                    .Append(name);

                    postProcessor?.Invoke(description);
                }
            }
            catch (Exception ex)
            {
                myTelemetryClient.TrackExceptionEx(ex);
            }

            return(location, gym, distance);
        }
        public async Task <(string, bool, string)> Handle(CallbackQuery data, object context = default, CancellationToken cancellationToken = default)
        {
            var callback = data.Data.Split(':');

            if (callback[0] != "set")
            {
                return(null, false, null);
            }

            if (!await myChatInfo.CandEditPoll(data.Message.Chat, data.From?.Id, cancellationToken))
            {
                return("You can't edit the poll.", true, null);
            }

            var chatId      = data.Message.Chat.Id;
            var settingsSet = myContext.Set <Settings>();

            var allSettings = await settingsSet.GetSettings(chatId).AsTracking().ToListAsync(cancellationToken);

            Settings settings = null;

            switch (callback.ElementAtOrDefault(1))
            {
            case "list":
                return(await Return(await SettingsList(chatId, cancellationToken)));

            case var identifier when int.TryParse(identifier, out var id):
                settings = allSettings.FirstOrDefault(setting => setting.Id == id);

                break;
            }

            if (settings == null)
            {
                // create a new
                settings = new Settings {
                    Chat = chatId, Order = allSettings.Select(_ => _.Order).DefaultIfEmpty(-1).Max() + 1, Format = VoteEnum.Standard
                };
                await settingsSet.AddAsync(settings, cancellationToken);
            }

            string message = null;

            switch (callback.ElementAtOrDefault(2) ?? "")
            {
            case "default":
                settings.Order = allSettings.Select(_ => _.Order).DefaultIfEmpty(1).Min() - 1;
                await myContext.SaveChangesAsync(cancellationToken);

                return(await Return(await SettingsList(chatId, cancellationToken), "Default poll format is changed."));

            case "delete":
                settingsSet.Remove(settings);
                await myContext.SaveChangesAsync(cancellationToken);

                return(await Return(await SettingsList(chatId, cancellationToken), "Poll format is deleted."));

            case var format when FlagEnums.TryParseFlags(format, out VoteEnum toggleVotes, EnumFormat.DecimalValue):
                settings.Format = FlagEnums.ToggleFlags(settings.Format, toggleVotes);

                // adjust ⁺¹
                if (settings.Format.HasAnyFlags(VoteEnum.Plus) && !settings.Format.HasAnyFlags(VoteEnum.Countable))
                {
                    settings.Format = settings.Format.RemoveFlags(VoteEnum.Plus);
                }

                if (await myContext.SaveChangesAsync(cancellationToken) > 0)
                {
                    message = "Poll format is changed.";
                }
                goto default;

            default:
                var buttons = new []
                {
                    VoteEnum.Team,
                    VoteEnum.TeamHarmony,
                    VoteEnum.Plus1,
                    VoteEnum.Remotely,
                    VoteEnum.Invitation,
                    VoteEnum.MayBe,
                    VoteEnum.Yes,
                    VoteEnum.Thumbs,
                    VoteEnum.HarryPotter,
                    VoteEnum.Cancel,
                    VoteEnum.Share
                }
                .Select(format => new []
                {
                    InlineKeyboardButton.WithCallbackData($"{(settings.Format.HasAllFlags(format) ? '☑' : '☐')} {format.Format(new StringBuilder())}", $"{ID}:{settings.Id}:{format:D}")
                });

                if (allSettings.FirstOrDefault() is Settings existingDefault && existingDefault != settings)
                {
                    buttons = buttons.Append(new[]
                    {
                        InlineKeyboardButton.WithCallbackData("Make as a default", $"{ID}:{settings.Id}:default")
                    });
                }

                buttons = buttons
                          .Append(new [] { InlineKeyboardButton.WithCallbackData("Delete", $"{ID}:{settings.Id}:delete") })
                          .Append(new [] { InlineKeyboardButton.WithCallbackData("Back", $"{ID}:list") });

                return(await Return((
                                        settings.Format.Format(new StringBuilder("Selected poll format:").AppendLine()).ToTextMessageContent(),
                                        new InlineKeyboardMarkup(buttons)), message));
            }

            async Task <(string, bool, string)> Return((InputTextMessageContent content, InlineKeyboardMarkup replyMarkup) pass, string notification = "")
            {
                await myTelegramBotClient.EditMessageTextAsync(data.Message.Chat, data.Message.MessageId,
                                                               pass.content.MessageText, pass.content.ParseMode, pass.content.DisableWebPagePreview, pass.replyMarkup, cancellationToken);

                return(notification, false, null);
            }
        }
Esempio n. 25
0
        public async Task Execute(CancellationToken cancellationToken)
        {
            var nowWithLeadTime = myClock.GetCurrentInstant().ToDateTimeOffset() + myNotificationLeadTime;
            var previous        = nowWithLeadTime - CheckPeriod - ErrorOffset;
            var polls           = await myDB.Set <Poll>()
                                  .Where(poll => poll.Time > previous && poll.Time <= nowWithLeadTime)
                                  .IncludeRelatedData()
                                  .Include(poll => poll.Notifications)
                                  .ToListAsync(cancellationToken);

            foreach (var poll in polls)
            {
                using var notificationOperation = myTelemetryClient.StartOperation(
                          new DependencyTelemetry(GetType().Name, null, "PollNotification", poll.Id.ToString()));
                var pollMode        = poll.AllowedVotes?.HasFlag(VoteEnum.Invitation) ?? false ? PollMode.DefaultWithInvitation : default;
                var alreadyNotified = poll.Notifications.Where(notification => notification.Type == NotificationType.PollTime).Select(notification => notification.ChatId).ToHashSet();
                foreach (var pollVote in poll.Votes)
                {
                    var botId  = pollVote.BotId;
                    var userId = pollVote.UserId;
                    if (!(pollVote.Team?.HasAnyFlags(VoteEnum.Notify) ?? false))
                    {
                        continue;
                    }
                    if (alreadyNotified.Contains(userId))
                    {
                        continue;
                    }
                    try
                    {
                        var pollMessage = new PollMessage
                        {
                            BotId  = botId,
                            UserId = userId,
                            Chat   = new Chat {
                                Id = userId, Type = ChatType.Private
                            },
                            Poll     = poll,
                            PollId   = poll.Id,
                            PollMode = pollMode
                        };
                        var notificationMessage = await myRaidService.GetOrCreatePollAndMessage(pollMessage, null, poll.AllowedVotes, cancellationToken);

                        var notification = new Notification
                        {
                            PollId    = poll.Id,
                            BotId     = pollMessage.BotId,
                            ChatId    = notificationMessage.Chat.Id,
                            MessageId = notificationMessage.MessageId,
                            DateTime  = notificationMessage.Modified,
                            Type      = NotificationType.PollTime
                        };
                        poll.Notifications.Add(notification);
                        myTelemetryClient.TrackEvent("Notification", new Dictionary <string, string?>
                        {
                            { nameof(notificationMessage.UserId), notificationMessage.UserId?.ToString() },
                            { nameof(notificationMessage.PollId), notificationMessage.PollId.ToString() },
                            { nameof(notificationMessage.BotId), notificationMessage.BotId?.ToString() },
                            { nameof(notificationMessage.ChatId), notificationMessage.ChatId?.ToString() },
                            { nameof(notificationMessage.MessageId), notificationMessage.MessageId?.ToString() }
                        });
                    }
                    catch (Exception ex)
                    {
                        if (ex is ApiRequestException {
                            ErrorCode: 403
                        })
        public async Task <(string text, bool showAlert, string url)> Handle(CallbackQuery data, object context = default, CancellationToken cancellationToken = default)
        {
            var callback = data.Data?.Split(':');

            if (callback?[0] != ID)
            {
                return(null, false, null);
            }

            var host   = data.From;
            var player = await myDB.Set <Player>().Get(host, cancellationToken);

            var command          = callback.Skip(1).FirstOrDefault();
            var commandParameter = callback.Skip(2).FirstOrDefault();

            switch (command)
            {
            case Commands.SendCodeId:
            case Commands.AutoApproveId:
            case Commands.ApproveSettingsId when bool.TryParse(commandParameter, out var autoApprove) && autoApprove:
                if (player?.FriendCode == null)
                {
                    await myFriendshipService.SetupFriendCode(myBot, host, StringSegment.Empty, cancellationToken);

                    return("Please, specify your Friend Code first", true, null);
                }

                break;
            }


            switch (command)
            {
            case Commands.SendCodeId
                when long.TryParse(commandParameter, out var userId) &&
                long.TryParse(callback.Skip(3).FirstOrDefault(), out var botId):
                try
                {
                    if (!myBots.TryGetValue(botId, out var bot))
                    {
                        bot = myBot;
                    }
                    await myFriendshipService.SendCode(bot, new User { Id = userId }, host, player, cancellationToken);

                    await myBot.EditMessageReplyMarkupAsync(data, InlineKeyboardMarkup.Empty(), cancellationToken);

                    return("Friend Code sent", false, null);
                }
                catch (ApiRequestException apiEx) when(apiEx.ErrorCode == 403)
                {
                    return("User blocked personal messages for the bot.\r\nSend him/her code by yourself.", true, null);
                }

            case Commands.AskCodeId
                when long.TryParse(commandParameter, out var userId) &&
                long.TryParse(callback.Skip(3).FirstOrDefault(), out var botId):
                try
                {
                    if (!myBots.TryGetValue(botId, out var bot))
                    {
                        bot = myBot;
                    }
                    await myFriendshipService.AskCode(host, myBot, new User { Id = userId }, bot, player, cancellationToken);

                    await myBot.EditMessageReplyMarkupAsync(data, InlineKeyboardMarkup.Empty(), cancellationToken);

                    return("Friend Code asked", false, null);
                }
                catch (ApiRequestException apiEx) when(apiEx.ErrorCode == 403)
                {
                    return("User blocked personal messages for the bot.\r\nAsk him/her for the code by yourself.", true, null);
                }

            case Commands.ApproveId
                when int.TryParse(commandParameter, out var userId):

                await myFriendshipService.ApproveFriendship(host, new User { Id = userId }, cancellationToken);

                await myBot.EditMessageReplyMarkupAsync(data, InlineKeyboardMarkup.Empty(), cancellationToken);

                return("He/She marked as already Friend.", false, null);

            case Commands.AutoApproveId
                when int.TryParse(commandParameter, out var pollId):

                var poll = await myDB
                           .Set <Poll>()
                           .Where(_ => _.Id == pollId)
                           .IncludeRelatedData()
                           .FirstOrDefaultAsync(cancellationToken);

                if (poll == null)
                {
                    return("Poll is publishing. Try later.", true, null);
                }

                var hostVote = poll.Votes.FirstOrDefault(_ => _.UserId == host.Id);
                if (hostVote == null)
                {
                    return("Poll is publishing. Try later.", true, null);
                }

                hostVote.Team |= VoteEnum.AutoApproveFriend;

                // approve already awaiting requests
                foreach (var friendship in await myDB.Set <Friendship>()
                         .Where(f => f.PollId == pollId && (f.Id == host.Id || f.FriendId == host.Id)).ToListAsync(cancellationToken))
                {
                    friendship.Type = FriendshipType.Approved;
                    if (poll.Votes.SingleOrDefault(v => (v.UserId == friendship.Id || v.UserId == friendship.FriendId) && v.UserId != host.Id) is { } vote)
                    {
                        try
                        {
                            if (vote.BotId is not {
                            } botId || !myBots.TryGetValue(botId, out var bot))
                            {
                                bot = myBot;
                            }
                            await myFriendshipService.SendCode(bot, vote.User, host, player, cancellationToken);
                        }
                        catch (ApiRequestException apiEx) when(apiEx.ErrorCode == 403)
                        {
                            // personal messages banned for host - propose user to ask for FC manually
                        }
                    }
                }
                await myDB.SaveChangesAsync(cancellationToken);

                await myBot.EditMessageReplyMarkupAsync(data, InlineKeyboardMarkup.Empty(), cancellationToken);

                return($"All invitees of `{poll.Title}` will be automatically approved.", false, null);

            case Commands.ApproveSettingsId:
                if (player == null)
                {
                    player = new Player
                    {
                        UserId = host.Id
                    };
                    myDB.Add(player);
                }
                if (bool.TryParse(commandParameter, out var autoApprove))
                {
                    player.AutoApproveFriendship = autoApprove;
                }
                else
                {
                    player.AutoApproveFriendship = null;
                }

                await myDB.SaveChangesAsync(cancellationToken);

                await myBot.EditMessageReplyMarkupAsync(data, FriendshipCommandHandler.GetInlineKeyboardMarkup(player), cancellationToken);

                return("Friendship settings modified", false, null);
            }

            return(null, false, null);
        }
Esempio n. 27
0
        public async Task <PollMessage> GetOrCreatePollAndMessage(PollMessage pollMessage, IUrlHelper urlHelper, VoteEnum?format = null, CancellationToken cancellationToken = default)
        {
            bool exRaidGym = false;
            var  pollId    = pollMessage.PollId;

            if (pollId < 0)
            {
                pollId    = pollMessage.PollId = -pollId;
                exRaidGym = true;
            }
            var poll = pollMessage.Poll ?? await myContext
                       .Set <Poll>()
                       .Where(_ => _.Id == pollId)
                       .IncludeRelatedData()
                       .Include(poll => poll.Notifications)
                       .FirstOrDefaultAsync(cancellationToken);

            if (poll != null)
            {
                var existingMessage = poll.Messages.FirstOrDefault(_ => _.InlineMessageId == pollMessage.InlineMessageId && _.ChatId == pollMessage.ChatId && _.MessageId == pollMessage.MessageId);
                if (existingMessage != null)
                {
                    return(existingMessage);
                }

                pollMessage.Poll = poll;
                return(await AddPollMessage(pollMessage, urlHelper, cancellationToken));
            }

            var pollData = GetTemporaryPoll(pollId);

            if (pollData == null)
            {
                return(null);
            }

            pollMessage.Poll = new Poll
            {
                Id           = pollId,
                Title        = pollData.Title,
                AllowedVotes = format,
                Owner        = pollData.Owner,
                Portal       = pollData.Portal,
                ExRaidGym    = exRaidGym,
                Time         = pollData.Time,
                TimeZoneId   = pollData.TimeZoneId,
                Votes        = pollData.Votes,
                Limits       = pollData.Limits
            };
            if (pollMessage.UserId is { } userId)
            {
                pollMessage.Poll.InitImplicitVotes(GetCachedUser(userId), pollMessage.BotId);
            }

            // MUST be set before portal
            myContext.Set <Poll>().Attach(pollMessage.Poll).State = EntityState.Added;

            if (pollData.Portal is { } portal)
            {
                var portalSet = myContext.Set <Portal>();
                // always check remote
                var existingPortal = await portalSet.AsNoTracking().FirstOrDefaultAsync(p => p.Guid == portal.Guid, cancellationToken);

                if (existingPortal == null)
                {
                    portalSet.Attach(portal).State = EntityState.Added;
                }
                else
                {
                    myContext.Entry(existingPortal).SetNotNullProperties(portal);
                    myContext.Entry(portal).SetNotNullProperties(existingPortal);
                }
            }

            return(await AddPollMessage(pollMessage, urlHelper, cancellationToken, withLog : true));
        }
Esempio n. 28
0
        public async Task <bool?> Handle(InlineQuery data, object context = default, CancellationToken cancellationToken = default)
        {
            IReadOnlyCollection <InlineQueryResultBase> inlineQueryResults;

            string query             = null;
            Portal portal            = null;
            bool   exRaidGym         = false;
            string switchPmParameter = null;

            foreach (var queryPart in data.Query.Split(' ', StringSplitOptions.RemoveEmptyEntries))
            {
                switch (queryPart)
                {
                case string _ when queryPart.StartsWith(GymInlineQueryHandler.PREFIX):
                    var guid = queryPart.Substring(GymInlineQueryHandler.PREFIX.Length);

                    if (guid.EndsWith('+'))
                    {
                        guid      = guid.Substring(0, guid.Length - 1);
                        exRaidGym = true;
                    }
                    var portalGuid = PortalEx.DecodeGuid(guid);
                    portal = await myIngressClient.Get(portalGuid, data.Location, cancellationToken);

                    break;

                default:
                    query += (query == null ? default(char?) : ' ') + queryPart;
                    break;
                }
            }
            if (string.IsNullOrWhiteSpace(data.Query)) // check whole query for sharing branch
            {
                inlineQueryResults = await myShareInlineQueryHandler.GetActivePolls(data.From, cancellationToken);
            }
            else if (string.IsNullOrWhiteSpace(query))
            {
                inlineQueryResults = new[]
                {
                    new InlineQueryResultArticle($"EnterPollTopic", "Enter a topic",
                                                 new InputTextMessageContent("Enter a topic to create a poll"))
                    {
                        Description = "to create a poll",
                        ThumbUrl    = myUrlHelper.AssetsContent("static_assets/png/POI_Submission_Illustration_02.png").ToString()
                    }
                };
            }
            else
            {
                var pollId = await myRaidService.GetPollId(new Poll(data) { Title = query, Portal = portal }, cancellationToken);

                switchPmParameter = portal == null ? $"{SwitchToGymParameter}{pollId}" : null;
                ICollection <VoteEnum> voteFormats = await myDb.Set <Settings>().GetFormats(data.From.Id, cancellationToken).ToListAsync(cancellationToken);

                if (voteFormats.Count == 0)
                {
                    voteFormats = VoteEnumEx.DefaultVoteFormats;
                }
                inlineQueryResults = voteFormats
                                     .Select(format => new Poll
                {
                    Id           = exRaidGym ? -pollId : pollId,
                    Title        = query,
                    AllowedVotes = format,
                    Portal       = portal,
                    ExRaidGym    = exRaidGym
                })
                                     .Select((fakePoll, i) => new InlineQueryResultArticle(fakePoll.GetInlineId(i), fakePoll.GetTitle(myUrlHelper),
                                                                                           fakePoll.GetMessageText(myUrlHelper, disableWebPreview: fakePoll.DisableWebPreview()))
                {
                    Description = fakePoll.AllowedVotes?.Format(new StringBuilder("Create a poll ")).ToString(),
                    HideUrl     = true,
                    ThumbUrl    = fakePoll.GetThumbUrl(myUrlHelper).ToString(),
                    ReplyMarkup = fakePoll.GetReplyMarkup()
                })
                                     .ToArray();
            }

            await myBot.AnswerInlineQueryWithValidationAsync(data.Id, inlineQueryResults,
                                                             switchPmText : switchPmParameter != null? "Link the poll to a gym" : null, switchPmParameter : switchPmParameter,
                                                             cacheTime : 0, cancellationToken : cancellationToken);

            await myDb.SaveChangesAsync(cancellationToken);

            return(true);
        }
        public async Task <bool?> Handle(InlineQuery data, object context = default, CancellationToken cancellationToken = default)
        {
            var location   = data.Location;
            var queryParts = data.Query.Split(' ', StringSplitOptions.RemoveEmptyEntries);

            var poll        = default(Poll);
            var pollQuery   = new List <string>(queryParts.Length);
            var searchQuery = new List <string>(queryParts.Length);
            var query       = pollQuery;

            foreach (var queryPart in queryParts)
            {
                switch (queryPart)
                {
                case string locationPart when OpenLocationCode.IsValid(locationPart):
                    location = OpenLocationCode.Decode(locationPart) is var code
              ? new Location
                    {
                        Longitude = (float)code.CenterLongitude, Latitude = (float)code.CenterLatitude
                    }

              : location;
                    break;

                case string part when Regex.Match(part, PATTERN) is Match match && match.Success:
                    if (int.TryParse(match.Groups["pollId"].Value, out var pollId))
                    {
                        poll = myRaidService.GetTemporaryPoll(pollId);
                    }

                    query = searchQuery;

                    break;

                default:
                    query.Add(queryPart);
                    break;
                }
            }

            Portal[] portals;
            if (searchQuery.Count == 0)
            {
                portals = await myIngressClient.GetPortals(0.200, location, cancellationToken);
            }
            else
            {
                portals = await myIngressClient.Search(searchQuery, location, cancellationToken);
            }

            var results = new List <InlineQueryResultBase>(Math.Min(portals.Length, MAX_PORTALS_PER_RESPONSE) + 2);

            if ((poll == null) && (pollQuery.Count != 0))
            {
                var voteFormat = await myDb.Set <Settings>().GetFormat(data.From.Id, cancellationToken);

                for (var i = 0; i < portals.Length && i < MAX_PORTALS_PER_RESPONSE; i++)
                {
                    poll = new Poll(data)
                    {
                        Title        = string.Join("  ", pollQuery),
                        AllowedVotes = voteFormat,
                        Portal       = portals[i],
                        ExRaidGym    = false
                    };
                    await myRaidService.GetPollId(poll, cancellationToken);

                    results.Add(new InlineQueryResultArticle(poll.GetInlineId(), poll.GetTitle(myUrlHelper),
                                                             poll.GetMessageText(myUrlHelper, disableWebPreview: poll.DisableWebPreview()))
                    {
                        Description = poll.AllowedVotes?.Format(new StringBuilder("Create a poll ")).ToString(),
                        HideUrl     = true,
                        ThumbUrl    = poll.GetThumbUrl(myUrlHelper).ToString(),
                        ReplyMarkup = poll.GetReplyMarkup()
                    });

                    if (i == 0)
                    {
                        poll.Id        = -poll.Id;
                        poll.ExRaidGym = true;
                        results.Add(new InlineQueryResultArticle(poll.GetInlineId(), poll.GetTitle(myUrlHelper) + " (EX Raid Gym)",
                                                                 poll.GetMessageText(myUrlHelper, disableWebPreview: poll.DisableWebPreview()))
                        {
                            Description = poll.AllowedVotes?.Format(new StringBuilder("Create a poll ")).ToString(),
                            HideUrl     = true,
                            ThumbUrl    = poll.GetThumbUrl(myUrlHelper).ToString(),
                            ReplyMarkup = poll.GetReplyMarkup()
                        });
                    }
                }
            }
            else
            {
                for (var i = 0; i < portals.Length && i < MAX_PORTALS_PER_RESPONSE; i++)
                {
                    var portal = portals[i];
                    var title  = portal.Name is string name && !string.IsNullOrWhiteSpace(name) ? name : portal.Guid;
                    if (portal.EncodeGuid() is string portalGuid)
                    {
                        InlineQueryResultArticle Init(InlineQueryResultArticle article, InlineKeyboardButton createButton)
                        {
                            const int thumbnailSize = 64;

                            article.Description = portal.Address;
                            article.ReplyMarkup = new InlineKeyboardMarkup(createButton);
                            article.ThumbUrl    = portal.GetImage(myUrlHelper, thumbnailSize)?.AbsoluteUri;
                            article.ThumbHeight = thumbnailSize;
                            article.ThumbWidth  = thumbnailSize;
                            return(article);
                        }

                        var portalContent = new StringBuilder()
                                            .Bold((builder, mode) => builder.Sanitize(portal.Name)).NewLine()
                                            .Sanitize(portal.Address)
                                            .Link("\u200B", portal.Image)
                                            .ToTextMessageContent();
                        results.Add(Init(
                                        new InlineQueryResultArticle($"portal:{portal.Guid}", title, portalContent),
                                        InlineKeyboardButton.WithSwitchInlineQuery("Create a poll", $"{PREFIX}{portalGuid} {poll?.Title}")));

                        if (i == 0)
                        {
                            var exRaidPortalContent = new StringBuilder()
                                                      .Sanitize("☆ ")
                                                      .Bold((builder, mode) => builder.Sanitize(portal.Name))
                                                      .Sanitize(" (EX Raid Gym)").NewLine()
                                                      .Sanitize(portal.Address)
                                                      .Link("\u200B", portal.Image)
                                                      .ToTextMessageContent();
                            results.Add(Init(
                                            new InlineQueryResultArticle($"portal:{portal.Guid}+", $"☆ {title} (EX Raid Gym)", exRaidPortalContent),
                                            InlineKeyboardButton.WithSwitchInlineQuery("Create a poll ☆ (EX Raid Gym)", $"{PREFIX}{portalGuid}+ {poll?.Title}")));
                        }
                    }
                }
            }

            if (searchQuery.Count == 0)
            {
                results.Add(
                    new InlineQueryResultArticle("EnterGymName", "Enter a Gym's Title", new InputTextMessageContent("Enter a Gym's Title to search"))
                {
                    Description = "to search",
                    ThumbUrl    = default(Portal).GetImage(myUrlHelper)?.AbsoluteUri
                });
            }

            if (results.Count == 0)
            {
                var search = string.Join(" ", searchQuery);
                results.Add(new InlineQueryResultArticle("NothingFound", "Nothing found",
                                                         new StringBuilder($"Nothing found by request ").Code((builder, mode) => builder.Sanitize(search, mode)).ToTextMessageContent())
                {
                    Description = $"Request {search}",
                    ThumbUrl    = myUrlHelper.AssetsContent(@"static_assets/png/btn_close_normal.png").AbsoluteUri
                });
            }

            await myBot.AnswerInlineQueryWithValidationAsync(data.Id, results, cacheTime : 0, isPersonal : true, cancellationToken : cancellationToken);

            await myDb.SaveChangesAsync(cancellationToken);

            return(true);
        }
Esempio n. 30
0
        public async Task <bool?> Handle(MessageEntityEx entity, PollMessage pollMessage, CancellationToken cancellationToken = default)
        {
            var commandText = entity.AfterValue.Trim();

            switch (entity.Command.ToString().ToLowerInvariant())
            {
            case "/new":
                var title = commandText;
                if (StringSegment.IsNullOrEmpty(title))
                {
                    return(false);
                }

                pollMessage.Poll = new Poll(myMessage)
                {
                    Title = title.Value
                };
                return(true);

            case "/poll"  when PollEx.TryGetPollId(commandText, out var pollId, out _):
            case "/start" when PollEx.TryGetPollId(commandText, out pollId, out _):

                var existingPoll = await myContext
                                   .Set <Poll>()
                                   .Where(_ => _.Id == pollId)
                                   .IncludeRelatedData()
                                   .FirstOrDefaultAsync(cancellationToken);

                if (existingPoll == null)
                {
                    return(false);
                }

                pollMessage.Poll = existingPoll;
                return(true);

            case "/set":

                var(setContent, setReplyMarkup) = await mySetCallbackQueryHandler.SettingsList(myMessage.Chat.Id, cancellationToken);

                await myTelegramBotClient.SendTextMessageAsync(myMessage.Chat, setContent.MessageText, setContent.ParseMode, setContent.DisableWebPagePreview,
                                                               disableNotification : true, replyMarkup : setReplyMarkup, cancellationToken : cancellationToken);

                return(false); // processed, but not pollMessage

            case "/help" when myMessage.Chat.Type == ChatType.Private:
                await myTelegramBotClient.SendTextMessageAsync(myMessage.Chat, "http://telegra.ph/Raid-Battles-Bot-Help-02-18", cancellationToken : cancellationToken);

                return(false); // processed, but not pollMessage

            // deep linking to gym
            case "/start" when commandText.StartsWith(GeneralInlineQueryHandler.SwitchToGymParameter, StringComparison.Ordinal):
                var query = commandText.Substring(GeneralInlineQueryHandler.SwitchToGymParameter.Length);

                var pollTitle = new StringBuilder("Poll creation");
                if (int.TryParse(query, out int gymPollId))
                {
                    pollTitle
                    .NewLine()
                    .Bold((builder, m) => builder.Sanitize(myRaidService.GetTemporaryPoll(gymPollId)?.Title, m));
                }

                var content = pollTitle.ToTextMessageContent();
                await myTelegramBotClient.SendTextMessageAsync(myMessage.Chat, content.MessageText, content.ParseMode, content.DisableWebPagePreview, disableNotification : true,
                                                               replyMarkup : new InlineKeyboardMarkup(InlineKeyboardButton.WithSwitchInlineQueryCurrentChat("Choose a Gym", $"{GymInlineQueryHandler.PREFIX}{query} ")), cancellationToken : cancellationToken);

                return(false);

            case "/p" when int.TryParse(commandText, out var pollId):
                var poll = await myContext
                           .Set <Poll>()
                           .Where(_ => _.Id == pollId)
                           .IncludeRelatedData()
                           .FirstOrDefaultAsync(cancellationToken);

                if (poll == null)
                {
                    return(false);
                }

                var voters = poll.Votes.ToDictionary(vote => vote.UserId, vote => vote.User);
                var users  = poll.Messages
                             .Where(message => message.UserId != null)
                             .Select(message => message.UserId.Value)
                             .Distinct()
                             .ToList();
                var unknownUsers = users.Where(u => !voters.ContainsKey(u)).ToList();
                var data         = await myContext
                                   .Set <Vote>()
                                   .Where(v => unknownUsers.Contains(v.UserId))
                                   .GroupBy(v => v.UserId)
                                   .Select(vv => vv.OrderByDescending(vote => vote.Modified).First())
                                   .ToListAsync(cancellationToken);

                var allVoters = voters.Values.Concat(data.Select(d => d.User))
                                .ToDictionary(u => u.Id, u => u);
                var info = users
                           .Select(u => allVoters.TryGetValue(u, out var user) ?
                                   user : new User {
                    Id = u, FirstName = u.ToString()
                })
                           .Aggregate(
                    poll.GetDescription(myUrlHelper).NewLine().NewLine(),
                    (builder, user) => builder.Append(user.GetLink()).NewLine())
                           .ToTextMessageContent(disableWebPreview: true);

                await myTelegramBotClient.SendTextMessageAsync(myMessage.Chat, info.MessageText, info.ParseMode, info.DisableWebPagePreview, disableNotification : true,
                                                               replyToMessageId : myMessage.MessageId, cancellationToken : cancellationToken);

                return(false);
            }

            return(null);
        }