feat: implement triggering text to speech on successful payment
This commit is contained in:
@@ -60,6 +60,10 @@ jobs:
|
|||||||
--restart always \
|
--restart always \
|
||||||
-d \
|
-d \
|
||||||
-p 4343:8080 \
|
-p 4343:8080 \
|
||||||
|
-e StreamerBotOptions__Host=${{ secrets.STREAMER_BOT_HOST }} \
|
||||||
|
-e StreamerBotOptions__Post=${{ secrets.STREAMER_BOT_PORT }} \
|
||||||
|
-e StreamerBotOptions__ApiKey=${{ secrets.STREAMER_BOT_API_KEY }} \
|
||||||
|
-e StreamerBotOptions__TextToSpeechActionId=${{ secrets.STREAMER_BOT_ACTION_ID }} \
|
||||||
-e StripeOptions__ApiKey=${{ secrets.STRIPE_API_KEY }} \
|
-e StripeOptions__ApiKey=${{ secrets.STRIPE_API_KEY }} \
|
||||||
-e StripeOptions__EventsWebhookSecret=${{ secrets.STRIPE_EVENTS_WEBHOOK_SECRET }} \
|
-e StripeOptions__EventsWebhookSecret=${{ secrets.STRIPE_EVENTS_WEBHOOK_SECRET }} \
|
||||||
-e ContextOptions__DatabaseFilePath=/app/data/groundsforsupport.db \
|
-e ContextOptions__DatabaseFilePath=/app/data/groundsforsupport.db \
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using GroundsForSupport.Server.Data;
|
using GroundsForSupport.Server.Data;
|
||||||
using GroundsForSupport.Server.Payments.Stripe;
|
using GroundsForSupport.Server.Payments.Stripe;
|
||||||
|
using GroundsForSupport.Server.TextToSpeech;
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
@@ -22,7 +23,8 @@ internal static class EventsEndpoint
|
|||||||
HttpContext httpContext,
|
HttpContext httpContext,
|
||||||
[FromServices] IOptions<StripeOptions> options,
|
[FromServices] IOptions<StripeOptions> options,
|
||||||
[FromServices] Context dbContext,
|
[FromServices] Context dbContext,
|
||||||
[FromServices] TimeProvider timeProvider
|
[FromServices] TimeProvider timeProvider,
|
||||||
|
[FromServices] IStreamerBotService streamerBotService
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
var json = await new StreamReader(httpContext.Request.Body).ReadToEndAsync();
|
var json = await new StreamReader(httpContext.Request.Body).ReadToEndAsync();
|
||||||
@@ -59,6 +61,12 @@ internal static class EventsEndpoint
|
|||||||
dbContext.Payments.Add(payment);
|
dbContext.Payments.Add(payment);
|
||||||
await dbContext.SaveChangesAsync();
|
await dbContext.SaveChangesAsync();
|
||||||
|
|
||||||
|
await streamerBotService.TriggerTextToSpeechAsync(
|
||||||
|
name,
|
||||||
|
paymentIntent.AmountReceived,
|
||||||
|
message
|
||||||
|
);
|
||||||
|
|
||||||
return Results.Ok();
|
return Results.Ok();
|
||||||
}
|
}
|
||||||
catch (StripeException e)
|
catch (StripeException e)
|
||||||
|
|||||||
@@ -14,10 +14,10 @@ internal sealed class StripeService(
|
|||||||
private readonly ILogger<StripeService> _logger = logger;
|
private readonly ILogger<StripeService> _logger = logger;
|
||||||
|
|
||||||
public async Task<(bool IsSuccess, Intent Intent)> CreatePaymentIntentAsync(
|
public async Task<(bool IsSuccess, Intent Intent)> CreatePaymentIntentAsync(
|
||||||
string name,
|
string name,
|
||||||
decimal amount,
|
decimal amount,
|
||||||
string? message,
|
string? message,
|
||||||
string? email,
|
string? email,
|
||||||
CancellationToken cancellationToken = default
|
CancellationToken cancellationToken = default
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using GroundsForSupport.Server.Logging;
|
|||||||
using GroundsForSupport.Server.Payments.Endpoints;
|
using GroundsForSupport.Server.Payments.Endpoints;
|
||||||
using GroundsForSupport.Server.Payments.Stripe;
|
using GroundsForSupport.Server.Payments.Stripe;
|
||||||
using GroundsForSupport.Server.RateLimiting;
|
using GroundsForSupport.Server.RateLimiting;
|
||||||
|
using GroundsForSupport.Server.TextToSpeech;
|
||||||
|
|
||||||
using Microsoft.AspNetCore.HttpOverrides;
|
using Microsoft.AspNetCore.HttpOverrides;
|
||||||
|
|
||||||
@@ -34,6 +35,9 @@ builder.Services.AddHostedService<MigrationService>();
|
|||||||
builder.Services.ConfigureOptions<StripeOptionsSetup>();
|
builder.Services.ConfigureOptions<StripeOptionsSetup>();
|
||||||
builder.Services.AddSingleton<IStripeService, StripeService>();
|
builder.Services.AddSingleton<IStripeService, StripeService>();
|
||||||
|
|
||||||
|
builder.Services.ConfigureOptions<StreamerBotOptionsSetup>();
|
||||||
|
builder.Services.AddSingleton<IStreamerBotService, StreamerBotService>();
|
||||||
|
|
||||||
builder.Services.ConfigureHttpJsonOptions(
|
builder.Services.ConfigureHttpJsonOptions(
|
||||||
static options => options.SerializerOptions.DictionaryKeyPolicy = JsonNamingPolicy.CamelCase
|
static options => options.SerializerOptions.DictionaryKeyPolicy = JsonNamingPolicy.CamelCase
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
namespace GroundsForSupport.Server.TextToSpeech;
|
||||||
|
|
||||||
|
internal interface IStreamerBotService
|
||||||
|
{
|
||||||
|
Task TriggerTextToSpeechAsync(
|
||||||
|
string name,
|
||||||
|
long amount,
|
||||||
|
string message,
|
||||||
|
CancellationToken cancellationToken = default
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
|
namespace GroundsForSupport.Server.TextToSpeech;
|
||||||
|
|
||||||
|
internal sealed record StreamerBotOptions
|
||||||
|
{
|
||||||
|
public string Host { get; init; } = string.Empty;
|
||||||
|
public int Port { get; init; }
|
||||||
|
public string ApiKey { get; init; } = string.Empty;
|
||||||
|
public string TextToSpeechActionId { get; init; } = string.Empty;
|
||||||
|
|
||||||
|
public string GetBaseUrl()
|
||||||
|
{
|
||||||
|
return $"http://{Host}:{Port}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed record StreamerBotOptionsSetup : IConfigureOptions<StreamerBotOptions>
|
||||||
|
{
|
||||||
|
private const string SectionName = nameof(StreamerBotOptions);
|
||||||
|
private readonly IConfiguration _configuration;
|
||||||
|
|
||||||
|
public StreamerBotOptionsSetup(IConfiguration configuration)
|
||||||
|
{
|
||||||
|
_configuration = configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Configure(StreamerBotOptions options)
|
||||||
|
{
|
||||||
|
_configuration.GetSection(SectionName).Bind(options);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
|
namespace GroundsForSupport.Server.TextToSpeech;
|
||||||
|
|
||||||
|
internal sealed class StreamerBotService(
|
||||||
|
IHttpClientFactory httpClientFactory,
|
||||||
|
IOptions<StreamerBotOptions> options
|
||||||
|
) : IStreamerBotService
|
||||||
|
{
|
||||||
|
private const string DoActionEndpoint = "/DoAction";
|
||||||
|
private readonly IHttpClientFactory _httpClientFactory = httpClientFactory;
|
||||||
|
private readonly StreamerBotOptions _options = options.Value;
|
||||||
|
|
||||||
|
public Task TriggerTextToSpeechAsync(string name, long amount, string message, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
var client = _httpClientFactory.CreateClient();
|
||||||
|
var url = new UriBuilder(_options.GetBaseUrl())
|
||||||
|
{
|
||||||
|
Path = DoActionEndpoint
|
||||||
|
}.Uri;
|
||||||
|
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Post, url);
|
||||||
|
|
||||||
|
var body = new
|
||||||
|
{
|
||||||
|
action = new
|
||||||
|
{
|
||||||
|
id = _options.TextToSpeechActionId
|
||||||
|
},
|
||||||
|
args = new
|
||||||
|
{
|
||||||
|
name,
|
||||||
|
amount = $"${amount / 100.0:F2}",
|
||||||
|
message,
|
||||||
|
apiKey = _options.ApiKey
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var json = JsonSerializer.Serialize(body);
|
||||||
|
request.Content = new StringContent(json, System.Text.Encoding.UTF8, "application/json");
|
||||||
|
|
||||||
|
return client.SendAsync(request, cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,5 +5,11 @@
|
|||||||
},
|
},
|
||||||
"ContextOptions": {
|
"ContextOptions": {
|
||||||
"DatabaseFilePath": "DatabaseFilePath"
|
"DatabaseFilePath": "DatabaseFilePath"
|
||||||
|
},
|
||||||
|
"StreamerBotOptions": {
|
||||||
|
"Host": "Host",
|
||||||
|
"Port": 1234,
|
||||||
|
"ApiKey": "ApiKey",
|
||||||
|
"TextToSpeechActionId": "TextToSpeechActionId"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
using Microsoft.AspNetCore.Hosting;
|
using System.Text;
|
||||||
using Microsoft.AspNetCore.Mvc.Testing;
|
using System.Text.Json;
|
||||||
|
|
||||||
using GroundsForSupport.Server.Data;
|
using GroundsForSupport.Server.Data;
|
||||||
using GroundsForSupport.Server.Payments.Stripe;
|
using GroundsForSupport.Server.Payments.Stripe;
|
||||||
|
|
||||||
using System.Text.Json;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Testing;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace GroundsForSupport.Server.Tests.Integration.Infra;
|
namespace GroundsForSupport.Server.Tests.Integration.Infra;
|
||||||
|
|
||||||
@@ -25,7 +26,7 @@ public sealed class TestApi : WebApplicationFactory<Program>
|
|||||||
|
|
||||||
var contextOptionsJson = JsonSerializer.Serialize(contextOptions);
|
var contextOptionsJson = JsonSerializer.Serialize(contextOptions);
|
||||||
var stripeOptionsJson = JsonSerializer.Serialize(stripeOptions);
|
var stripeOptionsJson = JsonSerializer.Serialize(stripeOptions);
|
||||||
|
|
||||||
var configJson = $@"{{
|
var configJson = $@"{{
|
||||||
""{nameof(ContextOptions)}"": {contextOptionsJson},
|
""{nameof(ContextOptions)}"": {contextOptionsJson},
|
||||||
""{nameof(StripeOptions)}"": {stripeOptionsJson}
|
""{nameof(StripeOptions)}"": {stripeOptionsJson}
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ public sealed class PaymentTests(TestApi api) : IClassFixture<TestApi>
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
.ReturnsAsync((true, new Intent("pi_12345_secret_67890")));
|
.ReturnsAsync((true, new Intent("pi_12345_secret_67890")));
|
||||||
|
|
||||||
var api = _api.WithWebHostBuilder(
|
var api = _api.WithWebHostBuilder(
|
||||||
b => b.ConfigureTestServices(
|
b => b.ConfigureTestServices(
|
||||||
s => s.AddSingleton(mockStripeService.Object)
|
s => s.AddSingleton(mockStripeService.Object)
|
||||||
|
|||||||
Reference in New Issue
Block a user