using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using BotFramework.Attributes; using BotFramework.Enums; using Kruzya.TelegramBot.Core.Extensions; using Kruzya.TelegramBot.RichSiteSummary.Data; using Microsoft.EntityFrameworkCore; using Telegram.Bot; using Telegram.Bot.Types; using Telegram.Bot.Types.Enums; using Telegram.Bot.Types.ReplyMarkups; namespace Kruzya.TelegramBot.RichSiteSummary.Handler { public class FeedList : AbstractHandler { public FeedList(RichSiteSummaryContext dbContext) : base(dbContext) { } [ParametrizedCommand("rss_show")] public async Task ShowFeed(Feed feed) => await ShowFeed(feed, Chat, From); public async Task ShowFeed(Guid feedId, Chat chat, User from) { var feed = await _dbContext.FindAsync(feedId); if (feed == null) { return; } await ShowFeed(feed, chat, from); } public async Task ShowFeed(Feed feed, Chat chat, User from, Message message = null) { var textBuilder = new StringBuilder(); textBuilder.Append(feed.Representation(ParseMode.Html)); textBuilder.Append("\n"); textBuilder.Append($"Posts at this moment: {feed.Posts.Count}\n"); textBuilder.Append($"RSS feed: {feed.Url}\n"); textBuilder.Append($"Home page: {feed.HomePage}\n"); textBuilder.Append($"Last synchronization: {feed.UpdatedAt}"); var text = textBuilder.ToString(); var buttons = new List(); var callbackData = $"feed|{from.Id}|do|{feed.FeedId}"; if (await _dbContext.Subscriptions.HasSubscription(chat, feed)) { buttons.Add(new InlineKeyboardButton("Unsubscribe") { CallbackData = callbackData, }); } else { buttons.Add(new InlineKeyboardButton("Subscribe") { CallbackData = callbackData, }); } buttons.Add(new InlineKeyboardButton("Last 10 posts") { CallbackData = $"feed|{from.Id}|last|{feed.FeedId}", }); var inlineButtons = new InlineKeyboardMarkup(buttons); if (message == null) { await Bot.SendTextMessageAsync(new ChatId(chat.Id), text, 0, ParseMode.Html, null, true, true, false, 0, replyMarkup: inlineButtons); } else { await Bot.EditMessageTextAsync(chat, message.MessageId, text, ParseMode.Html, null, true, replyMarkup: inlineButtons); } } [ParametrizedCommand("rss_list")] public async Task List() => await GenerateMenu(); [ParametrizedCommand("rss_search")] public async Task Search(string query) { if (!(await GenerateMenu(query))) { await Bot.SendTextMessageAsync(Chat, $"No one feed match by search pattern ({query}).", 0, ParseMode.Html, null, true); } } [Command("rss_subscriptions")] public async Task Subscriptions() { if (!(await GenerateMenu(":sub"))) { await Bot.SendTextMessageAsync(Chat, "There are no one subscription. Add new with /feed_list or /feed_search."); } } #region Message generator protected async Task GenerateMenu() => await GenerateMenu(string.Empty); protected async Task GenerateMenu(string query) => await GenerateMenu(query, 0); protected async Task GenerateMenu(string query, UInt16 page, Message editableMessage = null) { var chat = editableMessage != null ? (editableMessage.Chat) : Chat; var elementsOnPage = 9; var startIndex = page * elementsOnPage; var dbQuery = _dbContext.Feeds.AsQueryable() .Where(f => !f.IsPrivate); if (!string.IsNullOrWhiteSpace(query)) { var searchQuery = query.ToLower(); if (query.EndsWith(":sub")) { var chatList = await _dbContext.Subscriptions.AllSubscriptions(chat).Select(chat => chat.Feed.FeedId).ToArrayAsync(); dbQuery = dbQuery.Where(sub => chatList.Contains(sub.FeedId)); searchQuery = searchQuery.Remove(searchQuery.Length - 4, 4); } dbQuery = dbQuery.Where(feed => feed.Title.ToLower().Contains(searchQuery) || feed.Url.ToLower().Contains(searchQuery) || feed.HomePage.ToLower().Contains(searchQuery)); } var totalCount = await dbQuery.CountAsync(); if (totalCount == 0) { return false; } if (totalCount == 1) { await ShowFeed((await dbQuery.FirstOrDefaultAsync()), Chat, From); return true; } var result = await dbQuery.Take(elementsOnPage * (page + 1)).Skip(startIndex) .ToArrayAsync(); var backButton = (page > 0); var nextButton = totalCount >= (startIndex + elementsOnPage); var buttons = new List>(); List row; foreach (var feed in result) { row = new List(); row.Add(new InlineKeyboardButton(feed.Title) { CallbackData = $"feed|{From.Id}|show|{feed.FeedId.ToString()}", }); buttons.Add(row); } if (backButton || nextButton) { row = new List(); if (backButton) { row.Add(new InlineKeyboardButton("⬅️") { CallbackData = $"feed|{From.Id}|page|{query}|{page - 1}", }); } row.Add(new InlineKeyboardButton($"{page + 1}/{(totalCount / elementsOnPage) + 1}") { CallbackData = $"feed|{From.Id}|donothing", }); if (nextButton) { row.Add(new InlineKeyboardButton("➡️") { CallbackData = $"feed|{From.Id}|page|{query}|{page + 1}", }); } buttons.Add(row); } if (editableMessage == null) { await Bot.SendTextMessageAsync(Chat, "Select the feed for viewing details", 0, null, null, true, true, false, 0, replyMarkup: new InlineKeyboardMarkup(buttons)); } else { await Bot.EditMessageTextAsync(editableMessage.Chat, editableMessage.MessageId, "Select the feed for viewing details", null, null, false, new InlineKeyboardMarkup(buttons)); } return true; } #endregion #region Message handler [Update(UpdateFlag.CallbackQuery)] public async Task HandleAction() { if (!RawUpdate.CallbackQuery.Data.StartsWith("feed|")) { return; } var data = RawUpdate.CallbackQuery.Data.Split('|'); var fromId = Int64.Parse(data[1]); if (fromId != From.Id) { await Bot.AnswerCallbackQueryAsync(RawUpdate.CallbackQuery.Id, "You can't perform this action: command called by another user.", true); return; } // Check action. switch (data[2]) { case "page": await GenerateMenu(data[3], UInt16.Parse(data[4]), RawUpdate.CallbackQuery.Message); break; case "show": await ShowFeed(Guid.Parse(data[3]), RawUpdate.CallbackQuery.Message.Chat, RawUpdate.CallbackQuery.From); break; case "do": if (RawUpdate.CallbackQuery.From.Id != RawUpdate.CallbackQuery.Message.Chat.Id) { var userStatus = (await Bot.GetChatMemberAsync(RawUpdate.CallbackQuery.Message.Chat, RawUpdate.CallbackQuery.From.Id)).Status; var allowedUserStatuses = new ChatMemberStatus[] {ChatMemberStatus.Administrator, ChatMemberStatus.Creator}; if (!allowedUserStatuses.Contains(userStatus)) { await Bot.AnswerCallbackQueryAsync(RawUpdate.CallbackQuery.Id, "You can't perform this action: you don't have administrator permissions.", true); return; } } await ChangeFeedSubscription(Guid.Parse(data[3]), RawUpdate.CallbackQuery.Message.Chat, RawUpdate.CallbackQuery.From, RawUpdate.CallbackQuery.Message); break; case "last": var feed = await _dbContext.FindAsync(Guid.Parse(data[3])); var posts = feed.Posts.OrderByDescending(p => p.ReceivedAt).Take(10).ToList(); var messageTextBuilder = new StringBuilder(); messageTextBuilder.Append($"Last 10 posts from {feed.Representation(ParseMode.Html)}\n"); messageTextBuilder.Append("\n"); var idx = 1; foreach (var post in posts) { messageTextBuilder.Append($"{idx}. {post}\n"); idx++; } await Bot.SendTextMessageAsync(RawUpdate.CallbackQuery.Message.Chat, messageTextBuilder.ToString(), 0, ParseMode.Html, null, true); break; } await Bot.AnswerCallbackQueryAsync(RawUpdate.CallbackQuery.Id); } protected async Task ChangeFeedSubscription(Guid feedId, Chat chat, User from, Message message) { var feed = await _dbContext.FindAsync(feedId); if (feed == null) { return; } var subscriptionState = await _dbContext.Subscriptions.HasSubscription(chat, feed); if (subscriptionState) { var subId = await _dbContext.Subscriptions .Where(subscriber => subscriber.Feed == feed && subscriber.SubscriberId == chat.Id) .Select(subscriber => subscriber.SubscriptionId).FirstAsync(); _dbContext.Remove(_dbContext.Find(subId)); } else { var subscription = _dbContext.Subscriptions.Create(); subscription.Feed = feed; subscription.SubscriberId = chat.Id; _dbContext.Add(subscription); } await _dbContext.SaveChangesAsync(); await ShowFeed(feed, chat, from, message); } #endregion } }