Skip to content

Commit

Permalink
Add /ramad
Browse files Browse the repository at this point in the history
  • Loading branch information
ronnygunawan committed Nov 25, 2023
1 parent dcd74c8 commit 19b38f6
Show file tree
Hide file tree
Showing 19 changed files with 236 additions and 2 deletions.
36 changes: 36 additions & 0 deletions BotNet.Services/BotCommands/Meme.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using BotNet.Services.Meme;
using Microsoft.Extensions.DependencyInjection;
using Telegram.Bot;
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
using Telegram.Bot.Types.InputFiles;

namespace BotNet.Services.BotCommands {
public static class Meme {
public static async Task HandleRamadAsync(ITelegramBotClient botClient, IServiceProvider serviceProvider, Message message, CancellationToken cancellationToken) {
if (message.Entities is { Length: 1 } entities
&& entities[0] is { Type: MessageEntityType.BotCommand, Offset: 0, Length: int commandLength }
&& message.Text![commandLength..].Trim() is string commandArgument) {
byte[] generatedImage = serviceProvider.GetRequiredService<MemeGenerator>().CaptionRamad(commandArgument);
using MemoryStream floppedImageStream = new(generatedImage);

await botClient.SendPhotoAsync(
chatId: message.Chat.Id,
photo: new InputOnlineFile(floppedImageStream, "ramad.png"),
replyToMessageId: message.MessageId,
cancellationToken: cancellationToken);
} else {
await botClient.SendTextMessageAsync(
chatId: message.Chat.Id,
text: "Untuk menyuruh Riza presentasi, silakan ketik /ramad diikuti judul presentasi.",
parseMode: ParseMode.Html,
replyToMessageId: message.MessageId,
cancellationToken: cancellationToken);
}
}
}
}
20 changes: 20 additions & 0 deletions BotNet.Services/BotNet.Services.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

<ItemGroup>
<None Remove="CopyPasta\Pasta.json" />
<None Remove="Meme\Images\ramad.jpg" />
<None Remove="Stability\generation.proto" />
<None Remove="FancyText\CharMaps\Bold.json" />
<None Remove="FancyText\CharMaps\BoldItalic.json" />
Expand All @@ -15,6 +16,15 @@
<None Remove="FancyText\CharMaps\Medieval.json" />
<None Remove="FancyText\CharMaps\Monospace.json" />
<None Remove="FancyText\CharMaps\WideText.json" />
<None Remove="Typography\Assets\Inter-Black.ttf" />
<None Remove="Typography\Assets\Inter-Bold.ttf" />
<None Remove="Typography\Assets\Inter-ExtraBold.ttf" />
<None Remove="Typography\Assets\Inter-ExtraLight.ttf" />
<None Remove="Typography\Assets\Inter-Light.ttf" />
<None Remove="Typography\Assets\Inter-Medium.ttf" />
<None Remove="Typography\Assets\Inter-Regular.ttf" />
<None Remove="Typography\Assets\Inter-SemiBold.ttf" />
<None Remove="Typography\Assets\Inter-Thin.ttf" />
<None Remove="Typography\Assets\JetBrainsMonoNL-Bold.ttf" />
<None Remove="Typography\Assets\JetBrainsMonoNL-BoldItalic.ttf" />
<None Remove="Typography\Assets\JetBrainsMonoNL-ExtraBold.ttf" />
Expand Down Expand Up @@ -58,6 +68,16 @@
<ItemGroup>
<EmbeddedResource Include="FancyText\CharMaps\**\*.*" />
<EmbeddedResource Include="CopyPasta\Pasta.json" />
<EmbeddedResource Include="Meme\Images\Ramad.jpg" />
<EmbeddedResource Include="Typography\Assets\Inter-Black.ttf" />
<EmbeddedResource Include="Typography\Assets\Inter-Bold.ttf" />
<EmbeddedResource Include="Typography\Assets\Inter-ExtraBold.ttf" />
<EmbeddedResource Include="Typography\Assets\Inter-ExtraLight.ttf" />
<EmbeddedResource Include="Typography\Assets\Inter-Light.ttf" />
<EmbeddedResource Include="Typography\Assets\Inter-Medium.ttf" />
<EmbeddedResource Include="Typography\Assets\Inter-Regular.ttf" />
<EmbeddedResource Include="Typography\Assets\Inter-SemiBold.ttf" />
<EmbeddedResource Include="Typography\Assets\Inter-Thin.ttf" />
<EmbeddedResource Include="Typography\Assets\JetBrainsMonoNL-Bold.ttf" />
<EmbeddedResource Include="Typography\Assets\JetBrainsMonoNL-BoldItalic.ttf" />
<EmbeddedResource Include="Typography\Assets\JetBrainsMonoNL-ExtraBold.ttf" />
Expand Down
Binary file added BotNet.Services/Meme/Images/Ramad.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
96 changes: 96 additions & 0 deletions BotNet.Services/Meme/MemeGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using BotNet.Services.Typography;
using SkiaSharp;

