Skip to content

Commit

Permalink
Merge pull request #49 from xforman2/43-docker-image-for-bot
Browse files Browse the repository at this point in the history
43 docker image for bot
  • Loading branch information
mf-16 authored Sep 8, 2024
2 parents b11438d + ce24e2c commit 36be73b
Show file tree
Hide file tree
Showing 8 changed files with 255 additions and 94 deletions.
40 changes: 40 additions & 0 deletions Backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Stage 1: Build the application
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build-env
WORKDIR /App

# Copy the solution file and project files for Backend and SharedDependencies
COPY HouseScout.sln ./
COPY SharedDependencies/SharedDependencies.csproj ./SharedDependencies/
COPY Backend/Backend.csproj ./Backend/

# Restore dependencies for Backend
WORKDIR ./Backend
RUN dotnet restore
WORKDIR ..

# Copy all the files for Backend and SharedDependencies
COPY SharedDependencies/ ./SharedDependencies/
COPY Backend/ ./Backend/

# Build and publish the Backend project
WORKDIR /App/Backend
RUN dotnet publish -c Release -o out

# Stage 2: Build the runtime image
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime

# Install cron
RUN apt-get update && apt-get install -y cron

WORKDIR /App

# Copy the build output from the previous stage
COPY --from=build-env /App/Backend/out .

# Copy the crontab file into the image and give exec rights
COPY ./Backend/crontab /etc/cron.d/crontab
RUN chmod 0644 /etc/cron.d/crontab

# Apply the crontab
RUN crontab /etc/cron.d/crontab
CMD ["cron", "&&", "tail", "-f", "/var/log/cron.log"]
1 change: 1 addition & 0 deletions Backend/crontab
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*/30 * * * * cd /App && ./Backend >> /var/log/cron.log 2>&1
30 changes: 30 additions & 0 deletions DiscordBot/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Stage 1: Build the application
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build-env
WORKDIR /App

# Copy the solution file and project files for DiscordBot and SharedDependencies
COPY HouseScout.sln ./
COPY SharedDependencies/SharedDependencies.csproj ./SharedDependencies/
COPY DiscordBot/DiscordBot.csproj ./DiscordBot/

# Restore dependencies for both DiscordBot and SharedDependencies
WORKDIR ./DiscordBot
RUN dotnet restore
WORKDIR ..

# Copy all the files for DiscordBot and SharedDependencies
COPY SharedDependencies/ ./SharedDependencies/
COPY DiscordBot/ ./DiscordBot/

# Build and publish the DiscordBot project
WORKDIR /App/DiscordBot
RUN dotnet publish -c Release -o out

# Stage 2: Build the runtime image
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime
WORKDIR /App

# Copy the build output from the previous stage
COPY --from=build-env /App/DiscordBot/out .

ENTRYPOINT ["dotnet", "DiscordBot.dll"]
65 changes: 36 additions & 29 deletions DiscordBot/Filters/DataFilter.cs
Original file line number Diff line number Diff line change
@@ -1,39 +1,46 @@
using Microsoft.EntityFrameworkCore;
using SharedDependencies.Model;
using SharedDependencies.Services;

namespace DiscordBot.Filters;

public class DataFilter
namespace DiscordBot.Filters
{
private HouseScoutContext _context;

public DataFilter(HouseScoutContext context)
{
_context = context;
}

public List<Estate> SurfacePriceFilter(
int priceMin,
int priceMax,
int surfaceMin,
int surfaceMax,
bool isNewUser
)
public class DataFilter
{
var query = _context.Estates.AsQueryable();
private readonly IDbContextFactory<HouseScoutContext> _contextFactory;

// we want to process only new data if user is not new,
// and all data if user is new
if (!isNewUser)
public DataFilter(IDbContextFactory<HouseScoutContext> contextFactory)
{
query = query.Where(e => e.IsNew);
_contextFactory = contextFactory;
}
query = query.Where(e =>
e.Price >= priceMin
&& e.Price <= priceMax
&& e.Surface >= surfaceMin
&& e.Surface <= surfaceMax
);

return query.ToList();
public List<Estate> SurfacePriceFilter(
int priceMin,
int priceMax,
int surfaceMin,
int surfaceMax,
bool isNewUser
)
{
using (var context = _contextFactory.CreateDbContext())
{
var query = context.Estates.AsQueryable();

// Process only new data if the user is not new,
// and all data if the user is new
if (!isNewUser)
{
query = query.Where(e => e.IsNew);
}

query = query.Where(e =>
e.Price >= priceMin
&& e.Price <= priceMax
&& e.Surface >= surfaceMin
&& e.Surface <= surfaceMax
);

return query.ToList();
}
}
}
}
83 changes: 46 additions & 37 deletions DiscordBot/Modules/RegisterModule.cs
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
using Discord;
using Discord.Interactions;
using DiscordBot.Filters;
using Microsoft.EntityFrameworkCore;
using SharedDependencies.Model;
using SharedDependencies.Services;

