Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 23 additions & 10 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,17 @@ tab_width = 4
end_of_line = crlf
insert_final_newline = true

#### .NET Coding Conventions ####

# Organize usings
dotnet_separate_import_directive_groups = false
dotnet_sort_system_directives_first = true

#### C# Coding Conventions ####

# Code-block preferences
csharp_style_namespace_declarations = file_scoped
# Visual Studio
csharp_style_namespace_declarations=file_scoped # Use file scoped namespace by default for new class files

### Naming styles ###

# Naming rules

dotnet_naming_rule.private_or_internal_static_field_should_be_pascal_case.severity = warning
dotnet_naming_rule.private_or_internal_static_field_should_be_pascal_case.symbols = private_or_internal_static_field
dotnet_naming_rule.private_or_internal_static_field_should_be_pascal_case.style = pascal_case

dotnet_naming_rule.private_or_internal_field_should_be__fieldname.severity = suggestion
dotnet_naming_rule.private_or_internal_field_should_be__fieldname.symbols = private_or_internal_field
dotnet_naming_rule.private_or_internal_field_should_be__fieldname.style = _fieldname
Expand All @@ -38,8 +34,16 @@ dotnet_naming_rule.local_should_be_camelcase.severity = warning
dotnet_naming_rule.local_should_be_camelcase.symbols = local
dotnet_naming_rule.local_should_be_camelcase.style = camelcase

dotnet_naming_rule.constant_field_should_be_pascal_case.severity = warning
dotnet_naming_rule.constant_field_should_be_pascal_case.symbols = constant_field
dotnet_naming_rule.constant_field_should_be_pascal_case.style = pascal_case

# Symbol specifications

dotnet_naming_symbols.private_or_internal_static_field.applicable_kinds = field
dotnet_naming_symbols.private_or_internal_static_field.applicable_accessibilities = internal, private, private_protected
dotnet_naming_symbols.private_or_internal_static_field.required_modifiers = static

dotnet_naming_symbols.private_or_internal_field.applicable_kinds = field
dotnet_naming_symbols.private_or_internal_field.applicable_accessibilities = internal, private, private_protected
dotnet_naming_symbols.private_or_internal_field.required_modifiers =
Expand All @@ -48,6 +52,10 @@ dotnet_naming_symbols.local.applicable_kinds = local
dotnet_naming_symbols.local.applicable_accessibilities = local
dotnet_naming_symbols.local.required_modifiers =

dotnet_naming_symbols.constant_field.applicable_kinds = field
dotnet_naming_symbols.constant_field.applicable_accessibilities = *
dotnet_naming_symbols.constant_field.required_modifiers = const

# Naming styles

dotnet_naming_style._fieldname.required_prefix = _
Expand All @@ -60,6 +68,11 @@ dotnet_naming_style.camelcase.required_suffix =
dotnet_naming_style.camelcase.word_separator =
dotnet_naming_style.camelcase.capitalization = camel_case

dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case

### Stylecop rules ###

# Default rulesets:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ jobs:
env:
CONNECTION_STRING: ${{secrets.DB_CONNECTION_STRING}}
BOT_TOKEN: ${{secrets.BOT_TOKEN}}
OPENAI_API_KEY: ${{secrets.OPENAI_API_KEY}}

- name: Setup .NET Core
uses: actions/setup-dotnet@v3
Expand Down
10 changes: 10 additions & 0 deletions Kattbot.sln
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kattbot.Common", "Kattbot.C
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kattbot.Data.Migrations", "Kattbot.Data.Migrations\Kattbot.Data.Migrations.csproj", "{D26776E6-F360-425C-9281-F4E7B176197E}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "deploy", "deploy", "{727EE4ED-CFEC-4BBC-B67A-3DA7F8EE3F1E}"
ProjectSection(SolutionItems) = preProject
deploy\kattbot-backup-db.sh = deploy\kattbot-backup-db.sh
deploy\kattbot-deploy.sh = deploy\kattbot-deploy.sh
deploy\kattbot.service = deploy\kattbot.service
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -58,6 +65,9 @@ Global
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{727EE4ED-CFEC-4BBC-B67A-3DA7F8EE3F1E} = {2D6F1BD9-5D5D-4C85-B254-B773679A5AF9}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {49047B12-10BC-4E9D-9DA6-758947DF9CE8}
EndGlobalSection
Expand Down
41 changes: 25 additions & 16 deletions Kattbot/BotOptions.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Kattbot
namespace Kattbot;

public record BotOptions
{
public const string OptionsKey = "Kattbot";

public string CommandPrefix { get; set; } = null!;

public string AlternateCommandPrefix { get; set; } = null!;

public string ConnectionString { get; set; } = null!;

public string BotToken { get; set; } = null!;

public ulong ErrorLogGuildId { get; set; }

public ulong ErrorLogChannelId { get; set; }

public string OpenAiApiKey { get; set; } = null!;
}

public record KattGptOptions
{
public class BotOptions
{
public const string OptionsKey = "Kattbot";

public string CommandPrefix { get; set; } = null!;
public string AlternateCommandPrefix { get; set; } = null!;
public string ConnectionString { get; set; } = null!;
public string BotToken { get; set; } = null!;
public ulong ErrorLogGuildId { get; set; }
public ulong ErrorLogChannelId { get; set; }
}
public const string OptionsKey = "KattGpt";

public string[] SystemPrompts { get; set; } = Array.Empty<string>();
}
59 changes: 13 additions & 46 deletions Kattbot/CommandHandlers/Images/DallePromptCommand.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using DSharpPlus.CommandsNext;
using DSharpPlus.Entities;
using Kattbot.Services.Images;
using Kattbot.Services.KattGpt;
using MediatR;
using Newtonsoft.Json;

