Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Add clickable metadata links when using the Telegram connector. (IMDb / Trakt / TVDb, TVMaze) #6613

Draft
wants to merge 25 commits into
base: develop
Choose a base branch
from

Conversation

qTipTip
Copy link

@qTipTip qTipTip commented Mar 9, 2024

Description

Adds a checkbox and a dropdown allowing you to select whether to add a clickable link to your Telegram notification, and which database you want a link to.

Questions:

  1. Which events should this be added to. Currently only implemented for series added and deleted.
  2. Should it be added for episode-specific events, like episode grabbed?
  3. Should we only use the dropdown and have a None or Disabled-option instead? Probably easier to reason about for the user.

I'd appreciate some input on wording. The setting-labels are not very enlightening at the moment.

Screenshots for UI Changes

Screenshot 2024-03-09 135542
Screenshot 2024-03-09 135512
Screenshot 2024-03-09 001358

Issues Fixed or Closed by this PR

@@ -1,6 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net6.0</TargetFrameworks>
<Platforms>AnyCPU;x86</Platforms>
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll drop the changes in the .csproj-files. Not sure how I managed to commit them.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please do, the Sonarr.sln one as well.

private readonly ITelegramProxy _proxy;
private readonly Dictionary<MetadataLinkType, Func<string, string, string>> _formatLinkFromIdMethods;

private string FormatImdbLinkFromId(string message, string id)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be encapsulated in a utility class somehow? Might be useful for other notication providers?

Copy link
Member

@markus101 markus101 Mar 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potentially, but it depends a lot on the format something else might need, so for now I wouldn't worry about it.

This whole group of methods can be cleaned up and combined with FormatMessageWithLink

Something like to cut down on the functions and only add links when they will be correctly formatted:

private string FormatMessageWithLink(string message, Series series)
{
	var linkType = (MetadataLinkType)Settings.MetadataLinkType;

	if (MetadataLinkType.None)
	{
		return message;
	}

	if (MetadataLinkType.Imdbb && series.ImdbId.IsNotNullOrWhiteSpace())
	{
		return $"[{message}](https://www.imdb.com/title/{series.ImdbId})";
	}

	...

	return message;
}

No need to throw if the ID is unavailable or the enum value is unexpected since we don't want the notification to be skipped, it'll just be missing that info.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also move these private functions to the bottom of the class.

@qTipTip qTipTip changed the title Add clickable metadata links when using the Telegram connector. (IMDb / Trakt / TVDb, TVMaze) [Feature] Add clickable metadata links when using the Telegram connector. (IMDb / Trakt / TVDb, TVMaze) Mar 9, 2024
Copy link
Member

@stevietv stevietv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice first contribution :)
I was wondering why the choice for 4 different methods for formatting the string? Couldnt it be done in a single method with a case switch if you pass in the MedatdataLinkType?

How have you handled cases where there isn't an imdb, trakt or tvmaze Id associated with the show, but user has selected on?

@qTipTip
Copy link
Author

qTipTip commented Mar 9, 2024

nice first contribution :) I was wondering why the choice for 4 different methods for formatting the string? Couldnt it be done in a single method with a case switch if you pass in the MedatdataLinkType?

How have you handled cases where there isn't an imdb, trakt or tvmaze Id associated with the show, but user has selected on?

Thanks for the input!

I'll try to create a switch.

And no, I haven't considered that fact!
Would that materialize as a null-value for the ID? If so I can make adding the link a no-op in that case.

Copy link
Member

@markus101 markus101 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for contributing! Left quite a few comments which should help clean things up a lot and reduce the number of changes to make it easier to follow.

@@ -1,6 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net6.0</TargetFrameworks>
<Platforms>AnyCPU;x86</Platforms>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please do, the Sonarr.sln one as well.

private readonly ITelegramProxy _proxy;
private readonly Dictionary<MetadataLinkType, Func<string, string, string>> _formatLinkFromIdMethods;

private string FormatImdbLinkFromId(string message, string id)
Copy link
Member

@markus101 markus101 Mar 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potentially, but it depends a lot on the format something else might need, so for now I wouldn't worry about it.

This whole group of methods can be cleaned up and combined with FormatMessageWithLink

Something like to cut down on the functions and only add links when they will be correctly formatted:

