Add DogstatsD,
Move to inheritance for manipulators,
Add Metrics counting for endpoint
main
exsersewo 2 years ago
parent f1f27e7cd8
commit e52c1be361
Signed by: exsersewo
GPG Key ID: 9C6DDF9AC9BA14A0
  1. 62
      src/Attributes/EndpointMetrics.cs
  2. 13
      src/Controllers/AmogusController.cs
  3. 21
      src/Controllers/ImageGenerationController.cs
  4. 22
      src/Controllers/ImageManipulationController.cs
  5. 41
      src/Extensions/MagickImageExtensions.cs
  6. 210
      src/Helpers/AmongUs/AmogusHelper.cs
  7. 191
      src/Helpers/AmongUs/AmongUs.Models.cs
  8. 1
      src/Kynareth.csproj
  9. 16
      src/Managers/ImageGenerator.cs
  10. 10
      src/Managers/ImageMagiker.cs
  11. 11
      src/Managers/ImageManipulator.cs
  12. 38
      src/Managers/ImageModifier.cs
  13. 19
      src/Program.cs

@ -1,8 +1,11 @@
using System.Diagnostics;
using System.Text;
using Exsersewo.Common.Extensions; using Exsersewo.Common.Extensions;
using Kynareth.Helpers; using Kynareth.Helpers;
using Kynareth.Models; using Kynareth.Models;
using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Primitives; using Microsoft.Extensions.Primitives;
using StatsdClient;
namespace Kynareth.Attributes; namespace Kynareth.Attributes;
@ -10,30 +13,65 @@ public class EndpointMetricsAttribute : ActionFilterAttribute
{ {
public string EndpointMetricName; public string EndpointMetricName;
private Stopwatch watch = new Stopwatch();
public EndpointMetricsAttribute(string metricName) public EndpointMetricsAttribute(string metricName)
{ {
EndpointMetricName = metricName; EndpointMetricName = metricName;
} }
public override void OnActionExecuting(ActionExecutingContext context)
{
base.OnActionExecuting(context);
#if DEBUG
Debug.WriteLine("Executing");
#endif
watch.Restart();
watch.Start();
}
public override void OnActionExecuted(ActionExecutedContext context) public override void OnActionExecuted(ActionExecutedContext context)
{ {
base.OnActionExecuted(context); base.OnActionExecuted(context);
watch.Stop();
#if DEBUG
Debug.WriteLine("Executed");
#endif
string key = context.HttpContext.Request.GetToken(); string key = context.HttpContext.Request.GetToken();
if (string.IsNullOrWhiteSpace(key)) return;
using var database = context.HttpContext.RequestServices.GetRequiredService<KynarethDbContext>(); if (!string.IsNullOrWhiteSpace(key))
{
using var database = context.HttpContext.RequestServices.GetRequiredService<KynarethDbContext>();
var token = database.Tokens.FirstOrDefault(x => x.Token.Equals(key)); var token = database.Tokens.FirstOrDefault(x => x.Token.Equals(key));
if (token is null) return; if (token is not null)
{
token.ApiCalls.Add(new() { token.ApiCalls.Add(new()
TimeStamp = DateTime.UtcNow, {
Route = EndpointMetricName TimeStamp = DateTime.UtcNow,
}); Route = EndpointMetricName
});
database.SaveChanges(); database.SaveChanges();
}
}
string environment = new StringBuilder("environment:")
#if DEBUG
.Append("dev")
#else
.Append("production")
#endif
.ToString();
DogStatsd.Gauge(EndpointMetricName, watch.Elapsed.TotalSeconds, tags: new[] { environment });
DogStatsd.Increment(EndpointMetricName, tags: new[] { environment });
} }
} }

