🚧 RSS as a module

This commit is contained in:
2020-03-02 00:00:52 +04:00
parent e86049f4fd
commit eac0da5016
16 changed files with 1185 additions and 0 deletions
+84
View File
@@ -0,0 +1,84 @@
/// This file is a part of RSS Bot for Telegram.
/// License: MIT
/// Author: CrazyHackGUT aka Kruzya (Sergey Gut) <kruzefag@gmail.com>
///
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace Kruzya.TelegramBot.RichSiteSummary.Data
{
/// <summary>
/// Represents a feed in database.
/// </summary>
public class Feed : RepresentableEntity
{
#region RepresentableEntity
protected override string ViewableText
{
get => Title;
}
protected override string ViewableUrl
{
get => HomePage;
}
#endregion
/// <summary>
/// The unique feed id.
/// </summary>
[Key]
public Guid FeedId { get; set; }
/// <summary>
/// The feed URL.
/// </summary>
[Required]
public string Url { get; set; }
/// <summary>
/// The home page for this feed.
/// </summary>
[Required]
public string HomePage { get; set; }
/// <summary>
/// Feed title.
/// </summary>
[MaxLength(256)]
[Required]
public string Title { get; set; }
/// <summary>
/// DateTime when feed fetched successfully in last time.
/// </summary>
[Required]
public DateTime UpdatedAt { get; set; }
/// <summary>
/// This flag indicates, bot should monitor changes in post date, or not.
/// </summary>
[Required]
[DefaultValue(false)]
public bool WatchPostDate { get; set; }
public Feed()
{
FeedId = new Guid();
UpdatedAt = DateTime.Now;
}
/// <summary>
/// All posts from this feed.
/// </summary>
public virtual ICollection<Post> Posts { get; set; }
/// <summary>
/// All exists subscriber for this feed.
/// </summary>
public virtual ICollection<Subscriber> Subscribers { get; set; }
}
}
+98
View File
@@ -0,0 +1,98 @@
/// This file is a part of RSS Bot for Telegram.
/// License: MIT
/// Author: CrazyHackGUT aka Kruzya (Sergey Gut) <kruzefag@gmail.com>
///
using System;
using System.ComponentModel.DataAnnotations;
using System.Text;
using Telegram.Bot.Types.Enums;
namespace Kruzya.TelegramBot.RichSiteSummary.Data
{
/// <summary>
/// Represents a post from feed in database.
/// </summary>
public class Post : RepresentableEntity
{
#region RepresentableEntity
protected override ParseMode DefaultRepresentation
{
get => ParseMode.Html;
}
protected override string ViewableText
{
get => Title;
}
protected override string ViewableUrl
{
get => Url;
}
#endregion
/// <summary>
/// The unique post id.
/// </summary>
[Key]
public Guid PostId { get; set; }
/// <summary>
/// The feed unique identifier from where this post is fetched.
/// </summary>
public Guid FeedId { get; set; }
/// <summary>
/// The feed from where this post is fetched.
/// </summary>
[Required]
public virtual Feed Feed { get; set; }
/// <summary>
/// The post title.
/// </summary>
[MaxLength(256)]
[Required]
public string Title { get; set; }
/// <summary>
/// The URI where this post is located in web.
/// </summary>
[Required]
public string Url { get; set; }
/// <summary>
/// DateTime when post is created in RSS feed.
/// </summary>
[Required]
public DateTime PostedAt { get; set; }
/// <summary>
/// DateTime when post is fetched/updated in database.
/// </summary>
[Required]
public DateTime ReceivedAt { get; set; }
public Post()
{
PostId = new Guid();
ReceivedAt = DateTime.Now;
}
public string MessageText
{
get
{
var message = new StringBuilder();
message.Append($"{Representation("🗞")} New on {Feed.Representation(ParseMode.Html)}\n");
message.Append("\n");
message.Append(Title);
message.Append("\n");
message.Append(Representation("Open in browser"));
return message.ToString();
}
}
}
}
@@ -0,0 +1,65 @@
/// This file is a part of RSS Bot for Telegram.
/// License: MIT
/// Author: CrazyHackGUT aka Kruzya (Sergey Gut) <kruzefag@gmail.com>
///
using System;
using System.Web;
using Telegram.Bot.Types.Enums;
namespace Kruzya.TelegramBot.RichSiteSummary.Data
{
/// <summary>
/// Implements the basic logic for rendering Database Entities in messages.
/// </summary>
public abstract class RepresentableEntity
{
protected virtual string ViewableText { get; }
protected virtual string ViewableUrl { get; }
protected virtual ParseMode DefaultRepresentation { get; }
public override string ToString() => Representation();
public string Representation(string text) => Representation(DefaultRepresentation, text);
public string Representation() => Representation(DefaultRepresentation);
public string Representation(ParseMode parseMode) => Representation(parseMode, ViewableText);
public string Representation(ParseMode parseMode, string text)
{
string content = String.Empty;
switch (parseMode)
{
case ParseMode.Default:
content = RawRepresentation(text);
break;
case ParseMode.Html:
content = HtmlRepresentation(text);
break;
case ParseMode.Markdown:
case ParseMode.MarkdownV2:
throw new NotImplementedException();
}
return content;
}
#region Representations
public string RawRepresentation(string text)
=> text;
public string HtmlRepresentation(string text)
{
var escapedText = HttpUtility.HtmlEncode(text);
var escapedUrl = HttpUtility.HtmlAttributeEncode(ViewableUrl);
return $"<a href=\"{escapedUrl}\">{escapedText}</a>";
}
#endregion
}
}
@@ -0,0 +1,57 @@
using Microsoft.EntityFrameworkCore;
namespace Kruzya.TelegramBot.RichSiteSummary.Data
{
public class RichSiteSummaryContext : DbContext
{
/// <summary>
/// Repository with all feeds in database.
/// </summary>
public DbSet<Feed> Feeds { get; set; }
/// <summary>
/// Repository with all posts in database.
/// </summary>
public DbSet<Post> Posts { get; set; }
/// <summary>
/// Repository with all subscriptions in database.
/// </summary>
public DbSet<Subscriber> Subscriptions { get; set; }
/// <summary>
/// Ensures the database is created and calls parent constructor.
/// </summary>
/// <param name="options"></param>
public RichSiteSummaryContext(DbContextOptions<RichSiteSummaryContext> options) : base(options)
{
Database.EnsureCreated();
}
/// <summary>
/// Setup the additional unique keys.
/// </summary>
/// <param name="modelBuilder"></param>
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Setup unique keys for Feed entity.
modelBuilder.Entity<Feed>().HasIndex(feed => feed.Url).IsUnique();
modelBuilder.Entity<Feed>().HasIndex(feed => feed.HomePage).IsUnique();
// Setup unique keys for Post entity.
modelBuilder.Entity<Post>()
.HasOne(p => p.Feed)
.WithMany(f => f.Posts)
.HasForeignKey(p => p.FeedId);
modelBuilder.Entity<Post>().HasIndex(post =>
new {post.FeedId, post.Url}).IsUnique();
// Setup foreign keys for Subscriber entity.
modelBuilder.Entity<Subscriber>()
.HasOne(s => s.Feed)
.WithMany(f => f.Subscribers)
.HasForeignKey(s => s.FeedId);
}
}
}
@@ -0,0 +1,56 @@
/// This file is a part of RSS Bot for Telegram.
/// License: MIT
/// Author: CrazyHackGUT aka Kruzya (Sergey Gut) <kruzefag@gmail.com>
///
using System;
using System.ComponentModel.DataAnnotations;
namespace Kruzya.TelegramBot.RichSiteSummary.Data
{
/// <summary>
/// Represents a subscription in database.
/// </summary>
public class Subscriber
{
/// <summary>
/// The unique Subscription identifier.
/// </summary>
[Key]
public Guid SubscriptionId { get; set; }
/// <summary>
/// The Telegram subscriber id.
/// </summary>
[Required]
public Int64 SubscriberId { get; set; }
/// <summary>
/// Unique feed identifier related with this Subscription. Identifies what user is read.
/// </summary>
public Guid FeedId { get; set; }
/// <summary>
/// Feed related with this Subscription. Identifies what user is read.
/// </summary>
[Required]
public virtual Feed Feed { get; set; }
/// <summary>
/// When user is subscribed on this feed.
/// </summary>
[Required]
public DateTime SubscribedAt { get; set; }
public Subscriber()
{
SubscriptionId = new Guid();
SubscribedAt = DateTime.Now;
}
public Subscriber(Feed feed) : base()
{
Feed = feed;
}
}
}