namespace Kattbot.CommandHandlers.Images;

#pragma warning disable SA1402 // File may only contain a single type
public class DallePromptCommand : CommandRequest
{
public string Prompt { get; set; }
Expand All @@ -26,12 +24,12 @@ public DallePromptCommand(CommandContext ctx, string prompt)

public class DallePromptCommandHandler : AsyncRequestHandler<DallePromptCommand>
{
private readonly IHttpClientFactory _httpClientFactory;
private readonly DalleHttpClient _dalleHttpClient;
private readonly ImageService _imageService;

public DallePromptCommandHandler(IHttpClientFactory httpClientFactory, ImageService imageService)
public DallePromptCommandHandler(DalleHttpClient dalleHttpClient, ImageService imageService)
{
_httpClientFactory = httpClientFactory;
_dalleHttpClient = dalleHttpClient;
_imageService = imageService;
}

Expand All @@ -41,41 +39,25 @@ protected override async Task Handle(DallePromptCommand request, CancellationTok

try
{
HttpClient client = _httpClientFactory.CreateClient();
var response = await _dalleHttpClient.CreateImage(new CreateImageRequest { Prompt = request.Prompt });

string url = "https://backend.craiyon.com/generate";
if (response.Data == null || !response.Data.Any()) throw new Exception("Empty result");

var body = new DalleRequest { Prompt = request.Prompt };
var imageUrl = response.Data.First();

string json = JsonConvert.SerializeObject(body);
var data = new StringContent(json, Encoding.UTF8, "application/json");
var image = await _imageService.LoadImage(imageUrl.Url);

HttpResponseMessage response = await client.PostAsync(url, data, cancellationToken);

response.EnsureSuccessStatusCode();

string jsonString = await response.Content.ReadAsStringAsync(cancellationToken);

DalleResponse? searchResponse = JsonConvert.DeserializeObject<DalleResponse>(jsonString);

if (searchResponse?.Images == null)
{
throw new Exception("Couldn't deserialize response");
}

ImageStreamResult combinedImage = await _imageService.CombineImages(searchResponse.Images.ToArray());
var imageStream = await _imageService.GetImageStream(image);

string safeFileName = new(request.Prompt.Select(c => char.IsLetterOrDigit(c) ? c : '_').ToArray());
string fileName = $"{safeFileName}.{combinedImage.FileExtension}";
string fileName = $"{safeFileName}.{imageStream.FileExtension}";

DiscordEmbedBuilder eb = new DiscordEmbedBuilder()
.WithTitle(request.Prompt)
.WithImageUrl($"attachment://{fileName}")
.WithFooter("Generated by craiyon.com")
.WithUrl("https://www.craiyon.com/");
.WithImageUrl($"attachment://{fileName}");

DiscordMessageBuilder mb = new DiscordMessageBuilder()
.AddFile(fileName, combinedImage.MemoryStream)
.AddFile(fileName, imageStream.MemoryStream)
.WithEmbed(eb)
.WithContent($"There you go {request.Ctx.Member?.Mention ?? "Unknown user"}");

Expand All @@ -90,18 +72,3 @@ protected override async Task Handle(DallePromptCommand request, CancellationTok
}
}
}

public class DalleResponse
{
[JsonProperty("images")]
public List<string>? Images;

[JsonProperty("version")]
public string? Version;
}

public class DalleRequest
{
[JsonProperty("prompt")]
public string Prompt { get; set; } = string.Empty;
}
13 changes: 12 additions & 1 deletion Kattbot/CommandModules/AdminModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public async Task AddFriend(CommandContext ctx, DiscordMember user)

var hasRole = await _botUserRolesRepo.UserHasRole(userId, friendRole);

if(hasRole)
if (hasRole)
{
await ctx.RespondAsync("User already has role");
return;
Expand Down Expand Up @@ -93,5 +93,16 @@ public async Task SetBotChannel(CommandContext ctx, DiscordChannel channel)

await ctx.RespondAsync($"Set bot channel to #{channel.Name}");
}

[Command("set-kattgpt-channel")]
public async Task SetKattGptChannel(CommandContext ctx, DiscordChannel channel)
{
var channelId = channel.Id;
var guildId = channel.GuildId!.Value;

await _guildSettingsService.SetKattGptChannel(guildId, channelId);

await ctx.RespondAsync($"Set KattGpt channel to #{channel.Name}");
}
}
}
15 changes: 14 additions & 1 deletion Kattbot/NotificationHandlers/EventNotifications.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
using MediatR;
using DSharpPlus.EventArgs;
using MediatR;

namespace Kattbot.NotificationHandlers;

public abstract record EventNotification(EventContext Ctx) : INotification;

// TODO clean this up by removing EventContext from base contructor entirely
// or at least move the mapping somewhere else
public record MessageCreatedNotification(MessageCreateEventArgs EventArgs)
: EventNotification(new EventContext()
{
Channel = EventArgs.Channel,
Guild = EventArgs.Guild,
User = EventArgs.Author,
Message = EventArgs.Message,
EventName = "MessageCreated",
});
Loading