namespace DiscordBot.Modules
{
public class RegisterModule : InteractionModuleBase<SocketInteractionContext>
{
private readonly DataFilter _filter;
private static int MinPrice { get; set; }
private static int MaxPrice { get; set; }
private static int MinSurface { get; set; }
private static int MaxSurface { get; set; }
private static OfferType OfferType { get; set; }
private static EstateType EstateType { get; set; }

private static HouseScoutContext _context;
private readonly IDbContextFactory<HouseScoutContext> _contextFactory;

public RegisterModule(DataFilter filter, HouseScoutContext context)
public RegisterModule(IDbContextFactory<HouseScoutContext> contextFactory)
{
_filter = filter;
_context = context;
_contextFactory = contextFactory;
}

public class EstateModal : IModal
Expand Down Expand Up @@ -52,14 +52,17 @@ public class EstateModal : IModal
[SlashCommand("register", "Register your preferences to receive notifications")]
public async Task Register()
{
var user = _context.Users.FirstOrDefault(u => (ulong)u.UserId == Context.User.Id);
if (user is null)
await using (var context = await _contextFactory.CreateDbContextAsync())
{
await RespondWithModalAsync<EstateModal>("registerModal");
}
else
{
await RespondAsync("User already registered");
var user = context.Users.FirstOrDefault(u => (ulong)u.UserId == Context.User.Id);
if (user is null)
{
await RespondWithModalAsync<EstateModal>("registerModal");
}
else
{
await RespondAsync("User already registered");
}
}
}

Expand All @@ -79,7 +82,7 @@ public async Task HandleEstateDetailsModalSubmit(EstateModal modal)

var builder = new ComponentBuilder().WithSelectMenu(estateSelectMenu);

await RespondAsync("Please select a estate type:", components: builder.Build());
await RespondAsync("Please select an estate type:", components: builder.Build());
}

[ComponentInteraction("estateType")]
Expand All @@ -95,27 +98,30 @@ public async Task HandleEstateTypeSelect(string[] selectedValues)

var builder = new ComponentBuilder().WithSelectMenu(offerSelectMenu);

await RespondAsync("Please select a offer type:", components: builder.Build());
await RespondAsync("Please select an offer type:", components: builder.Build());
}

[ComponentInteraction("offerType")]
public async Task HandleOfferTypeSelect(string[] selectedValues)
{
OfferType = selectedValues[0] == "sale" ? OfferType.SALE : OfferType.RENT;
_context.Users.Add(
new User(
//Postgres allows only this: identity column type must be smallint, integer, or bigint
(long)Context.User.Id,
MinPrice,
MaxPrice,
MinSurface,
MaxSurface,
EstateType,
OfferType,
true
)
);
await _context.SaveChangesAsync();

await using (var context = await _contextFactory.CreateDbContextAsync())
{
context.Users.Add(
new User(
(long)Context.User.Id,
MinPrice,
MaxPrice,
MinSurface,
MaxSurface,
EstateType,
OfferType,
true
)
);
await context.SaveChangesAsync();
}

await RespondAsync(
$"You have been registered with the following preferences:\n"
Expand All @@ -129,16 +135,19 @@ await RespondAsync(
[SlashCommand("unregister", "Unregister your notifications")]
public async Task Unregister()
{
var user = _context.Users.FirstOrDefault(u => (ulong)u.UserId == Context.User.Id);
if (user is null)
{
await RespondAsync("User is not registered yet");
}
else
await using (var context = await _contextFactory.CreateDbContextAsync())
{
_context.Users.Remove(user);
await _context.SaveChangesAsync();
await RespondAsync("User unregistered");
var user = context.Users.FirstOrDefault(u => (ulong)u.UserId == Context.User.Id);
if (user is null)
{
await RespondAsync("User is not registered yet");
}
else
{
context.Users.Remove(user);
await context.SaveChangesAsync();
await RespondAsync("User unregistered");
}
}
}
}
Expand Down
16 changes: 16 additions & 0 deletions DiscordBot/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,17 @@ static async Task Main(string[] args)
var rabbitMqService = host.Services.GetRequiredService<RabbitMQService>();
rabbitMqService.StartListening();

using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;

var context = services.GetRequiredService<HouseScoutContext>();
if ((await context.Database.GetPendingMigrationsAsync()).Any())
{
await context.Database.MigrateAsync();
}
}

await Task.Delay(-1); // Keep the bot running
}

Expand Down Expand Up @@ -87,6 +98,11 @@ private static IHostBuilder CreateHostBuilder() =>
configuration.GetConnectionString("DefaultConnection")
)
)
.AddDbContextFactory<HouseScoutContext>(options =>
options.UseNpgsql(
configuration.GetConnectionString("DefaultConnection")
)
)
.AddSingleton<CommandService>()
.AddScoped<DataFilter>()
.AddSingleton<RabbitMQService>()
Expand Down
Loading

0 comments on commit 36be73b

Please sign in to comment.