private string FormatMessageWithLink(string message, Series series)
{
	var linkType = (MetadataLinkType)Settings.MetadataLinkType;

	if (MetadataLinkType.None)
	{
		return message;
	}

	if (MetadataLinkType.Imdbb && series.ImdbId.IsNotNullOrWhiteSpace())
	{
		return $"[{message}](https://www.imdb.com/title/{series.ImdbId})";
	}

	...

	return message;
}

No need to throw if the ID is unavailable or the enum value is unexpected since we don't want the notification to be skipped, it'll just be missing that info.

private readonly ITelegramProxy _proxy;
private readonly Dictionary<MetadataLinkType, Func<string, string, string>> _formatLinkFromIdMethods;

private string FormatImdbLinkFromId(string message, string id)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also move these private functions to the bottom of the class.


private string FormatMessageWithLink(string message, Series series)
{
if (Settings.SendMetadataLink && _formatLinkFromIdMethods.TryGetValue(Settings.MetadataLinkType, out var formatMethod))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Settings should validate that the stored MetadataLinkType is valid.

@markus101
Copy link
Member

Would that materialize as a null-value for the ID? If so I can make adding the link a no-op in that case.

We'll have a TVDB ID 100% of the time, IMDB should be null (off chance of an empty string) and everything else will be 0 if not set.


var requestBuilder = new HttpRequestBuilder(URL).Resource("bot{token}/sendmessage").Post();

var request = requestBuilder.SetSegment("token", settings.BotToken)
.AddFormParameter("chat_id", settings.ChatId)
.AddFormParameter("parse_mode", "HTML")
.AddFormParameter("parse_mode", "MarkdownV2")
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed the parser to MarkdownV2 to have the link render properly, as I think the HTML hrefs are being escaped in HTML-mode when just added to the message. However, this breaks the SeriesDeleteMessage as it contains a reserved character -.

Not quite sure how to handle this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which part of the message? A dash could also be in the series. We could escape the -, possibly with just \-, but what about * and other special characters in markdown? _ could cause issues, especially if there are two in the title.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still need to determine the impact of this

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, apologies for the lack of activity here.

I'll try to mess around with reverting back to HTML.
Been unable to find a clean solution.

The problem I had initially with the HTML parser was that the markup would render as raw strings in the message, instead of formatting the message.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is because HttpUtility.HtmlEncode is being called on the body text. The message body with the link can't be passed into SendNotification, the link needs to be passed in separately and then built in the proxy, which is better because it hides that implementation detail from Telegram.cs

public enum MetadataLinkType
{
None = 0,
[FieldOption(Label = "IMDb")]
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I add FieldOption to None and Trakt as well, where the Enum key aligns with the Label?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think leaving them off as fine as long as the UI is rendering them correctly.

@qTipTip
Copy link
Author

qTipTip commented Mar 11, 2024

Thanks for contributing! Left quite a few comments which should help clean things up a lot and reduce the number of changes to make it easier to follow.

Thanks for the thorough review @markus101 ! I've cleaned up stuff, and it's a lot more readable now, so thanks for that! 🔥

How can I have None set as the default option in TelegramSettings.cs? Does that happen just by virtue of the = 0 in the enum?

Copy link
Member

@markus101 markus101 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work on the clean up, much easier to review now, left some more comments and questions.

src/NzbDrone.Core/Notifications/Telegram/Telegram.cs Outdated Show resolved Hide resolved
@@ -35,13 +35,13 @@ public TelegramProxy(IHttpClient httpClient, ILocalizationService localizationSe
public void SendNotification(string title, string message, TelegramSettings settings)
{
// Format text to add the title before and bold using markdown
var text = $"<b>{HttpUtility.HtmlEncode(title)}</b>\n{HttpUtility.HtmlEncode(message)}";
var text = $"*{HttpUtility.HtmlEncode(title)}*\n{HttpUtility.HtmlEncode(message)}";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this isn't sending HTML, do we still need to encode it for HTML?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should send HTML still, this works when the link is passed in:

var text = new StringBuilder($"<b>{HttpUtility.HtmlEncode(title)}</b>\n");

if (link.IsNullOrWhiteSpace())
{
    text.Append($"{{HttpUtility.HtmlEncode(message)}}");
}
else
{
    text.Append($"<a href=\"{link}\">{HttpUtility.HtmlEncode(message)}</a>");
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For test notifications something like the following inside Test:

var link = settings.MetadataLink == (int)MetadataLinkType.None ? null : "https://sonarr.tv";

SendNotification(settings.IncludeAppNameInTitle ? brandedTitle : title, body, link, settings);

This would allow people to see that the link works.

@@ -32,9 +39,27 @@ public class TelegramSettings : IProviderConfig
[FieldDefinition(3, Label = "NotificationsTelegramSettingsSendSilently", Type = FieldType.Checkbox, HelpText = "NotificationsTelegramSettingsSendSilentlyHelpText")]
public bool SendSilently { get; set; }

public bool SendMetadataLink { get; set; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No longer needed

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still needs to be removed.

public enum MetadataLinkType
{
None = 0,
[FieldOption(Label = "IMDb")]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think leaving them off as fine as long as the UI is rendering them correctly.


var requestBuilder = new HttpRequestBuilder(URL).Resource("bot{token}/sendmessage").Post();

var request = requestBuilder.SetSegment("token", settings.BotToken)
.AddFormParameter("chat_id", settings.ChatId)
.AddFormParameter("parse_mode", "HTML")
.AddFormParameter("parse_mode", "MarkdownV2")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which part of the message? A dash could also be in the series. We could escape the -, possibly with just \-, but what about * and other special characters in markdown? _ could cause issues, especially if there are two in the title.

src/NzbDrone.Core/Notifications/Telegram/Telegram.cs Outdated Show resolved Hide resolved
src/NzbDrone.Core/Notifications/Telegram/Telegram.cs Outdated Show resolved Hide resolved
qTipTip and others added 3 commits March 21, 2024 16:34
Co-authored-by: Mark McDowall <markus.mcd5@gmail.com>
Co-authored-by: Mark McDowall <markus.mcd5@gmail.com>
Co-authored-by: Mark McDowall <markus.mcd5@gmail.com>
Copy link
Member

@markus101 markus101 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tested this locally and added some additional comments and suggestions on how to make this functional.

I do wonder if a slightly different approach would be better, where individual links are added for each selected Metadata type, instead of turning the body of the message into a single link.

Let me know what you think about that and whether you'll be able to pick up the rest of the fixes here.

Comment on lines +1410 to +1411
"NotificationsTelegramSettingsSendMetadataLink": "Add a link to the series metadata",
"NotificationsTelegramSettingsSendMetadataLinkHelpText": "Add a link to the series metadata when sending the notifications",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"NotificationsTelegramSettingsSendMetadataLink": "Add a link to the series metadata",
"NotificationsTelegramSettingsSendMetadataLinkHelpText": "Add a link to the series metadata when sending the notifications",
"NotificationsTelegramSettingsMetadataLink": "MetadataLink",
"NotificationsTelegramSettingsMetadataLinkHelpText": "Add a link to the series metadata when sending notifications",

@@ -33,12 +34,16 @@ public override void OnEpisodeFileDelete(EpisodeDeleteMessage deleteMessage)

public override void OnSeriesAdd(SeriesAddMessage message)
{
_proxy.SendNotification(SERIES_ADDED_TITLE, message.Message, Settings);
var text = FormatMessageWithLink(message.Message, message.Series);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These links should also be included for OnGrab, OnDownload and OnEpisodeFileDelete


var requestBuilder = new HttpRequestBuilder(URL).Resource("bot{token}/sendmessage").Post();

var request = requestBuilder.SetSegment("token", settings.BotToken)
.AddFormParameter("chat_id", settings.ChatId)
.AddFormParameter("parse_mode", "HTML")
.AddFormParameter("parse_mode", "MarkdownV2")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is because HttpUtility.HtmlEncode is being called on the body text. The message body with the link can't be passed into SendNotification, the link needs to be passed in separately and then built in the proxy, which is better because it hides that implementation detail from Telegram.cs

@@ -32,9 +39,27 @@ public class TelegramSettings : IProviderConfig
[FieldDefinition(3, Label = "NotificationsTelegramSettingsSendSilently", Type = FieldType.Checkbox, HelpText = "NotificationsTelegramSettingsSendSilentlyHelpText")]
public bool SendSilently { get; set; }

public bool SendMetadataLink { get; set; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still needs to be removed.

@@ -32,9 +39,27 @@ public class TelegramSettings : IProviderConfig
[FieldDefinition(3, Label = "NotificationsTelegramSettingsSendSilently", Type = FieldType.Checkbox, HelpText = "NotificationsTelegramSettingsSendSilentlyHelpText")]
public bool SendSilently { get; set; }

public bool SendMetadataLink { get; set; }
[FieldDefinition(4, Label = "NotificationsTelegramSettingsMetadataLinkType", Type = FieldType.Select, SelectOptions = typeof(MetadataLinkType), HelpText = "NotificationsTelegramSettingsMetadataLinkType")]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
[FieldDefinition(4, Label = "NotificationsTelegramSettingsMetadataLinkType", Type = FieldType.Select, SelectOptions = typeof(MetadataLinkType), HelpText = "NotificationsTelegramSettingsMetadataLinkType")]
[FieldDefinition(4, Label = "NotificationsTelegramSettingsMetadataLink", Type = FieldType.Select, SelectOptions = typeof(MetadataLinkType), HelpText = "NotificationsTelegramSettingsMetadataLink")]

@@ -32,9 +39,27 @@ public class TelegramSettings : IProviderConfig
[FieldDefinition(3, Label = "NotificationsTelegramSettingsSendSilently", Type = FieldType.Checkbox, HelpText = "NotificationsTelegramSettingsSendSilentlyHelpText")]
public bool SendSilently { get; set; }

public bool SendMetadataLink { get; set; }
[FieldDefinition(4, Label = "NotificationsTelegramSettingsMetadataLinkType", Type = FieldType.Select, SelectOptions = typeof(MetadataLinkType), HelpText = "NotificationsTelegramSettingsMetadataLinkType")]
public MetadataLinkType MetadataLinkType { get; set; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public MetadataLinkType MetadataLinkType { get; set; }
public int MetadataLink { get; set; }

@@ -69,5 +74,37 @@ public override ValidationResult Test()

return new ValidationResult(failures);
}

private string FormatMessageWithLink(string message, Series series)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be renamed to GetLink and just return the link, which gets passed to SendNotification and the message is formatted there.

@@ -35,13 +35,13 @@ public TelegramProxy(IHttpClient httpClient, ILocalizationService localizationSe
public void SendNotification(string title, string message, TelegramSettings settings)
{
// Format text to add the title before and bold using markdown
var text = $"<b>{HttpUtility.HtmlEncode(title)}</b>\n{HttpUtility.HtmlEncode(message)}";
var text = $"*{HttpUtility.HtmlEncode(title)}*\n{HttpUtility.HtmlEncode(message)}";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should send HTML still, this works when the link is passed in:

var text = new StringBuilder($"<b>{HttpUtility.HtmlEncode(title)}</b>\n");

if (link.IsNullOrWhiteSpace())
{
    text.Append($"{{HttpUtility.HtmlEncode(message)}}");
}
else
{
    text.Append($"<a href=\"{link}\">{HttpUtility.HtmlEncode(message)}</a>");
}

@@ -35,13 +35,13 @@ public TelegramProxy(IHttpClient httpClient, ILocalizationService localizationSe
public void SendNotification(string title, string message, TelegramSettings settings)
{
// Format text to add the title before and bold using markdown
var text = $"<b>{HttpUtility.HtmlEncode(title)}</b>\n{HttpUtility.HtmlEncode(message)}";
var text = $"*{HttpUtility.HtmlEncode(title)}*\n{HttpUtility.HtmlEncode(message)}";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For test notifications something like the following inside Test:

var link = settings.MetadataLink == (int)MetadataLinkType.None ? null : "https://sonarr.tv";

SendNotification(settings.IncludeAppNameInTitle ? brandedTitle : title, body, link, settings);

This would allow people to see that the link works.

@github-actions github-actions bot added the merge-conflict PR has merge conflicts that need to be resolved before it can be merged label Apr 16, 2024
@qTipTip
Copy link
Author

qTipTip commented Apr 29, 2024

I've tested this locally and added some additional comments and suggestions on how to make this functional.

I do wonder if a slightly different approach would be better, where individual links are added for each selected Metadata type, instead of turning the body of the message into a single link.

Let me know what you think about that and whether you'll be able to pick up the rest of the fixes here.

Hey!

I'll have a look at this sometime this week!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
connection merge-conflict PR has merge conflicts that need to be resolved before it can be merged
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add IMDB Link to Telegram Notifications
3 participants