Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
namespace RealEstatesWatcher.AdPostsHandlers.Base.Html;

public static class CommonHtmlTemplateElements
{
public const string FullPage = """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Real Estate Advertisements</title>
</head>
<body style="max-width: 800px; margin:10px auto;">
<maintitle/>
<posts/>
</body>
</html>
""";

public const string TitleNewPosts = """
<h1>🏦 <span style="color: #4f4f4f; font-style: italic;">NEW Real estate offer</span></h1>
""";

public const string TitleInitialPosts = """
<h1>🏦 <span style="color: #4f4f4f; font-style: italic;"> Current Real estate offer</span></h1>
""";

public const string Post = """
<div style="padding: 10px; background: #ededed; min-height: 200px;">
<div style="float: left; margin-right: 1em; width: 30%; height: 180px; display: {$img-display};">
<img src="{$img-link}" style="height: 100%; width: 100%; object-fit: cover;" alt="Ad main visual presentation"/>
</div>
<a href="{$post-link}">
<h3 style="margin: 0.2em; margin-top: 0;">{$title}</h3>
</a>
<span style="font-size: medium; color: #4f4f4f; display: {$price-display};">
<strong>{$price}</strong> {$currency}
<span style="display: {$additional-fees-display};"> + {$additional-fees} {$currency}</span><br/>
</span>
<span style="font-size: medium; color: #4f4f4f; display: {$price-comment-display};">
<strong>{$price-comment}</strong><br/>
</span>
<span>
<strong>Server:</strong> {$portal-name}<br/>
<strong>Adresa:</strong> {$address}
<span style="display: {$address-links-display};">
<a target="_blank" rel="noopener noreferrer" href="https://www.google.com/maps/search/?api=1&query={$address-encoded}"><img alt="Google's map logo" title="Otvoriť v Google Mapách" src="https://upload.wikimedia.org/wikipedia/commons/thumb/a/aa/Google_Maps_icon_%282020%29.svg/32px-Google_Maps_icon_%282020%29.svg.png?20200218211225" style="margin-left: 6px; max-height: 14px; width: auto" /></a>
<a target="_blank" rel="noopener noreferrer" href="https://mapy.com/fnc/v1/search?query={$address-encoded}"><img alt="Icon of Mapy.com" title="Otvoriť na Mapy.com" src="https://mapy.com/img/favicon/common/plain/favicon-32x32.png" style="max-height: 14px; width: auto"></a>
<a target="_blank" rel="noopener noreferrer" href="https://maps.apple.com/?q={$address-encoded}"><img alt="Icon of Apple Maps" title="Otvoriť v Apple Mapách" src="https://maps.apple.com/static/maps-app-web-client/images/maps-app-icon-120x120.png" style="max-height: 14px; width: auto"></a>
</span><br/>
<strong>Výmera:</strong> {$floor-area}<br/>
<strong>Dispozícia:</strong> {$layout}<br/>
</span>
<p style="margin: 0.2em; font-size: small; text-align: justify; display: {$text-display};">{$text}</p>
</div>
""";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
using RealEstatesWatcher.Models;

using System.Globalization;
using System.Text;
using System.Web;

namespace RealEstatesWatcher.AdPostsHandlers.Base.Html;

public abstract class HtmlBasedAdPostsHandlerBase(NumberFormatInfo numberFormat)
{
protected virtual string CreateHtmlPostElement(RealEstateAdPost post)
{
var postHtmlBuilder = new StringBuilder(CommonHtmlTemplateElements.Post)
.Replace("{$title}", post.Title)
.Replace("{$portal-name}", post.AdsPortalName)
.Replace("{$post-link}", post.WebUrl.AbsoluteUri)
.Replace("{$address}", post.Address);

// address links
if (!string.IsNullOrEmpty(post.Address))
{
postHtmlBuilder.Replace("{$address-links-display}", "inline-block")
.Replace("{$address-encoded}", HttpUtility.UrlEncode(post.Address));
}
else
{
postHtmlBuilder.Replace("{$address-links-display}", "none");
}

// layout
postHtmlBuilder.Replace("{$layout}", post.Layout is not Layout.NotSpecified ? post.Layout.ToDisplayString() : "-");

// floor area
postHtmlBuilder.Replace("{$floor-area}", post.FloorArea is not null and not decimal.Zero ? post.FloorArea + " m²" : "-");

// image
if (post.ImageUrl is not null)
{
postHtmlBuilder.Replace("{$img-link}", post.ImageUrl.AbsoluteUri)
.Replace("{$img-display}", "block");
}
else
{
postHtmlBuilder.Replace("{$img-link}", string.Empty)
.Replace("{$img-display}", "none");
}

// price
if (post.Price is not decimal.Zero)
{
postHtmlBuilder.Replace("{$price}", post.Price.ToString("N", numberFormat))
.Replace("{$currency}", post.Currency.ToString())
.Replace("{$price-display}", "block")
.Replace("{$price-comment-display}", "none")
.Replace("{$price-comment}", string.Empty);
}
else
{
postHtmlBuilder.Replace("{$price-comment}", post.PriceComment ?? "-")
.Replace("{$price-comment-display}", "block")
.Replace("{$price-display}", "none")
.Replace("{$price}", string.Empty)
.Replace("{$currency}", string.Empty);
}

// additional fees
if (post.AdditionalFees is not null and not decimal.Zero)
{
postHtmlBuilder.Replace("{$additional-fees}", post.AdditionalFees.Value.ToString("N", numberFormat))
.Replace("{$additional-fees-display}", "inline-block");
}
else
{
postHtmlBuilder.Replace("{$additional-fees}", decimal.Zero.ToString("N"))
.Replace("{$additional-fees-display}", "none");
}

// text
postHtmlBuilder.Replace("{$text}", post.Text)
.Replace("{$text-display}", !string.IsNullOrEmpty(post.Text) ? "table" : "none");

return postHtmlBuilder.ToString();
}

protected virtual string CreateFullHtmlPage(IEnumerable<RealEstateAdPost> posts, string titleHtmlElement) =>
CommonHtmlTemplateElements.FullPage
.Replace("<maintitle/>", titleHtmlElement)
.Replace("<posts/>", string.Join(Environment.NewLine, posts.Select(CreateHtmlPostElement)));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\RealEstatesWatcher.Models\RealEstatesWatcher.Models.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,18 @@

using MimeKit;

using RealEstatesWatcher.AdPostsHandlers.Base.Html;
using RealEstatesWatcher.AdPostsHandlers.Contracts;
using RealEstatesWatcher.AdPostsHandlers.Templates;
using RealEstatesWatcher.Models;

using System.Globalization;
using System.Net;
using System.Text;
using System.Web;

namespace RealEstatesWatcher.AdPostsHandlers.Email;

public class EmailNotifyingAdPostsHandler(EmailNotifyingAdPostsHandlerSettings settings,
ILogger<EmailNotifyingAdPostsHandler>? logger = null) : IRealEstateAdPostsHandler
NumberFormatInfo? numberFormat = null,
ILogger<EmailNotifyingAdPostsHandler>? logger = null) : HtmlBasedAdPostsHandlerBase(numberFormat ?? NumberFormatInfo.CurrentInfo), IRealEstateAdPostsHandler
{
private readonly EmailNotifyingAdPostsHandlerSettings _settings = settings ?? throw new ArgumentNullException(nameof(settings));

Expand All @@ -28,7 +27,7 @@ public async Task HandleNewRealEstateAdPostAsync(RealEstateAdPost adPost, Cancel

logger?.LogDebug("Received new Real Estate Ad Post: {Post}", adPost);

await SendEmailAsync("🆕 New Real Estate Advert published!", CreateHtmlBody(adPost, CommonHtmlTemplateElements.TitleNewPosts), cancellationToken).ConfigureAwait(false);
await SendEmailAsync("🆕 New Real Estate Advert published!", CreateFullHtmlPage([adPost], CommonHtmlTemplateElements.TitleNewPosts), cancellationToken).ConfigureAwait(false);
}

public async Task HandleNewRealEstatesAdPostsAsync(IList<RealEstateAdPost> adPosts, CancellationToken cancellationToken = default)
Expand All @@ -37,7 +36,7 @@ public async Task HandleNewRealEstatesAdPostsAsync(IList<RealEstateAdPost> adPos

logger?.LogDebug("Received '{PostsCount}' new Real Estate Ad Posts.", adPosts.Count);

await SendEmailAsync("🆕 New Real Estate Adverts published!", CreateHtmlBody(adPosts, CommonHtmlTemplateElements.TitleNewPosts), cancellationToken).ConfigureAwait(false);
await SendEmailAsync("🆕 New Real Estate Adverts published!", CreateFullHtmlPage(adPosts, CommonHtmlTemplateElements.TitleNewPosts), cancellationToken).ConfigureAwait(false);
}

public async Task HandleInitialRealEstateAdPostsAsync(IList<RealEstateAdPost> adPosts, CancellationToken cancellationToken = default)
Expand All @@ -52,7 +51,7 @@ public async Task HandleInitialRealEstateAdPostsAsync(IList<RealEstateAdPost> ad

logger?.LogDebug("Received initial {PostsCount} Real Estate Ad Posts.", adPosts.Count);

await SendEmailAsync("🏦 Current Real Estate Adverts offering", CreateHtmlBody(adPosts, CommonHtmlTemplateElements.TitleInitialPosts), cancellationToken).ConfigureAwait(false);
await SendEmailAsync("🏦 Current Real Estate Adverts offering", CreateFullHtmlPage(adPosts, CommonHtmlTemplateElements.TitleInitialPosts), cancellationToken).ConfigureAwait(false);
}

private async Task SendEmailAsync(string subject, string body, CancellationToken cancellationToken = default)
Expand Down Expand Up @@ -94,98 +93,4 @@ await client.AuthenticateAsync(new NetworkCredential(_settings.Username,
throw new RealEstateAdPostsHandlerException($"Error during sending email notification: {ex.Message}", ex);
}
}

private static string CreateHtmlBody(RealEstateAdPost adPost, string titleHtmlElement)
{
return CommonHtmlTemplateElements.FullPage.Replace("<maintitle/>", titleHtmlElement)
.Replace("<posts/>", CreateSingleHtmlPost(adPost));
}

private static string CreateHtmlBody(IEnumerable<RealEstateAdPost> adPosts, string titleHtmlElement)
{
return CommonHtmlTemplateElements.FullPage.Replace("<maintitle/>", titleHtmlElement)
.Replace("<posts/>", string.Join(Environment.NewLine, adPosts.Select(CreateSingleHtmlPost)));
}

private static string CreateSingleHtmlPost(RealEstateAdPost post)
{
var postHtmlBuilder = new StringBuilder(CommonHtmlTemplateElements.Post)
.Replace("{$title}", post.Title)
.Replace("{$portal-name}", post.AdsPortalName)
.Replace("{$post-link}", post.WebUrl.AbsoluteUri)
.Replace("{$address}", post.Address);

// address links
if (!string.IsNullOrEmpty(post.Address))
{
postHtmlBuilder = postHtmlBuilder.Replace("{$address-links-display}", "inline-block")
.Replace("{$address-encoded}", HttpUtility.UrlEncode(post.Address));
}
else
{
postHtmlBuilder = postHtmlBuilder.Replace("{$address-links-display}", "none");
}

// layout
postHtmlBuilder = postHtmlBuilder.Replace("{$layout}", post.Layout is not Layout.NotSpecified ? post.Layout.ToDisplayString() : "-");

// floor area
postHtmlBuilder = post.FloorArea is not null and not decimal.Zero ? postHtmlBuilder.Replace("{$floor-area}", post.FloorArea + " m²") : postHtmlBuilder.Replace("{$floor-area}", "-");

// image
if (post.ImageUrl is not null)
{
postHtmlBuilder = postHtmlBuilder.Replace("{$img-link}", post.ImageUrl.AbsoluteUri)
.Replace("{$img-display}", "block");
}
else
{
postHtmlBuilder = postHtmlBuilder.Replace("{$img-link}", string.Empty)
.Replace("{$img-display}", "none");
}

// price
if (post.Price is not decimal.Zero)
{
postHtmlBuilder = postHtmlBuilder.Replace("{$price}", post.Price.ToString("N", new NumberFormatInfo { NumberGroupSeparator = " " }))
.Replace("{$currency}", post.Currency.ToString())
.Replace("{$price-display}", "block")
.Replace("{$price-comment-display}", "none")
.Replace("{$price-comment}", string.Empty);
}
else
{
postHtmlBuilder = postHtmlBuilder.Replace("{$price-comment}", post.PriceComment ?? "-")
.Replace("{$price-comment-display}", "block")
.Replace("{$price-display}", "none")
.Replace("{$price}", string.Empty)
.Replace("{$currency}", string.Empty);
}

// additional fees
if (post.AdditionalFees is not null and not decimal.Zero)
{
postHtmlBuilder = postHtmlBuilder.Replace("{$additional-fees}", post.AdditionalFees.Value.ToString("N", new NumberFormatInfo { NumberGroupSeparator = " " }))
.Replace("{$additional-fees-display}", "inline-block");
}
else
{
postHtmlBuilder = postHtmlBuilder.Replace("{$additional-fees}", decimal.Zero.ToString("N"))
.Replace("{$additional-fees-display}", "none");
}

// text
if (!string.IsNullOrEmpty(post.Text))
{
postHtmlBuilder = postHtmlBuilder.Replace("{$text}", post.Text)
.Replace("{$text-display}", "table");
}
else
{
postHtmlBuilder = postHtmlBuilder.Replace("{$text}", string.Empty)
.Replace("{$text-display}", "none");
}

return postHtmlBuilder.ToString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\RealEstatesWatcher.AdPostsHandlers.Templates\RealEstatesWatcher.AdPostsHandlers.Templates.csproj" />
<ProjectReference Include="..\RealEstatesWatcher.AdPostsHandlers.Base.Html\RealEstatesWatcher.AdPostsHandlers.Base.Html.csproj" />
<ProjectReference Include="..\RealEstatesWatcher.AdPostsHandlers.Contracts\RealEstatesWatcher.AdPostsHandlers.Contracts.csproj" />
</ItemGroup>

Expand Down
Loading
Loading