namespace BotNet.Services.Meme {
public class MemeGenerator {
private readonly BotNetFontService _botNetFontService;

public MemeGenerator(
BotNetFontService botNetFontService
) {
_botNetFontService = botNetFontService;
}

public byte[] CaptionRamad(string text) {
using Stream templateStream = typeof(MemeGenerator).Assembly.GetManifestResourceStream(Templates.RAMAD.ImageResourceName)!;
using SKBitmap template = SKBitmap.Decode(templateStream);
using SKSurface surface = SKSurface.Create(new SKImageInfo(template.Width, template.Height));
using SKCanvas canvas = surface.Canvas;

SKRect templateRect = SKRect.Create(template.Width, template.Height);
canvas.DrawBitmap(
bitmap: template,
source: templateRect,
dest: templateRect
);

canvas.Save();
canvas.RotateDegrees(1.4f);
using Stream fontStream = _botNetFontService.GetFontStyleById("Inter-Regular").OpenStream();
using SKTypeface typeface = SKTypeface.FromStream(fontStream);
using SKPaint paint = new() {
TextAlign = SKTextAlign.Left,
Color = new SKColor(0x00, 0x00, 0x00, 0xcc),
Typeface = typeface,
TextSize = 17f,
IsAntialias = true
};
float offset = 0f;
foreach (string line in WrapWords(text, 110f, paint)) {
canvas.DrawText(
text: line,
x: 120f,
y: 100f + offset,
paint: paint
);
offset += 20f; // line height
}
canvas.Restore();

using SKImage image = surface.Snapshot();
using SKData data = image.Encode(SKEncodedImageFormat.Png, 100);

using MemoryStream memoryStream = new();
data.SaveTo(memoryStream);

return memoryStream.ToArray();
}

private static List<string> WrapWords(string text, float maxWidth, SKPaint paint) {
string[] words = text.Split(' ', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
List<string> lines = new();
bool firstWord = true;
string line = "";
foreach (string word in words) {
// Do not wrap first word
if (firstWord) {
line = word;
firstWord = false;
continue;
}

// Measure how wide will it take if we append this word to the current line
string testLine = line + " " + word;
SKRect bound = new();
paint.MeasureText(testLine, ref bound);

// Wrap
if (bound.Width > maxWidth) {
lines.Add(line);
line = word;
continue;
}

// Append
line += " " + word;
}
lines.Add(line);

return lines;
}
}
}
10 changes: 10 additions & 0 deletions BotNet.Services/Meme/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using Microsoft.Extensions.DependencyInjection;

namespace BotNet.Services.Meme {
public static class ServiceCollectionExtensions {
public static IServiceCollection AddMemeGenerator(this IServiceCollection services) {
services.AddTransient<MemeGenerator>();
return services;
}
}
}
9 changes: 9 additions & 0 deletions BotNet.Services/Meme/Templates.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace BotNet.Services.Meme {
internal sealed record Template(
string ImageResourceName
);

internal static class Templates {
public static readonly Template RAMAD = new("BotNet.Services.Meme.Images.Ramad.jpg");
}
}
Binary file not shown.
Binary file added BotNet.Services/Typography/Assets/Inter-Bold.ttf
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added BotNet.Services/Typography/Assets/Inter-Thin.ttf
Binary file not shown.
39 changes: 37 additions & 2 deletions BotNet.Services/Typography/BotNetFontService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ public class BotNetFontService {
name: "JetBrainsMonoNL",
stylesSetup: EnumerateJetBrainsMonoMLStyles);

private readonly FontFamily _inter = new(
name: "Inter",
stylesSetup: EnumerateInterStyles);

private static IEnumerable<FontStyle> EnumerateJetBrainsMonoMLStyles(FontFamily fontFamily) {
Assembly resourceAssembly = Assembly.GetAssembly(typeof(BotNetFontService))!;
string resourceNamespace = "BotNet.Services.Typography.Assets";
Expand Down Expand Up @@ -43,8 +47,39 @@ FontStyle CreateFontStyle(string name, int weight, FontStyleType styleType) {
yield return CreateFontStyle("JetBrainsMonoNL-ExtraBoldItalic", 800, FontStyleType.Italic);
}

private static IEnumerable<FontStyle> EnumerateInterStyles(FontFamily fontFamily) {
Assembly resourceAssembly = Assembly.GetAssembly(typeof(BotNetFontService))!;
string resourceNamespace = "BotNet.Services.Typography.Assets";

FontStyle CreateFontStyle(string name, int weight, FontStyleType styleType) {
return new FontStyle(
id: name,
name: name,
fullName: name,
weight: weight,
styleType: styleType,
fontFamily: fontFamily,
resourceAssembly: resourceAssembly,
resourceName: $"{resourceNamespace}.{name}.ttf");
}

yield return CreateFontStyle("Inter-Thin", 100, FontStyleType.Normal);
yield return CreateFontStyle("Inter-ExtraLight", 200, FontStyleType.Normal);
yield return CreateFontStyle("Inter-Light", 300, FontStyleType.Normal);
yield return CreateFontStyle("Inter-Regular", 400, FontStyleType.Normal);
yield return CreateFontStyle("Inter-Medium", 500, FontStyleType.Normal);
yield return CreateFontStyle("Inter-SemiBold", 600, FontStyleType.Normal);
yield return CreateFontStyle("Inter-Bold", 700, FontStyleType.Normal);
yield return CreateFontStyle("Inter-ExtraBold", 800, FontStyleType.Normal);
yield return CreateFontStyle("Inter-Black", 900, FontStyleType.Normal);
}

public FontStyle GetDefaultFontStyle() => _jetbrainsMonoNL.GetFontStyles().Single(style => style is { Weight: 400, StyleType: FontStyleType.Normal });
public FontFamily[] GetFontFamilies() => new[] { _jetbrainsMonoNL };
public FontStyle GetFontStyleById(string id) => _jetbrainsMonoNL.GetFontStyles().Single(style => style.Id == id);
public FontFamily[] GetFontFamilies() => new[] { _jetbrainsMonoNL, _inter };

public FontStyle GetFontStyleById(string id)
=> _jetbrainsMonoNL.GetFontStyles().SingleOrDefault(style => style.Id == id)
?? _inter.GetFontStyles().SingleOrDefault(style => style.Id == id)
?? throw new KeyNotFoundException();
}
}
3 changes: 3 additions & 0 deletions BotNet/Bot/UpdateHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,9 @@ await botClient.SendTextMessageAsync(
case "/preview":
await Preview.GetPreviewAsync(botClient, _serviceProvider, update.Message, cancellationToken);
break;
case "/ramad":
await Meme.HandleRamadAsync(botClient, _serviceProvider, update.Message, cancellationToken);
break;
}
}
break;
Expand Down
23 changes: 23 additions & 0 deletions BotNet/Controllers/MemeGeneratorTestController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#if DEBUG
using BotNet.Services.Meme;
using Microsoft.AspNetCore.Mvc;

namespace BotNet.Controllers {
[Route("meme")]
public class MemeGeneratorTestController : Controller {
private readonly MemeGenerator _memeGenerator;

public MemeGeneratorTestController(
MemeGenerator memeGenerator
) {
_memeGenerator = memeGenerator;
}

[Route("ramad")]
public IActionResult RenderRamad() {
byte[] memePng = _memeGenerator.CaptionRamad("Melakukan TDD, meski situasi sulit");
return File(memePng, "image/png", true);
}
}
}
#endif
2 changes: 2 additions & 0 deletions BotNet/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Orleans.Hosting;
using BotNet.Services.Meme;

Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup<Startup>())
Expand Down Expand Up @@ -73,6 +74,7 @@
services.AddWeatherService();
services.AddBMKG();
services.AddPreviewServices();
services.AddMemeGenerator();

// Hosted Services
services.Configure<BotOptions>(configuration.GetSection("BotOptions"));
Expand Down

0 comments on commit 19b38f6

Please sign in to comment.