Compare commits

...

12 Commits

Author SHA1 Message Date
9ab29a3a15 Update some packages 2025-04-24 19:07:52 +03:30
6c5fe1bc00 Added cors 2025-04-24 19:07:43 +03:30
8f9396c791 Minor UX improvement to web UI 2025-03-10 15:58:48 +03:30
83023a31a7 Minor improvements to broken images handling in web UI 2025-03-10 15:43:13 +03:30
a1aab1ac1f Change webUI font 2025-03-04 15:00:00 +03:30
bb950b97a7 Update .dockerignore
Some checks failed
release / Build and push Docker image (push) Has been cancelled
2025-02-26 14:00:26 +03:30
4ce1b29d86 Update some packages - Add some personal notes 2025-02-26 13:59:41 +03:30
b343438f9e Performance improvements on finding a random post for API and bot '/start' command
Some checks failed
release / Build and push Docker image (push) Has been cancelled
2025-02-26 13:41:43 +03:30
39fa7a8219 Update Telegram.Bot package - Update to chiseled image for docker base image
All checks were successful
release / Build and push Docker image (push) Successful in 2m38s
2025-02-08 19:05:31 +03:30
7f26e080a4 Minor improvements to web UI
Some checks failed
release / Build and push Docker image (push) Failing after 1m2s
2025-02-08 00:16:22 +03:30
787a9dd09b Performance improvements
All checks were successful
release / Build and push Docker image (push) Successful in 1m11s
2025-01-04 23:06:13 +03:30
59410c1458 Removed binary build from gitlab ci 2025-01-04 23:05:50 +03:30
10 changed files with 92 additions and 50 deletions

View File

@@ -22,4 +22,5 @@
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md
README.md
PersonalNotes

View File

@@ -36,24 +36,24 @@ docker-build:
- Dockerfile
stages:
- build
variables:
DOTNET_CLI_TELEMETRY_OPTOUT: "1"
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: "1"
before_script:
- apt-get update && apt-get install -y tar
bin-build:
stage: build
image: mcr.microsoft.com/dotnet/sdk:9.0-bookworm-slim
script:
- dotnet nuget add source https://pkgs.dev.azure.com/tgbots/Telegram.Bot/_packaging/release/nuget/v3/index.json -n Telegram.Bot
- dotnet restore --no-cache
- dotnet publish -c Release -r linux-x64 --self-contained true /p:PublishSingleFile=true /p:PublishTrimmed=false /p:EnableCompressionInSingleFile=true
- tar -czvf publish.tar.gz -C bin/Release/net9.0/linux-x64/publish/ .
artifacts:
paths:
- publish.tar.gz
#stages:
# - build
#
#variables:
# DOTNET_CLI_TELEMETRY_OPTOUT: "1"
# DOTNET_SKIP_FIRST_TIME_EXPERIENCE: "1"
#
#before_script:
# - apt-get update && apt-get install -y tar
#
#bin-build:
# stage: build
# image: mcr.microsoft.com/dotnet/sdk:9.0-bookworm-slim
# script:
# - dotnet nuget add source https://pkgs.dev.azure.com/tgbots/Telegram.Bot/_packaging/release/nuget/v3/index.json -n Telegram.Bot
# - dotnet restore --no-cache
# - dotnet publish -c Release -r linux-x64 --self-contained true /p:PublishSingleFile=true /p:PublishTrimmed=false /p:EnableCompressionInSingleFile=true
# - tar -czvf publish.tar.gz -C bin/Release/net9.0/linux-x64/publish/ .
# artifacts:
# paths:
# - publish.tar.gz

View File

@@ -1,8 +1,8 @@
FROM mcr.microsoft.com/dotnet/aspnet:9.0-bookworm-slim-amd64 AS base
FROM mcr.microsoft.com/dotnet/aspnet:9.0.1-noble-chiseled-composite AS base
USER $APP_UID
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:9.0-bookworm-slim-amd64 AS build
FROM mcr.microsoft.com/dotnet/sdk:9.0.102-bookworm-slim-amd64 AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
COPY ["mstdnCats.csproj", "./"]

