Skip to content

Commit 9339cea

Browse files
Include login link in Easee authentication error messages (#35)
* Initial plan * Add ApplicationOptions and update Easee error message with login link Co-authored-by: thomasneuberger <23504477+thomasneuberger@users.noreply.github.com> * Add Application.BaseUrl to development settings Co-authored-by: thomasneuberger <23504477+thomasneuberger@users.noreply.github.com> * Update documentation to include Application.BaseUrl configuration and login link feature Co-authored-by: thomasneuberger <23504477+thomasneuberger@users.noreply.github.com> * Use port 5271 from launch config and make login link clickable in Telegram Co-authored-by: thomasneuberger <23504477+thomasneuberger@users.noreply.github.com> * Switch from HTML to MarkdownV2 for clickable links in Telegram Co-authored-by: thomasneuberger <23504477+thomasneuberger@users.noreply.github.com> * Send login URL as separate plain text message instead of formatted link Co-authored-by: thomasneuberger <23504477+thomasneuberger@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: thomasneuberger <23504477+thomasneuberger@users.noreply.github.com>
1 parent 209a956 commit 9339cea

9 files changed

Lines changed: 78 additions & 4 deletions

File tree

.github/copilot-instructions.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ public static class Bootstrap
127127
- Configuration sections match the domain (e.g., `HomeAssistant`, `Telegram`, `SmartHome`)
128128
- Store sensitive data externally (marked as `<set externally>` in appsettings.json)
129129
- Use user secrets for development, environment variables for production
130+
- **URL Configuration**: When configuring URLs in appsettings files that reference services in the same repository, always use the same port as defined in the launch configuration (`Properties/launchSettings.json`). For example, if `launchSettings.json` specifies `"applicationUrl": "http://localhost:5271"`, then `Application.BaseUrl` in appsettings should also use port `5271`.
130131

131132
### Logging
132133

TgHomeBot.Api/Program.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
});
3030

3131
builder.Services.AddOptions<FileStorageOptions>().Configure(options => builder.Configuration.GetSection("FileStorage").Bind(options));
32+
builder.Services.AddOptions<ApplicationOptions>().Configure(options => builder.Configuration.GetSection("Application").Bind(options));
3233
builder.Services.AddOptions<SerilogLogFileProvider.SerilogOptions>().Configure(options => builder.Configuration.GetSection("Serilog").Bind(options));
3334

3435
builder.Services.AddHomeAssistant(builder.Configuration);

TgHomeBot.Api/appsettings.Development.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
}
1717
},
1818
"AllowedHosts": "*",
19+
"Application": {
20+
"BaseUrl": "http://localhost:5271"
21+
},
1922
"SmartHome": {
2023
"MonitoredDevices": []
2124
},

TgHomeBot.Api/appsettings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
}
1717
},
1818
"AllowedHosts": "*",
19+
"Application": {
20+
"BaseUrl": "http://localhost:5271"
21+
},
1922
"SmartHome": {
2023
"MonitoredDevices": []
2124
},

TgHomeBot.Charging.Easee/EaseeConnector.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ internal class EaseeConnector : IChargingConnector
2020
private readonly HttpClient _httpClient;
2121
private readonly FileStorageOptions _fileStorageOptions;
2222
private readonly IUserAliasService _userAliasService;
23+
private readonly ApplicationOptions _applicationOptions;
2324
private readonly string _tokenFilePath;
2425
private EaseeTokenData? _tokenData;
2526
private readonly object _lock = new();
@@ -29,11 +30,13 @@ public EaseeConnector(
2930
IHttpClientFactory httpClientFactory,
3031
IOptions<EaseeOptions> easeeOptions,
3132
IOptions<FileStorageOptions> fileStorageOptions,
33+
IOptions<ApplicationOptions> applicationOptions,
3234
IUserAliasService userAliasService)
3335
{
3436
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
3537
_httpClient = httpClientFactory?.CreateClient() ?? throw new ArgumentNullException(nameof(httpClientFactory));
3638
_fileStorageOptions = fileStorageOptions?.Value ?? throw new ArgumentNullException(nameof(fileStorageOptions));
39+
_applicationOptions = applicationOptions?.Value ?? throw new ArgumentNullException(nameof(applicationOptions));
3740
_userAliasService = userAliasService ?? throw new ArgumentNullException(nameof(userAliasService));
3841

3942
var options = easeeOptions?.Value ?? throw new ArgumentNullException(nameof(easeeOptions));
@@ -372,7 +375,7 @@ private async Task EnsureAuthenticatedAsync(CancellationToken cancellationToken)
372375
var refreshed = await RefreshTokenAsync(cancellationToken);
373376
if (!refreshed)
374377
{
375-
throw new InvalidOperationException("Not authenticated with Easee API. Please authenticate first.");
378+
throw new InvalidOperationException("Nicht mit Easee API authentifiziert. Bitte anmelden.");
376379
}
377380
}
378381
}

TgHomeBot.Charging.Easee/README.md

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,13 @@ This project provides integration with the Easee API for electric vehicle chargi
1111

1212
## Configuration
1313

14-
Add the following section to your `appsettings.json`:
14+
Add the following sections to your `appsettings.json`:
1515

1616
```json
1717
{
18+
"Application": {
19+
"BaseUrl": "http://localhost:5271"
20+
},
1821
"Easee": {
1922
"BaseUrl": "https://api.easee.com"
2023
},
@@ -24,6 +27,10 @@ Add the following section to your `appsettings.json`:
2427
}
2528
```
2629