@ -4,14 +4,23 @@ using Kynareth.Attributes;
using Kynareth.Helpers; using Kynareth.Helpers;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Kynareth.Extensions; using Kynareth.Extensions;
using Kynareth.Helpers.AmongUs;
namespace Kynareth.Controllers; namespace Kynareth.Controllers;
[Route("amogus")] [Route("amogus")]
public class AmogusController : BaseController<AmogusController> public class AmogusController : BaseController<AmogusController>
{ {
public AmogusController(IConfiguration configuration, ILoggerFactory loggerFactory, IWebHostEnvironment appEnvironment) : base(configuration, loggerFactory, appEnvironment) private AmongUsGenerator _amongUs;
public AmogusController(
AmongUsGenerator amongUs,
IConfiguration configuration,
ILoggerFactory loggerFactory,
IWebHostEnvironment appEnvironment
) : base(configuration, loggerFactory, appEnvironment)
{ {
_amongUs = amongUs;
} }
[HttpGet("eject")] [HttpGet("eject")]
@ -19,7 +28,7 @@ public class AmogusController : BaseController<AmogusController>
{ {
if (string.IsNullOrWhiteSpace(name)) return StatusCode(StatusCodes.Status400BadRequest, EventResult.FromFailure(nameof(name) + " needs to be provided.")); if (string.IsNullOrWhiteSpace(name)) return StatusCode(StatusCodes.Status400BadRequest, EventResult.FromFailure(nameof(name) + " needs to be provided."));
using var result = AmogusHelper.GenerateEjectionGif(name, colour, role, skin); using var result = _amongUs.GenerateEjectionGif(name, colour, role, skin);
if (result is MagickImageCollection data) if (result is MagickImageCollection data)
{ {

@ -12,8 +12,17 @@ namespace Kynareth.Controllers;
[Route("image/generate")] [Route("image/generate")]
public class ImageGenerationController : BaseController<ImageGenerationController> public class ImageGenerationController : BaseController<ImageGenerationController>
{ {
public ImageGenerationController(IConfiguration configuration, ILoggerFactory loggerFactory, IWebHostEnvironment environment) : base(configuration, loggerFactory, environment) private ImageGenerator _generator;
{ }
public ImageGenerationController(
ImageGenerator generator,
IConfiguration configuration,
ILoggerFactory loggerFactory,
IWebHostEnvironment environment
) : base(configuration, loggerFactory, environment)
{
_generator = generator;
}
[HttpGet] [HttpGet]
[EndpointMetrics("image.generate.get.templates")] [EndpointMetrics("image.generate.get.templates")]
@ -21,7 +30,7 @@ public class ImageGenerationController : BaseController<ImageGenerationControlle
[ProducesResponseType(typeof(IEnumerable<ImageGenerationEndpointRead>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(IEnumerable<ImageGenerationEndpointRead>), StatusCodes.Status200OK)]
public Task<object> GetTemplates() public Task<object> GetTemplates()
{ {
return Task.FromResult(HttpContext.Send(EventResult.FromSuccess(ImageManager.GetGenerationEndpoints()))); return Task.FromResult(HttpContext.Send(EventResult.FromSuccess(_generator.GetGenerationEndpoints())));
} }
[HttpGet("{template}")] [HttpGet("{template}")]
@ -35,7 +44,7 @@ public class ImageGenerationController : BaseController<ImageGenerationControlle
{ {
try try
{ {
var result = await ImageManager.GenerateImageAsync(template, variant, sources, Request.HttpContext.RequestAborted); var result = await _generator.GenerateImageAsync(template, variant, sources, Request.HttpContext.RequestAborted);
if (result is byte[] data) if (result is byte[] data)
{ {
@ -68,7 +77,7 @@ public class ImageGenerationController : BaseController<ImageGenerationControlle
{ {
if (!string.IsNullOrWhiteSpace(template)) if (!string.IsNullOrWhiteSpace(template))
{ {
var result = await ImageManager.GenerateImageAsync(template, variant, Request.HttpContext.RequestAborted); var result = await _generator.GenerateImageAsync(template, variant, Request.HttpContext.RequestAborted);
if (result is byte[] data) if (result is byte[] data)
{ {
@ -100,7 +109,7 @@ public class ImageGenerationController : BaseController<ImageGenerationControlle
{ {
try try
{ {
var result = await ImageManager.GenerateImageAsync(Request.HttpContext.RequestAborted); var result = await _generator.GenerateImageAsync(Request.HttpContext.RequestAborted);
if (result is byte[] data) if (result is byte[] data)
{ {

@ -10,8 +10,20 @@ namespace Kynareth.Controllers;
[Route("image/manipulation")] [Route("image/manipulation")]
public class ImageManipulationController : BaseController<ImageManipulationController> public class ImageManipulationController : BaseController<ImageManipulationController>
{ {
public ImageManipulationController(IConfiguration configuration, ILoggerFactory loggerFactory, IWebHostEnvironment appEnvironment) : base(configuration, loggerFactory, appEnvironment) private ImageMagiker _magiker;
{ } private ImageManipulator _manipulator;
public ImageManipulationController(
ImageMagiker magiker,
ImageManipulator manipulator,
IConfiguration configuration,
ILoggerFactory loggerFactory,
IWebHostEnvironment appEnvironment
) : base(configuration, loggerFactory, appEnvironment)
{
_magiker = magiker;
_manipulator = manipulator;
}
/// ---------- /// /// ---------- ///
/// MAGIK /// /// MAGIK ///
@ -28,7 +40,7 @@ public class ImageManipulationController : BaseController<ImageManipulationContr
{ {
if (!string.IsNullOrWhiteSpace(image)) if (!string.IsNullOrWhiteSpace(image))
{ {
var (data, contentType) = await ImageManager.GetLiquidRescaledImageAsync(image, HttpContext.RequestAborted); var (data, contentType) = await _magiker.GetLiquidRescaledImageAsync(image, HttpContext.RequestAborted);
return HttpContext.SendStream(data, contentType); return HttpContext.SendStream(data, contentType);
} }
@ -54,7 +66,7 @@ public class ImageManipulationController : BaseController<ImageManipulationContr
[ProducesResponseType(typeof(IEnumerable<ImageGenerationEndpointRead>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(IEnumerable<ImageGenerationEndpointRead>), StatusCodes.Status200OK)]
public Task<object> GetTemplates() public Task<object> GetTemplates()
{ {
return Task.FromResult(HttpContext.Send(EventResult.FromSuccess(ImageManager.GetManipulationEndpoints()))); return Task.FromResult(HttpContext.Send(EventResult.FromSuccess(_manipulator.GetManipulationEndpoints())));
} }
[HttpGet("{template}")] [HttpGet("{template}")]
@ -69,7 +81,7 @@ public class ImageManipulationController : BaseController<ImageManipulationContr
{ {
if (!string.IsNullOrWhiteSpace(template)) if (!string.IsNullOrWhiteSpace(template))
{ {
var result = await ImageManager.GenerateMemeImageAsync(template, text, HttpContext.RequestAborted); var result = await _manipulator.GenerateMemeImageAsync(template, text, HttpContext.RequestAborted);
if (result is byte[] data) if (result is byte[] data)
{ {

@ -1,49 +1,12 @@
using System.Globalization;
using System.Numerics; using System.Numerics;
using System.Text; using System.Text;
using ICU4N.Text;
using ImageMagick; using ImageMagick;
using Kynareth.Models; using Kynareth.Models;
using Microsoft.AspNetCore.Mvc;
namespace Kynareth.Managers; namespace Kynareth.Extensions;
public static partial class ImageManager public static class MagickImageExtensions
{ {
public static void Configure(IConfiguration configuration, IWebHostEnvironment appEnvironment)
{
ConfigureGeneration(configuration, appEnvironment);
ConfigureManipulation(configuration, appEnvironment);
}
static BreakIterator lineBreakIterator = BreakIterator.GetLineInstance(CultureInfo.InvariantCulture);
private static ObjectResult GetResult(int statusCode, object data) => new(data) { StatusCode = statusCode };
private static string GetText(string text)
{
StringBuilder newText = new();
// set the text to be broken
lineBreakIterator.SetText(text);
// get the first boundary
int start = lineBreakIterator.First();
// iterate over all boundaries
for (int end = lineBreakIterator.Next(); end != BreakIterator.Done; start = end, end = lineBreakIterator.Next())
{
// get the current line
string line = text.Substring(start, end - start);
// do something with the line
newText.AppendLine(line);
Console.WriteLine(line);
}
return newText.ToString();
}
public static MagickImage WriteText(this MagickImage image, string text, PositionRect rect, MagickColor fontColor, Encoding encoding, string font = "Impact", int strokeWidth = 2, MagickColor? strokeColor = null, bool wrapText = true, Gravity textGravity = Gravity.Northwest) public static MagickImage WriteText(this MagickImage image, string text, PositionRect rect, MagickColor fontColor, Encoding encoding, string font = "Impact", int strokeWidth = 2, MagickColor? strokeColor = null, bool wrapText = true, Gravity textGravity = Gravity.Northwest)
{ {
using var label = new MagickImage($"{(wrapText ? "caption" : "label")}:{text}", new MagickReadSettings() using var label = new MagickImage($"{(wrapText ? "caption" : "label")}:{text}", new MagickReadSettings()

@ -1,208 +1,18 @@
using System.Drawing; using System.Drawing;
using System.Numerics;
using System.Text; using System.Text;
using ImageMagick; using ImageMagick;
using Kynareth.Managers;
using Kynareth.Extensions; using Kynareth.Extensions;
using CollectionExtensions = Kynareth.Extensions.CollectionExtensions; using CollectionExtensions = Kynareth.Extensions.CollectionExtensions;
namespace Kynareth.Helpers; namespace Kynareth.Helpers.AmongUs;
public enum AmogusRole public class AmongUsGenerator
{ {
Imposter,
Crewmate,
Unknown
}
public enum AmogusColour
{
Red,
Blue,
Green,
Pink,
Orange,
Yellow,
Black,
White,
Purple,
Brown,
Cyan,
Lime,
Maroon,
Rose,
Banana,
Gray,
Tan,
Coral,
}
public enum AmogusSkin
{
Archaeologist,
Astronaut,
Captain,
Hazmat,
Mechanic,
Military,
Miner,
Police,
SecurityGuard,
Scientist,
BlackSuit,
WhiteSuit,
Tarmac,
Wall,
Winter,
None,
}
public static class AmogusHelper
{
#region CoreVariables
public class ColourHelper
{
private static readonly Dictionary<AmogusColour, MagickColor> _RedReplaceColors = new()
{
{ AmogusColour.Red, new(215, 30, 34, 255) },
{ AmogusColour.Blue, new(29, 60, 233, 255) },
{ AmogusColour.Green, new(27, 145, 62, 255) },
{ AmogusColour.Pink, new(255, 99, 212, 255) },
{ AmogusColour.Orange, new(255, 141, 28, 255) },
{ AmogusColour.Yellow, new(255, 255, 103, 255) },
{ AmogusColour.Black, new(74, 86, 94, 255) },
{ AmogusColour.White, new(233, 247, 255, 255) },
{ AmogusColour.Purple, new(120, 61, 210, 255) },
{ AmogusColour.Brown, new(128, 88, 45, 255) },
{ AmogusColour.Cyan, new(68, 255, 247, 255) },
{ AmogusColour.Lime, new(91, 255, 75, 255) },
{ AmogusColour.Maroon, new(108, 43, 61, 255) },
{ AmogusColour.Rose, new(255, 214, 236, 255) },
{ AmogusColour.Banana, new(255, 255, 190, 255) },
{ AmogusColour.Gray, new(131, 151, 167, 255) },
{ AmogusColour.Tan, new(159, 153, 137, 255) },
{ AmogusColour.Coral, new(236, 117, 120, 255) },
};
private static readonly Dictionary<AmogusColour, MagickColor> _BlueReplaceColors = new()
{
{ AmogusColour.Red, new(122, 8, 56, 255) },
{ AmogusColour.Blue, new(9, 21, 142, 255) },
{ AmogusColour.Green, new(10, 77, 46, 255) },
{ AmogusColour.Pink, new(172, 43, 174, 255) },
{ AmogusColour.Orange, new(180, 62, 21, 255) },
{ AmogusColour.Yellow, new(195, 136, 34, 255) },
{ AmogusColour.Black, new(30, 31, 38, 255) },
{ AmogusColour.White, new(132, 149, 192, 255) },
{ AmogusColour.Purple, new(59, 23, 124, 255) },
{ AmogusColour.Brown, new(94, 38, 21, 255) },
{ AmogusColour.Cyan, new(36, 169, 191, 255) },
{ AmogusColour.Lime, new(21, 168, 66, 255) },
{ AmogusColour.Maroon, new(65, 15, 26, 255) },
{ AmogusColour.Rose, new(222, 146, 179, 255) },
{ AmogusColour.Banana, new(210, 188, 137, 255) },
{ AmogusColour.Gray, new(70, 86, 100, 255) },
{ AmogusColour.Tan, new(81, 65, 62, 255) },
{ AmogusColour.Coral, new(180, 67, 98, 255) },
};
private static readonly MagickColor _green = new MagickColor(149, 202, 220, 255);
private readonly MagickColor _red;
private readonly MagickColor _blue;
public MagickColor Red => _red;
public MagickColor Green => _green;
public MagickColor Blue => _blue;
public ColourHelper(AmogusColour colour = AmogusColour.Blue)
{
_red = _RedReplaceColors[colour];
_blue = _BlueReplaceColors[colour];
}
}
public static readonly Dictionary<AmogusSkin, string> IdleSkins = new()
{
{ AmogusSkin.Archaeologist, "archae-idle.png" },
{ AmogusSkin.Astronaut, "astro-idle.png" },
{ AmogusSkin.Captain, "captain-idle.png" },
{ AmogusSkin.Hazmat, "hazmat-idle.png" },
{ AmogusSkin.Mechanic, "mech-idle.png" },
{ AmogusSkin.Military, "military-idle.png" },
{ AmogusSkin.Miner, "miner-idle.png" },
{ AmogusSkin.Police, "pol-idle.png" },
{ AmogusSkin.SecurityGuard, "secguard-idle.png" },
{ AmogusSkin.Scientist, "sci-idle.png" },
{ AmogusSkin.BlackSuit, "suitBlack-idle.png" },
{ AmogusSkin.WhiteSuit, "suitWhite-idle.png" },
{ AmogusSkin.Tarmac, "tarmac-idle.png" },
{ AmogusSkin.Wall, "wall-idle.png" },
{ AmogusSkin.Winter, "winter-idle.png" },
};
public static readonly Dictionary<AmogusSkin, string> EjectSkins = new()
{
{ AmogusSkin.Archaeologist, "archae-eject.png" },
{ AmogusSkin.Astronaut, "astro-eject.png" },
{ AmogusSkin.Captain, "captain-eject.png" },
{ AmogusSkin.Hazmat, "hazmat-eject.png" },
{ AmogusSkin.Mechanic, "mech-eject.png" },
{ AmogusSkin.Military, "military-eject.png" },
{ AmogusSkin.Miner, "miner-eject.png" },
{ AmogusSkin.Police, "police-eject.png" },
{ AmogusSkin.SecurityGuard, "secguard-eject.png" },
{ AmogusSkin.Scientist, "sci-eject.png" },
{ AmogusSkin.BlackSuit, "suitBlack-eject.png" },
{ AmogusSkin.WhiteSuit, "suitWhite-eject.png" },
{ AmogusSkin.Tarmac, "tarmac-eject.png" },
{ AmogusSkin.Wall, "wall-eject.png" },
{ AmogusSkin.Winter, "winter-eject.png" },
};
public static readonly Dictionary<AmogusSkin, Point> IdleOffset = new()
{
{ AmogusSkin.Archaeologist, new(13, 41) },
{ AmogusSkin.Astronaut, new(13, 46) },
{ AmogusSkin.Captain, new(14, 45) },
{ AmogusSkin.Hazmat, new(12, 34) },
{ AmogusSkin.Mechanic, new(13, 46) },
{ AmogusSkin.Military, new(11, 45) },
{ AmogusSkin.Miner, new(13, 40) },
{ AmogusSkin.Police, new(10, 45) },
{ AmogusSkin.SecurityGuard, new(13, 42) },
{ AmogusSkin.Scientist, new(14, 43) },
{ AmogusSkin.BlackSuit, new(14, 44) },
{ AmogusSkin.WhiteSuit, new(14, 44) },
{ AmogusSkin.Tarmac, new(14, 40) },
{ AmogusSkin.Wall, new(10, 44) },
{ AmogusSkin.Winter, new(9, 35) },
};
public static readonly Dictionary<AmogusSkin, Point> EjectOffset = new()
{
{ AmogusSkin.Archaeologist, new(12, 35) },
{ AmogusSkin.Astronaut, new(12, 35) },
{ AmogusSkin.Captain, new(12, 35) },
{ AmogusSkin.Hazmat, new(13, 37) },
{ AmogusSkin.Mechanic, new(13, 35) },
{ AmogusSkin.Military, new(12, 35) },
{ AmogusSkin.Miner, new(12, 36) },
{ AmogusSkin.Police, new(12, 35) },
{ AmogusSkin.SecurityGuard, new(13, 35) },
{ AmogusSkin.Scientist, new(-10, 35) },
{ AmogusSkin.BlackSuit, new(12, 35) },
{ AmogusSkin.WhiteSuit, new(11, 35) },
{ AmogusSkin.Tarmac, new(14, 37) },
{ AmogusSkin.Wall, new(12, 35) },
{ AmogusSkin.Winter, new(5, 30) },
};
public static string IdleRoot, EjectRoot, CommonRoot; public static string IdleRoot, EjectRoot, CommonRoot;
public const string Arial = "Arial"; public const string Arial = "Arial";
#endregion CoreVariables
public AmongUsGenerator(IConfiguration configuration, IWebHostEnvironment appEnvironment)
public static void Configure(IWebHostEnvironment appEnvironment)
{ {
string amogusBase = Path.Combine(appEnvironment.WebRootPath, "amogus"); string amogusBase = Path.Combine(appEnvironment.WebRootPath, "amogus");
@ -221,7 +31,7 @@ public static class AmogusHelper
static MagickImage GenerateBase(string baseImageName = "idle.png", AmogusColour colour = AmogusColour.Blue) static MagickImage GenerateBase(string baseImageName = "idle.png", AmogusColour colour = AmogusColour.Blue)
{ {
string baseIdle = Path.Combine(CommonRoot, baseImageName); string baseIdle = Path.Combine(CommonRoot, baseImageName);
MagickImage body = new MagickImage(baseIdle); MagickImage body = new(baseIdle);
body = ColourReplace(body, colour); body = ColourReplace(body, colour);
body.BackgroundColor = MagickColors.Transparent; body.BackgroundColor = MagickColors.Transparent;
@ -237,7 +47,7 @@ public static class AmogusHelper
/// <returns>Colour Replaced Amogie Crewmate</returns> /// <returns>Colour Replaced Amogie Crewmate</returns>
static MagickImage ColourReplace(MagickImage image, AmogusColour colour = AmogusColour.Blue) static MagickImage ColourReplace(MagickImage image, AmogusColour colour = AmogusColour.Blue)
{ {
var colours = new ColourHelper(colour); ColourHelper colours = new(colour);
var pixels = image.GetPixels(); var pixels = image.GetPixels();
@ -382,7 +192,7 @@ public static class AmogusHelper
skinImage = new MagickImage( skinImage = new MagickImage(
Path.Combine( Path.Combine(
ejected ? EjectRoot : IdleRoot, ejected ? EjectRoot : IdleRoot,
ejected ? EjectSkins[skin] : IdleSkins[skin] ejected ? AmongUsGeneratorHelper.EjectSkins[skin] : AmongUsGeneratorHelper.IdleSkins[skin]
) )
); );
} }
@ -391,7 +201,7 @@ public static class AmogusHelper
ApplyLayer( ApplyLayer(
baseImage, baseImage,
skinImage, skinImage,
ejected ? EjectOffset[skin] : IdleOffset[skin], new Point(0, 0) ejected ? AmongUsGeneratorHelper.EjectOffset[skin] : AmongUsGeneratorHelper.IdleOffset[skin], new Point(0, 0)
) : baseImage; ) : baseImage;
outImage.Trim(); outImage.Trim();
@ -457,14 +267,14 @@ public static class AmogusHelper
/// <param name="skin"></param> /// <param name="skin"></param>
/// <param name="role"></param> /// <param name="role"></param>
/// <returns>A <see cref="MemoryStream"/> containing the Amogus Ejected Gif</returns> /// <returns>A <see cref="MemoryStream"/> containing the Amogus Ejected Gif</returns>
public static MagickImageCollection GenerateEjectionGif(string name, AmogusColour? colour = null, AmogusRole? role = null, AmogusSkin? skin = null, bool addStars = true) public MagickImageCollection GenerateEjectionGif(string name, AmogusColour? colour = null, AmogusRole? role = null, AmogusSkin? skin = null, bool addStars = true)
{ {
if (role is null) role = EnumHelper.GetRandomEnumValue<AmogusRole>(); if (role is null) role = EnumHelper.GetRandomEnumValue<AmogusRole>();
return GenerateCustomEjectionGif(GenerateEjectionMessage(name, role.Value), colour, role, skin, addStars); return GenerateCustomEjectionGif(GenerateEjectionMessage(name, role.Value), colour, role, skin, addStars);
} }
public static MagickImageCollection GenerateCustomEjectionGif(string text, AmogusColour? colour = null, AmogusRole? role = null, AmogusSkin? skin = null, bool addStars = true) public MagickImageCollection GenerateCustomEjectionGif(string text, AmogusColour? colour = null, AmogusRole? role = null, AmogusSkin? skin = null, bool addStars = true)
{ {
if (colour is null) colour = EnumHelper.GetRandomEnumValue<AmogusColour>(); if (colour is null) colour = EnumHelper.GetRandomEnumValue<AmogusColour>();
if (role is null) role = EnumHelper.GetRandomEnumValue<AmogusRole>(); if (role is null) role = EnumHelper.GetRandomEnumValue<AmogusRole>();

@ -0,0 +1,191 @@
using System.Drawing;
using ImageMagick;
namespace Kynareth.Helpers.AmongUs;
public enum AmogusRole
{
Imposter,
Crewmate,
Unknown
}
public enum AmogusColour
{
Red,
Blue,
Green,
Pink,
Orange,
Yellow,
Black,
White,
Purple,
Brown,
Cyan,
Lime,
Maroon,
Rose,
Banana,
Gray,
Tan,
Coral,
}
public enum AmogusSkin
{
Archaeologist,
Astronaut,
Captain,
Hazmat,
Mechanic,
Military,
Miner,
Police,
SecurityGuard,
Scientist,
BlackSuit,
WhiteSuit,
Tarmac,
Wall,
Winter,
None,
}
public readonly struct ColourHelper
{
private static readonly Dictionary<AmogusColour, MagickColor> RedReplaceColors = new() {
{ AmogusColour.Red, new(215, 30, 34, 255) },
{ AmogusColour.Blue, new(29, 60, 233, 255) },
{ AmogusColour.Green, new(27, 145, 62, 255) },
{ AmogusColour.Pink, new(255, 99, 212, 255) },
{ AmogusColour.Orange, new(255, 141, 28, 255) },
{ AmogusColour.Yellow, new(255, 255, 103, 255) },
{ AmogusColour.Black, new(74, 86, 94, 255) },
{ AmogusColour.White, new(233, 247, 255, 255) },
{ AmogusColour.Purple, new(120, 61, 210, 255) },
{ AmogusColour.Brown, new(128, 88, 45, 255) },
{ AmogusColour.Cyan, new(68, 255, 247, 255) },
{ AmogusColour.Lime, new(91, 255, 75, 255) },
{ AmogusColour.Maroon, new(108, 43, 61, 255) },
{ AmogusColour.Rose, new(255, 214, 236, 255) },
{ AmogusColour.Banana, new(255, 255, 190, 255) },
{ AmogusColour.Gray, new(131, 151, 167, 255) },
{ AmogusColour.Tan, new(159, 153, 137, 255) },
{ AmogusColour.Coral, new(236, 117, 120, 255) },
};
private static readonly Dictionary<AmogusColour, MagickColor> BlueReplaceColors = new() {
{ AmogusColour.Red, new(122, 8, 56, 255) },
{ AmogusColour.Blue, new(9, 21, 142, 255) },
{ AmogusColour.Green, new(10, 77, 46, 255) },
{ AmogusColour.Pink, new(172, 43, 174, 255) },
{ AmogusColour.Orange, new(180, 62, 21, 255) },
{ AmogusColour.Yellow, new(195, 136, 34, 255) },
{ AmogusColour.Black, new(30, 31, 38, 255) },
{ AmogusColour.White, new(132, 149, 192, 255) },
{ AmogusColour.Purple, new(59, 23, 124, 255) },
{ AmogusColour.Brown, new(94, 38, 21, 255) },
{ AmogusColour.Cyan, new(36, 169, 191, 255) },
{ AmogusColour.Lime, new(21, 168, 66, 255) },
{ AmogusColour.Maroon, new(65, 15, 26, 255) },
{ AmogusColour.Rose, new(222, 146, 179, 255) },
{ AmogusColour.Banana, new(210, 188, 137, 255) },
{ AmogusColour.Gray, new(70, 86, 100, 255) },
{ AmogusColour.Tan, new(81, 65, 62, 255) },
{ AmogusColour.Coral, new(180, 67, 98, 255) },
};
private readonly MagickColor _green = new(149, 202, 220, 255);
private readonly MagickColor _red;
private readonly MagickColor _blue;
public MagickColor Red => _red;
public MagickColor Green => _green;
public MagickColor Blue => _blue;
public ColourHelper(AmogusColour colour = AmogusColour.Blue)
{
_red = RedReplaceColors[colour];
_blue = BlueReplaceColors[colour];
}
}
public static class AmongUsGeneratorHelper
{
public static readonly Dictionary<AmogusSkin, string> IdleSkins = new()
{
{ AmogusSkin.Archaeologist, "archae-idle.png" },
{ AmogusSkin.Astronaut, "astro-idle.png" },
{ AmogusSkin.Captain, "captain-idle.png" },
{ AmogusSkin.Hazmat, "hazmat-idle.png" },
{ AmogusSkin.Mechanic, "mech-idle.png" },
{ AmogusSkin.Military, "military-idle.png" },
{ AmogusSkin.Miner, "miner-idle.png" },
{ AmogusSkin.Police, "pol-idle.png" },
{ AmogusSkin.SecurityGuard, "secguard-idle.png" },
{ AmogusSkin.Scientist, "sci-idle.png" },
{ AmogusSkin.BlackSuit, "suitBlack-idle.png" },
{ AmogusSkin.WhiteSuit, "suitWhite-idle.png" },
{ AmogusSkin.Tarmac, "tarmac-idle.png" },
{ AmogusSkin.Wall, "wall-idle.png" },
{ AmogusSkin.Winter, "winter-idle.png" },
};
public static readonly Dictionary<AmogusSkin, string> EjectSkins = new()
{
{ AmogusSkin.Archaeologist, "archae-eject.png" },
{ AmogusSkin.Astronaut, "astro-eject.png" },
{ AmogusSkin.Captain, "captain-eject.png" },
{ AmogusSkin.Hazmat, "hazmat-eject.png" },
{ AmogusSkin.Mechanic, "mech-eject.png" },
{ AmogusSkin.Military, "military-eject.png" },
{ AmogusSkin.Miner, "miner-eject.png" },
{ AmogusSkin.Police, "police-eject.png" },
{ AmogusSkin.SecurityGuard, "secguard-eject.png" },
{ AmogusSkin.Scientist, "sci-eject.png" },
{ AmogusSkin.BlackSuit, "suitBlack-eject.png" },
{ AmogusSkin.WhiteSuit, "suitWhite-eject.png" },
{ AmogusSkin.Tarmac, "tarmac-eject.png" },
{ AmogusSkin.Wall, "wall-eject.png" },
{ AmogusSkin.Winter, "winter-eject.png" },
};
public static readonly Dictionary<AmogusSkin, Point> IdleOffset = new()
{
{ AmogusSkin.Archaeologist, new(13, 41) },
{ AmogusSkin.Astronaut, new(13, 46) },
{ AmogusSkin.Captain, new(14, 45) },
{ AmogusSkin.Hazmat, new(12, 34) },
{ AmogusSkin.Mechanic, new(13, 46) },
{ AmogusSkin.Military, new(11, 45) },
{ AmogusSkin.Miner, new(13, 40) },
{ AmogusSkin.Police, new(10, 45) },
{ AmogusSkin.SecurityGuard, new(13, 42) },
{ AmogusSkin.Scientist, new(14, 43) },
{ AmogusSkin.BlackSuit, new(14, 44) },
{ AmogusSkin.WhiteSuit, new(14, 44) },
{ AmogusSkin.Tarmac, new(14, 40) },
{ AmogusSkin.Wall, new(10, 44) },
{ AmogusSkin.Winter, new(9, 35) },
};
public static readonly Dictionary<AmogusSkin, Point> EjectOffset = new()
{
{ AmogusSkin.Archaeologist, new(12, 35) },
{ AmogusSkin.Astronaut, new(12, 35) },
{ AmogusSkin.Captain, new(12, 35) },
{ AmogusSkin.Hazmat, new(13, 37) },
{ AmogusSkin.Mechanic, new(13, 35) },
{ AmogusSkin.Military, new(12, 35) },
{ AmogusSkin.Miner, new(12, 36) },
{ AmogusSkin.Police, new(12, 35) },
{ AmogusSkin.SecurityGuard, new(13, 35) },
{ AmogusSkin.Scientist, new(-10, 35) },
{ AmogusSkin.BlackSuit, new(12, 35) },
{ AmogusSkin.WhiteSuit, new(11, 35) },
{ AmogusSkin.Tarmac, new(14, 37) },
{ AmogusSkin.Wall, new(12, 35) },
{ AmogusSkin.Winter, new(5, 30) },
};
}

@ -7,6 +7,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="DogStatsD-CSharp-Client" Version="8.0.0" />
<PackageReference Include="Exsersewo.Common" Version="2023.1.14" /> <PackageReference Include="Exsersewo.Common" Version="2023.1.14" />
<PackageReference Include="ICU4N" Version="60.1.0-alpha.356" /> <PackageReference Include="ICU4N" Version="60.1.0-alpha.356" />
<PackageReference Include="JwtSharp" Version="1.1.0" /> <PackageReference Include="JwtSharp" Version="1.1.0" />

@ -7,21 +7,21 @@ using Kynareth.Extensions;
namespace Kynareth.Managers; namespace Kynareth.Managers;
public static partial class ImageManager public class ImageGenerator : ImageModifier
{ {
private static List<ImageGenerationEndpoint> GenerationEndpoints; private static List<ImageGenerationEndpoint> GenerationEndpoints;
private static IReadOnlyList<string> sourceFiles; private static IReadOnlyList<string> sourceFiles;
private static string GenerationTemplateBaseFolder; private static string GenerationTemplateBaseFolder;
static void ConfigureGeneration(IConfiguration configuration, IWebHostEnvironment _appEnvironment) public ImageGenerator(IConfiguration configuration, IWebHostEnvironment appEnvironment)
{ {
sourceFiles = Directory.EnumerateFiles(Path.Combine(_appEnvironment.WebRootPath, configuration.GetValue<string>("ShitpostBot:Folder"), "..", "random")).ToImmutableList(); sourceFiles = Directory.EnumerateFiles(Path.Combine(appEnvironment.WebRootPath, configuration.GetValue<string>("ShitpostBot:Folder"), "..", "random")).ToImmutableList();
GenerationTemplateBaseFolder = Path.Combine(_appEnvironment.WebRootPath, configuration.GetValue<string>("ShitpostBot:Folder")); GenerationTemplateBaseFolder = Path.Combine(appEnvironment.WebRootPath, configuration.GetValue<string>("ShitpostBot:Folder"));
GenerationEndpoints = configuration.GetSection("ShitpostBot:Templates").Get<List<ImageGenerationEndpoint>>(); GenerationEndpoints = configuration.GetSection("ShitpostBot:Templates").Get<List<ImageGenerationEndpoint>>();
} }
public static async Task<Object> GenerateImageAsync(CancellationToken cancellationToken) public async Task<Object> GenerateImageAsync(CancellationToken cancellationToken)
{ {
ImageGenerationImage imageTemplate = null; ImageGenerationImage imageTemplate = null;
@ -45,7 +45,7 @@ public static partial class ImageManager
return await GenerateImageAsync(endpoint.Name, variant, sources, cancellationToken); return await GenerateImageAsync(endpoint.Name, variant, sources, cancellationToken);
} }
public static async Task<Object> GenerateImageAsync(string template, string variant, CancellationToken cancellationToken) public async Task<Object> GenerateImageAsync(string template, string variant, CancellationToken cancellationToken)
{ {
ImageGenerationImage imageTemplate = null; ImageGenerationImage imageTemplate = null;
@ -63,7 +63,7 @@ public static partial class ImageManager
return await GenerateImageAsync(template, variant, sources, cancellationToken); return await GenerateImageAsync(template, variant, sources, cancellationToken);
} }
public static async Task<object> GenerateImageAsync(string template, string variant, string[] sources, CancellationToken cancellationToken) public async Task<object> GenerateImageAsync(string template, string variant, string[] sources, CancellationToken cancellationToken)
{ {
while (true) while (true)
{ {
@ -188,7 +188,7 @@ public static partial class ImageManager
} }
} }
public static IEnumerable<ImageGenerationEndpointRead> GetGenerationEndpoints() public IEnumerable<ImageGenerationEndpointRead> GetGenerationEndpoints()
{ {
return GenerationEndpoints.Select(e => new ImageGenerationEndpointRead return GenerationEndpoints.Select(e => new ImageGenerationEndpointRead
{ {

@ -1,15 +1,11 @@
using System.Text; using Exsersewo.Common.Utilities;
using Exsersewo.Common.Utilities;
using ImageMagick; using ImageMagick;
namespace Kynareth.Managers; namespace Kynareth.Managers;
public static partial class ImageManager public class ImageMagiker : ImageModifier
{ {
readonly static Encoding s_defaultEncoding = Encoding.Unicode; public async Task<(MemoryStream data, string contentType)> GetLiquidRescaledImageAsync(string image, CancellationToken cancellationToken)
const double c_defaultFontSize = 20;
public static async Task<(MemoryStream data, string contentType)> GetLiquidRescaledImageAsync(string image, CancellationToken cancellationToken)
{ {
while (true) while (true)
{ {

@ -1,23 +1,24 @@
using System.Text; using System.Text;
using Exsersewo.Common.Utilities; using Exsersewo.Common.Utilities;
using ImageMagick; using ImageMagick;
using Kynareth.Extensions;
using Kynareth.Models; using Kynareth.Models;
namespace Kynareth.Managers; namespace Kynareth.Managers;
public static partial class ImageManager public class ImageManipulator : ImageModifier
{ {
private static List<ImageManipulationImage> ManipulationEndpoints; private static List<ImageManipulationImage> ManipulationEndpoints;
private static string ManipulationTemplateBaseFolder; private static string ManipulationTemplateBaseFolder;
static void ConfigureManipulation(IConfiguration configuration, IWebHostEnvironment _appEnvironment) public ImageManipulator(IConfiguration configuration, IWebHostEnvironment appEnvironment)
{ {
ManipulationTemplateBaseFolder = Path.Combine(_appEnvironment.WebRootPath, configuration.GetValue<string>("ImageManipulation:Folder")); ManipulationTemplateBaseFolder = Path.Combine(appEnvironment.WebRootPath, configuration.GetValue<string>("ImageManipulation:Folder"));
ManipulationEndpoints = configuration.GetSection("ImageManipulation:Templates").Get<List<ImageManipulationImage>>(); ManipulationEndpoints = configuration.GetSection("ImageManipulation:Templates").Get<List<ImageManipulationImage>>();
} }
public static IEnumerable<ImageManipulationImageRead> GetManipulationEndpoints() public IEnumerable<ImageManipulationImageRead> GetManipulationEndpoints()
{ {
return ManipulationEndpoints.Select(e => new ImageManipulationImageRead return ManipulationEndpoints.Select(e => new ImageManipulationImageRead
{ {
@ -26,7 +27,7 @@ public static partial class ImageManager
}); });
} }
public static async Task<object> GenerateMemeImageAsync(string template, string[] texts, CancellationToken cancellationToken) public async Task<object> GenerateMemeImageAsync(string template, string[] texts, CancellationToken cancellationToken)
{ {
while (true) while (true)
{ {

@ -0,0 +1,38 @@
using System.Globalization;
using System.Text;
using ICU4N.Text;
using Microsoft.AspNetCore.Mvc;
namespace Kynareth.Managers;
public class ImageModifier
{
protected static BreakIterator lineBreakIterator = BreakIterator.GetLineInstance(CultureInfo.InvariantCulture);
protected string GetText(string text)
{
StringBuilder newText = new();
// set the text to be broken
lineBreakIterator.SetText(text);
// get the first boundary
int start = lineBreakIterator.First();
// iterate over all boundaries
for (int end = lineBreakIterator.Next(); end != BreakIterator.Done; start = end, end = lineBreakIterator.Next())
{
// get the current line
string line = text.Substring(start, end - start);
// do something with the line
newText.AppendLine(line);
Console.WriteLine(line);
}
return newText.ToString();
}
protected ObjectResult GetResult(int statusCode, object data)
=> new(data) { StatusCode = statusCode };
}

@ -1,8 +1,10 @@
using Exsersewo.Common.Utilities; using Exsersewo.Common.Utilities;
using Kynareth.Helpers; using Kynareth.Helpers;
using Kynareth.Helpers.AmongUs;
using Kynareth.Managers; using Kynareth.Managers;
using Kynareth.Models; using Kynareth.Models;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using StatsdClient;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
builder.WebHost.UseKestrel(); builder.WebHost.UseKestrel();
@ -30,11 +32,24 @@ builder.Services.AddDbContext<KynarethDbContext>(options =>
options.UseLazyLoadingProxies(); options.UseLazyLoadingProxies();
}, contextLifetime: ServiceLifetime.Transient, optionsLifetime: ServiceLifetime.Singleton); }, contextLifetime: ServiceLifetime.Transient, optionsLifetime: ServiceLifetime.Singleton);
builder.Services.AddSingleton(builder.Environment);
builder.Services.AddSingleton(builder.Configuration);
builder.Services.AddSingleton<ImageManipulator>();
builder.Services.AddSingleton<ImageGenerator>();
builder.Services.AddSingleton<ImageMagiker>();
builder.Services.AddSingleton<AmongUsGenerator>();
var app = builder.Build(); var app = builder.Build();
RequestHelper.Configure(app); RequestHelper.Configure(app);
ImageManager.Configure(app.Configuration, app.Environment);
AmogusHelper.Configure(app.Environment); var dogstatsdConfig = new StatsdConfig
{
StatsdServerName = "127.0.0.1",
StatsdPort = 8125,
};
DogStatsd.Configure(dogstatsdConfig);
// Configure the HTTP request pipeline. // Configure the HTTP request pipeline.
//if (app.Environment.IsDevelopment()) //if (app.Environment.IsDevelopment())

Loading…
Cancel
Save