5
PersonalNotes/faster-query.md Executable file
View File

@@ -0,0 +1,5 @@
# How did I go from 4 second on each API request to under 1 second?
### 1. Using projection to limit fields
### 2. Using proper indexes
### 3. Using $sample along with aggregation

View File

@@ -2,6 +2,7 @@
using CatsOfMastodonBot.Models;
using CatsOfMastodonBot.Services;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using mstdnCats.Services;
@@ -41,12 +42,22 @@ public class MastodonBot
{
options.ListenAnyIP(5005); // Listen on port 5005
});
webBuilder.ConfigureServices(services =>
{
services.AddCors(options =>
{
options.AddDefaultPolicy(options =>
{
options.AllowAnyHeader()
.AllowAnyMethod()
.AllowAnyOrigin();
});
});
} );
ServerStartup.Serverstartup(db);
webBuilder.UseStartup<ServerStartup>();
})
.Build();
// Setup bot
TelegramBotClient bot;

View File

@@ -18,16 +18,25 @@ public class HandleStartMessage
(callbackQuery != null ? "Callback" : "Start command"));
// choose all media attachments that are approved
var mediaAttachmentsToSelect = await _db.AsQueryable()
.Where(post => post.MediaAttachments.Any(media => media.Approved))
.ToListAsync();
// OLD QUERY
// var mediaAttachmentsToSelect = await _db.AsQueryable()
// .Where(post => post.MediaAttachments.Any(media => media.Approved))
// .ToListAsync();
// select random approved media attachment
var selectedMediaAttachment = mediaAttachmentsToSelect[new Random().Next(mediaAttachmentsToSelect.Count)];
var filter = Builders<Post>.Filter.ElemMatch(post => post.MediaAttachments,
Builders<MediaAttachment>.Filter.Eq(media => media.Approved, true));
var projection = Builders<Post>.Projection
.Include(p => p.Url)
.Include(p => p.Account.DisplayName)
.Include(p => p.MediaAttachments);
var selectedPost = await _db.Aggregate().Match(filter).Project<Post>(projection).Sample(1).FirstOrDefaultAsync();
// send media attachment
await _bot.SendPhoto(message.Chat.Id,
selectedMediaAttachment.MediaAttachments.FirstOrDefault(m => m.Approved == true).Url,
$"Here is your cat!🐈\n" + "<a href=\"" + selectedMediaAttachment.Url + "\">" +
selectedPost.MediaAttachments.FirstOrDefault(m => m.Approved).RemoteUrl,
$"Here is your cat!🐈\n" + "<a href=\"" + selectedPost.Url + "\">" +
$"View on Mastodon " + " </a>", ParseMode.Html
, replyMarkup: new InlineKeyboardMarkup()
.AddButton(InlineKeyboardButton.WithUrl("Join channel 😺", "https://t.me/catsofmastodon"))

View File

@@ -37,6 +37,7 @@ public class ProcessPosts
$"<a href=\"" + post.Url + "\"> Mastodon </a>", ParseMode.Html
, replyMarkup: new InlineKeyboardMarkup().AddButton("Approve", $"approve-{media.MediaId}").AddButton("Reject", $"reject-{media.MediaId}"));
validPosts.Add(post);
newPosts++;
}
catch (Exception ex)
{
@@ -44,7 +45,7 @@ public class ProcessPosts
media.PreviewUrl);
}
newPosts++;
}
// Insert post

View File