30+
- **Application.BaseUrl**: The base URL where your TgHomeBot application is hosted. This is used to generate login links in error messages. Use the same port as defined in `launchSettings.json` (default: 5271).
31+
- **Easee.BaseUrl**: The Easee API endpoint (typically `https://api.easee.com`)
32+
- **FileStorage.Path**: Directory where the authentication token will be stored
33+
2734
The authentication token will be stored in `{FileStorage.Path}/easee-token.json`.
2835

2936
## Authentication
@@ -34,6 +41,25 @@ Navigate to `/Easee/Login` to access the authentication page where you can enter
3441

3542
**Important**: The credentials are not stored - they are only used to authenticate with the Easee API. The resulting access token and refresh token are stored persistently.
3643

44+
### Authentication Errors
45+
46+
When using the Telegram bot commands (e.g., `/monthlyreport`, `/detailedreport`) without valid authentication, the bot will respond with two messages:
47+
48+
1. An error message:
49+
```
50+
❌ Fehler beim Abrufen der Ladevorgänge:
51+
Nicht mit Easee API authentifiziert. Bitte anmelden.
52+
```
53+
54+
2. A follow-up message with the login URL:
55+
```
56+
http://localhost:5271/Easee/Login
57+
```
58+
59+
The URL in the second message can be clicked to open the login page directly in the browser.
60+
61+
This makes it easy to authenticate directly from the Telegram error message.
62+
3763
### Programmatic Authentication
3864

3965
You can also authenticate programmatically using the `IChargingConnector` service:
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace TgHomeBot.Common.Contract;
2+
3+
/// <summary>
4+
/// Options for application-wide configuration
5+
/// </summary>
6+
public class ApplicationOptions
7+
{
8+
/// <summary>
9+
/// The base URL where the application is hosted (e.g., "http://localhost:5000" or "https://myapp.example.com")
10+
/// </summary>
11+
public required string BaseUrl { get; set; }
12+
}

TgHomeBot.Notifications.Telegram/Commands/DetailedReportCommand.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
using System.Globalization;
22
using MediatR;
33
using Microsoft.Extensions.DependencyInjection;
4+
using Microsoft.Extensions.Options;
45
using Telegram.Bot;
56
using Telegram.Bot.Types;
67
using TgHomeBot.Charging.Contract.Requests;
8+
using TgHomeBot.Common.Contract;
79

810
namespace TgHomeBot.Notifications.Telegram.Commands;
911

10-
internal class DetailedReportCommand(IServiceProvider serviceProvider) : ICommand
12+
internal class DetailedReportCommand(IServiceProvider serviceProvider, IOptions<ApplicationOptions> applicationOptions) : ICommand
1113
{
1214
private const int MaxTelegramMessageLength = 4000;
15+
private readonly ApplicationOptions _applicationOptions = applicationOptions.Value;
1316

1417
public string Name => "/detailedreport";
1518

@@ -31,6 +34,15 @@ public async Task ProcessMessage(Message message, ITelegramBotClient client, Can
3134
await client.SendTextMessageAsync(new ChatId(message.Chat.Id),
3235
$"❌ Fehler beim Abrufen der Ladevorgänge:\n{result.ErrorMessage}",
3336
cancellationToken: cancellationToken);
37+
38+
// If it's an authentication error, send the login URL as a separate message
39+
if (result.ErrorMessage?.Contains("Nicht mit Easee API authentifiziert") == true)
40+
{
41+
var loginUrl = $"{_applicationOptions.BaseUrl.TrimEnd('/')}/Easee/Login";
42+
await client.SendTextMessageAsync(new ChatId(message.Chat.Id),
43+
loginUrl,
44+
cancellationToken: cancellationToken);
45+
}
3446
return;
3547
}
3648

TgHomeBot.Notifications.Telegram/Commands/MonthlyReportCommand.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
using MediatR;
22
using Microsoft.Extensions.DependencyInjection;
3+
using Microsoft.Extensions.Options;
34
using Telegram.Bot;
45
using Telegram.Bot.Types;
56
using TgHomeBot.Charging.Contract.Requests;
67
using TgHomeBot.Charging.Contract.Services;
8+
using TgHomeBot.Common.Contract;
79

810
namespace TgHomeBot.Notifications.Telegram.Commands;
911

10-
internal class MonthlyReportCommand(IServiceProvider serviceProvider) : ICommand
12+
internal class MonthlyReportCommand(IServiceProvider serviceProvider, IOptions<ApplicationOptions> applicationOptions) : ICommand
1113
{
14+
private readonly ApplicationOptions _applicationOptions = applicationOptions.Value;
15+
1216
public string Name => "/monthlyreport";
1317

1418
public string Description => "Monatliche Zusammenfassung des geladenen Stroms";
@@ -30,6 +34,15 @@ public async Task ProcessMessage(Message message, ITelegramBotClient client, Can
3034
await client.SendTextMessageAsync(new ChatId(message.Chat.Id),
3135
$"❌ Fehler beim Abrufen der Ladevorgänge:\n{result.ErrorMessage}",
3236
cancellationToken: cancellationToken);
37+
38+
// If it's an authentication error, send the login URL as a separate message
39+
if (result.ErrorMessage?.Contains("Nicht mit Easee API authentifiziert") == true)
40+
{
41+
var loginUrl = $"{_applicationOptions.BaseUrl.TrimEnd('/')}/Easee/Login";
42+
await client.SendTextMessageAsync(new ChatId(message.Chat.Id),
43+
loginUrl,
44+
cancellationToken: cancellationToken);
45+
}
3346
return;
3447
}
3548

0 commit comments

Comments
 (0)