@@ -1,10 +1,10 @@
using System.Diagnostics;
using System.Reflection;
using System.Text;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using MongoDB.Driver;
using MongoDB.Driver.Linq;
using mstdnCats.Models;
namespace mstdnCats.Services;
@@ -19,7 +19,7 @@ public class ServerStartup
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseRouting();
app.UseCors();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/", async context =>
@@ -47,15 +47,27 @@ public class ServerStartup
endpoints.MapGet("/api/gimme", async context =>
{
// Api endpoint
// Choose all media attachments that are approved
var mediaAttachmentsToSelect = await _db.AsQueryable()
.Where(post => post.MediaAttachments.Any(media => media.Approved))
.ToListAsync();
// Measure execution time
var stopwatch = Stopwatch.StartNew();
// Select random approved media attachment
var selectedPost = mediaAttachmentsToSelect[new Random().Next(mediaAttachmentsToSelect.Count)];
// Choose all posts media attachments that are approved
var filter = Builders<Post>.Filter.ElemMatch(post => post.MediaAttachments,
Builders<MediaAttachment>.Filter.Eq(media => media.Approved, true));
var projection = Builders<Post>.Projection
.Include(p => p.Url)
.Include(p => p.Account.DisplayName)
.Include(p => p.MediaAttachments);
var selectedPost = await _db.Aggregate().Match(filter).Project<Post>(projection).Sample(1).FirstOrDefaultAsync();
// Stop and print execution time
stopwatch.Stop();
Console.WriteLine($"Query executed in: {stopwatch.ElapsedMilliseconds} ms");
// Send as JSON
selectedPost.MediaAttachments = selectedPost.MediaAttachments
.Where(media => media.Approved)
.ToList();
await context.Response.WriteAsJsonAsync(selectedPost);
});
});

View File

@@ -3,9 +3,10 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cats of Mastodon!</title><style>
<title>Cats of Mastodon! - Mahdium</title><style>
@import url('https://fonts.googleapis.com/css2?family=Atma:wght@300;400;500;600;700&display=swap');
body {
font-family: 'Comic Neue', cursive;
font-family: "Atma", system-ui;
display: flex;
flex-direction: column;
justify-content: center;
@@ -197,7 +198,7 @@
</head>
<body>
<h2 class="mastodon-title">Cats of Mastodon!</h2>
<p class="welcome-message">Welcome to Daily Catventures! Get your daily dose of purr-fectly adorable feline fun! <span></span><br>Posts gathered across Mastodon 🤝</p>
<p class="welcome-message">Welcome to Daily Catventures! </br> Get your daily dose of purr-fectly adorable feline fun! <span></span><br>Posts gathered across Mastodon 🤝</p>
<div class="post-container">
<div class="post-content" style="display: none;">
@@ -261,23 +262,25 @@
imageUrl = data.media_attachments[0].PreviewUrl;
} else {
console.warn("No RemoteUrl or PreviewUrl found, using placeholder");
postImage.src = "placeholder-no-image.png";
postImage.src = "https://s6.uupload.ir/files/a69d5fc9e900cc51_1920_kmnr.png";
postImage.classList.remove('loading');
return;
}
postImage.onload = () => {
postImage.classList.remove('loading');
window.scrollTo(0, document.body.scrollHeight);
};
postImage.onerror = () => {
console.error("Error loading image:", imageUrl);
postImage.src = "placeholder-error.png";
postImage.src = "https://s6.uupload.ir/files/a69d5fc9e900cc51_1920_kmnr.png";
loadNewPost();
postImage.classList.remove('loading');
};
postImage.src = imageUrl;
mastodonLink.href = data.Url;
mastodonLink.href = data.url;
})
.catch(error => {
postContainer.classList.remove('loading');

View File

@@ -11,8 +11,8 @@
<ItemGroup>
<PackageReference Include="DotNetEnv" Version="3.1.1" />
<PackageReference Include="MongoDB.Driver" Version="3.1.0" />
<PackageReference Include="Telegram.Bot" Version="22.2.0" />
<PackageReference Include="MongoDB.Driver" Version="3.3.0" />
<PackageReference Include="Telegram.Bot" Version="22.4.4" />
</ItemGroup>
<ItemGroup>