Compare commits

..

15 commits

Author SHA1 Message Date
347297fb12
5.1.3 2024-07-08 01:38:24 +12:00
187c56e8c7
I found more and I hate everything
I am slowly losing my sanity
2024-07-08 01:34:34 +12:00
e100526d80
Found more words that were wrong
If I find more I am going to scream
2024-07-08 01:30:14 +12:00
94129fb3e2
Fixed a word being wrong in the EllieMarmalade project 2024-07-08 01:28:26 +12:00
2be2cdf713
Updated changelog and upped version to 5.1.3
I might have also updated creds_example.yml
2024-07-08 01:23:56 +12:00
291b2fb580
Updated Help module 2024-07-08 01:08:43 +12:00
2ebed3f441
Updated Marmalades module 2024-07-08 00:52:59 +12:00
f2618793b3
Updated Utility module 2024-07-08 00:36:45 +12:00
0787490aea
Updated Searches module 2024-07-07 22:28:56 +12:00
f91771a8db
Updated Games module 2024-07-07 20:35:25 +12:00
499c2a9a5f
Updated Administration module 2024-07-07 19:30:36 +12:00
2d7f345c40
Updated Common files 2024-07-07 18:10:58 +12:00
3a36799941
Updated data files 2024-07-07 14:36:56 +12:00
30f859b9a0
Updated Db migrations 2024-07-07 14:36:27 +12:00
56d82a5a26
Updated Db models 2024-07-07 14:27:42 +12:00
41 changed files with 14969 additions and 14818 deletions

View file

@ -2,6 +2,23 @@
Mostly based on [keepachangelog](https://keepachangelog.com/en/1.0.0/) except date format. a-c-f-r-o Mostly based on [keepachangelog](https://keepachangelog.com/en/1.0.0/) except date format. a-c-f-r-o
## [5.1.3] - 08.07.2024
### Added
- Added `'quran` command, which will show the provided ayah in english and arabic, including recitation by Alafasy
### Changed
- Replying to the bot's message in the channel where chatterbot is enabled will also trigger the ai response, as if you pinged the bot. This only works for chatterbot, but not for ellie ai command prompts
### Fixed
- Fixed `'stickeradd` it now properly supports 300x300 image uploads.
- Bot should now trim the invalid characters from chatterbot usernames to avoid openai errors
- Fixed prompt triggering chatterbot responses twice
- Honeypot commands now actually works
## [5.1.2] - 29.06.2024 ## [5.1.2] - 29.06.2024
### Fixed ### Fixed

View file

@ -1,7 +1,7 @@
namespace EllieBot.Marmalade; namespace EllieBot.Marmalade;
/// <summary> /// <summary>
/// Marks the class as a service which can be used within the same Medusa /// Marks the class as a service which can be used within the same Marmalade
/// </summary> /// </summary>
[AttributeUsage(AttributeTargets.Class)] [AttributeUsage(AttributeTargets.Class)]
public class svcAttribute : Attribute public class svcAttribute : Attribute

View file

@ -110,7 +110,7 @@ public sealed class Bot : IBot
var svcs = new Container(); var svcs = new Container();
// this is required in order for medusa unloading to work // this is required in order for marmalade unloading to work
// svcs.Components.Remove<IPlanner, Planner>(); // svcs.Components.Remove<IPlanner, Planner>();
// svcs.Components.Add<IPlanner, RemovablePlanner>(); // svcs.Components.Add<IPlanner, RemovablePlanner>();

View file

@ -282,10 +282,10 @@ public abstract class EllieContext : DbContext
var selfassignableRolesEntity = modelBuilder.Entity<SelfAssignedRole>(); var selfassignableRolesEntity = modelBuilder.Entity<SelfAssignedRole>();
selfassignableRolesEntity.HasIndex(s => new selfassignableRolesEntity.HasIndex(s => new
{ {
s.GuildId, s.GuildId,
s.RoleId s.RoleId
}) })
.IsUnique(); .IsUnique();
selfassignableRolesEntity.Property(x => x.Group).HasDefaultValue(0); selfassignableRolesEntity.Property(x => x.Group).HasDefaultValue(0);
@ -358,10 +358,10 @@ public abstract class EllieContext : DbContext
var xps = modelBuilder.Entity<UserXpStats>(); var xps = modelBuilder.Entity<UserXpStats>();
xps.HasIndex(x => new xps.HasIndex(x => new
{ {
x.UserId, x.UserId,
x.GuildId x.GuildId
}) })
.IsUnique(); .IsUnique();
xps.HasIndex(x => x.UserId); xps.HasIndex(x => x.UserId);
@ -407,9 +407,9 @@ public abstract class EllieContext : DbContext
.OnDelete(DeleteBehavior.SetNull); .OnDelete(DeleteBehavior.SetNull);
ci.HasIndex(x => new ci.HasIndex(x => new
{ {
x.Name x.Name
}) })
.IsUnique(); .IsUnique();
#endregion #endregion
@ -528,10 +528,10 @@ public abstract class EllieContext : DbContext
.IsUnique(false); .IsUnique(false);
rr2.HasIndex(x => new rr2.HasIndex(x => new
{ {
x.MessageId, x.MessageId,
x.Emote x.Emote
}) })
.IsUnique(); .IsUnique();
}); });
@ -606,11 +606,11 @@ public abstract class EllieContext : DbContext
{ {
// user can own only one of each item // user can own only one of each item
x.HasIndex(model => new x.HasIndex(model => new
{ {
model.UserId, model.UserId,
model.ItemType, model.ItemType,
model.ItemKey model.ItemKey
}) })
.IsUnique(); .IsUnique();
}); });
@ -635,10 +635,10 @@ public abstract class EllieContext : DbContext
#region Sticky Roles #region Sticky Roles
modelBuilder.Entity<StickyRole>(sr => sr.HasIndex(x => new modelBuilder.Entity<StickyRole>(sr => sr.HasIndex(x => new
{ {
x.GuildId, x.GuildId,
x.UserId x.UserId
}) })
.IsUnique()); .IsUnique());
#endregion #endregion

View file

@ -4,7 +4,7 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ImplicitUsings>true</ImplicitUsings> <ImplicitUsings>true</ImplicitUsings>
<SatelliteResourceLanguages>en</SatelliteResourceLanguages> <SatelliteResourceLanguages>en</SatelliteResourceLanguages>
<Version>5.1.2</Version> <Version>5.1.3</Version>
<!-- Output/build --> <!-- Output/build -->
<RunWorkingDirectory>$(MSBuildProjectDirectory)</RunWorkingDirectory> <RunWorkingDirectory>$(MSBuildProjectDirectory)</RunWorkingDirectory>

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -5,7 +5,7 @@ using EllieBot.Db.Models;
using System.Threading.Channels; using System.Threading.Channels;
namespace EllieBot.Modules.Administration.Honeypot; namespace EllieBot.Modules.Administration.Honeypot;
/*
public sealed class HoneyPotService : IHoneyPotService, IReadyExecutor, IExecNoCommand, IEService public sealed class HoneyPotService : IHoneyPotService, IReadyExecutor, IExecNoCommand, IEService
{ {
private readonly DbService _db; private readonly DbService _db;
@ -93,4 +93,3 @@ public sealed class HoneyPotService : IHoneyPotService, IReadyExecutor, IExecNoC
} }
} }
} }
*/

View file

@ -1,7 +1,7 @@
using EllieBot.Modules.Administration.Honeypot; using EllieBot.Modules.Administration.Honeypot;
namespace EllieBot.Modules.Administration; namespace EllieBot.Modules.Administration;
/*
public partial class Administration public partial class Administration
{ {
[Group] [Group]
@ -27,4 +27,3 @@ public partial class Administration
} }
} }
} }
*/

View file

@ -1,7 +1,6 @@
namespace EllieBot.Modules.Administration.Honeypot; namespace EllieBot.Modules.Administration.Honeypot;
/*
public interface IHoneyPotService public interface IHoneyPotService
{ {
public Task<bool> ToggleHoneypotChannel(ulong guildId, ulong channelId); public Task<bool> ToggleHoneypotChannel(ulong guildId, ulong channelId);
} }
*/

View file

@ -3,7 +3,7 @@
namespace EllieBot.Modules.Administration; namespace EllieBot.Modules.Administration;
public sealed class DummyLogCommandService : ILogCommandService public sealed class DummyLogCommandService : ILogCommandService
#if GLOBAL_NADEKO #if GLOBAL_ELLIE
, IEService , IEService
#endif #endif
{ {

View file

@ -8,8 +8,8 @@ using EllieBot.Db.Models;
namespace EllieBot.Modules.Administration; namespace EllieBot.Modules.Administration;
public sealed class LogCommandService : ILogCommandService, IReadyExecutor public sealed class LogCommandService : ILogCommandService, IReadyExecutor
#if !GLOBAL_NADEKO #if !GLOBAL_ELLIE
, IEService // don't load this service on global nadeko , IEService // don't load this service on global ellie
#endif #endif
{ {
public ConcurrentDictionary<ulong, LogSetting> GuildLogSettings { get; } public ConcurrentDictionary<ulong, LogSetting> GuildLogSettings { get; }

View file

@ -228,7 +228,7 @@ public class UserPunishService : IEService, IReadyExecutor
case PunishmentAction.RemoveRoles: case PunishmentAction.RemoveRoles:
return botUser.GuildPermissions.ManageRoles; return botUser.GuildPermissions.ManageRoles;
case PunishmentAction.ChatMute: case PunishmentAction.ChatMute:
return botUser.GuildPermissions.ManageRoles; // adds nadeko-mute role return botUser.GuildPermissions.ManageRoles; // adds ellie-mute role
case PunishmentAction.VoiceMute: case PunishmentAction.VoiceMute:
return botUser.GuildPermissions.MuteMembers; return botUser.GuildPermissions.MuteMembers;
case PunishmentAction.AddRole: case PunishmentAction.AddRole:

View file

@ -88,14 +88,16 @@ public class ChatterBotService : IExecOnMessage
public string PrepareMessage(IUserMessage msg) public string PrepareMessage(IUserMessage msg)
{ {
var nadekoId = _client.CurrentUser.Id; var ellieId = _client.CurrentUser.Id;
var normalMention = $"<@{nadekoId}> "; var normalMention = $"<@{ellieId}> ";
var nickMention = $"<@!{nadekoId}> "; var nickMention = $"<@!{ellieId}> ";
string message; string message;
if (msg.Content.StartsWith(normalMention, StringComparison.InvariantCulture)) if (msg.Content.StartsWith(normalMention, StringComparison.InvariantCulture))
message = msg.Content[normalMention.Length..].Trim(); message = msg.Content[normalMention.Length..].Trim();
else if (msg.Content.StartsWith(nickMention, StringComparison.InvariantCulture)) else if (msg.Content.StartsWith(nickMention, StringComparison.InvariantCulture))
message = msg.Content[nickMention.Length..].Trim(); message = msg.Content[nickMention.Length..].Trim();
else if (msg.ReferencedMessage?.Author.Id == ellieId)
message = msg.Content;
else else
return null; return null;

View file

@ -3,10 +3,12 @@ using Newtonsoft.Json;
using OneOf.Types; using OneOf.Types;
using System.Net.Http.Json; using System.Net.Http.Json;
using SharpToken; using SharpToken;
using System.CodeDom;
using System.Text.RegularExpressions;
namespace EllieBot.Modules.Games.Common.ChatterBot; namespace EllieBot.Modules.Games.Common.ChatterBot;
public class OfficialGptSession : IChatterBotSession public partial class OfficialGptSession : IChatterBotSession
{ {
private string Uri private string Uri
=> $"https://api.openai.com/v1/chat/completions"; => $"https://api.openai.com/v1/chat/completions";
@ -16,7 +18,7 @@ public class OfficialGptSession : IChatterBotSession
private readonly int _maxHistory; private readonly int _maxHistory;
private readonly int _maxTokens; private readonly int _maxTokens;
private readonly int _minTokens; private readonly int _minTokens;
private readonly string _nadekoUsername; private readonly string _ellieUsername;
private readonly GptEncoding _encoding; private readonly GptEncoding _encoding;
private List<GPTMessage> messages = new(); private List<GPTMessage> messages = new();
private readonly IHttpClientFactory _httpFactory; private readonly IHttpClientFactory _httpFactory;
@ -29,7 +31,7 @@ public class OfficialGptSession : IChatterBotSession
int maxTokens, int maxTokens,
int minTokens, int minTokens,
string personality, string personality,
string nadekoUsername, string ellieUsername,
IHttpClientFactory factory) IHttpClientFactory factory)
{ {
_apiKey = apiKey; _apiKey = apiKey;
@ -45,24 +47,31 @@ public class OfficialGptSession : IChatterBotSession
_maxHistory = chatHistory; _maxHistory = chatHistory;
_maxTokens = maxTokens; _maxTokens = maxTokens;
_minTokens = minTokens; _minTokens = minTokens;
_nadekoUsername = nadekoUsername; _ellieUsername = UsernameCleaner().Replace(ellieUsername, "");
_encoding = GptEncoding.GetEncodingForModel(_model); _encoding = GptEncoding.GetEncodingForModel(_model);
messages.Add(new() messages.Add(new()
{ {
Role = "system", Role = "system",
Content = personality, Content = personality,
Name = _nadekoUsername Name = _ellieUsername
}); });
} }
[GeneratedRegex("[^a-zA-Z0-9_-]")]
private static partial Regex UsernameCleaner();
public async Task<OneOf.OneOf<ThinkResult, Error<string>>> Think(string input, string username) public async Task<OneOf.OneOf<ThinkResult, Error<string>>> Think(string input, string username)
{ {
username = UsernameCleaner().Replace(username, "");
messages.Add(new() messages.Add(new()
{ {
Role = "user", Role = "user",
Content = input, Content = input,
Name = username Name = username
}); });
while (messages.Count > _maxHistory + 2) while (messages.Count > _maxHistory + 2)
{ {
messages.RemoveAt(1); messages.RemoveAt(1);
@ -104,7 +113,7 @@ public class OfficialGptSession : IChatterBotSession
{ {
var response = JsonConvert.DeserializeObject<OpenAiCompletionResponse>(dataString); var response = JsonConvert.DeserializeObject<OpenAiCompletionResponse>(dataString);
Log.Information("Received response: {response}", dataString); Log.Information("Received response: {response} ", dataString);
var res = response?.Choices?[0]; var res = response?.Choices?[0];
var message = res?.Message?.Content; var message = res?.Message?.Content;
@ -117,7 +126,7 @@ public class OfficialGptSession : IChatterBotSession
{ {
Role = "assistant", Role = "assistant",
Content = message, Content = message,
Name = _nadekoUsername Name = _ellieUsername
}); });
return new ThinkResult() return new ThinkResult()

View file

@ -9,8 +9,8 @@ namespace EllieBot.Modules.Help;
public sealed partial class Help : EllieModule<HelpService> public sealed partial class Help : EllieModule<HelpService>
{ {
public const string PATREON_URL = "https://patreon.com/toastie_t0ast"; public const string PATREON_URL = "https://patreon.com/elliebot";
public const string PAYPAL_URL = "https://paypal.me/EmotionChild"; public const string PAYPAL_URL = "https://paypal.me/toastiet0ast";
private readonly ICommandsUtilityService _cus; private readonly ICommandsUtilityService _cus;
private readonly CommandService _cmds; private readonly CommandService _cmds;
@ -529,7 +529,7 @@ public sealed partial class Help : EllieModule<HelpService>
*Keep in mind that running the bot on your computer means that the bot will be offline when you turn off your computer* *Keep in mind that running the bot on your computer means that the bot will be offline when you turn off your computer*
- You can find the selfhosting guides by using the `.guide` command and clicking on the second link that pops up. - You can find the selfhosting guides by using the `.guide` command and clicking on the second link that pops up.
- If you decide to selfhost the bot, still consider [supporting the project](https://patreon.com/join/toastie_t0ast) to keep the development going :) - If you decide to selfhost the bot, still consider [supporting the project](https://patreon.com/join/elliebot) to keep the development going :)
""", """,
true); true);
@ -551,7 +551,7 @@ public sealed partial class Help : EllieModule<HelpService>
eb eb
.WithDescription(""" .WithDescription("""
EllieBot relies on donations to keep the servers, services and APIs running. EllieBot relies on donations to keep the servers, services and APIs running.
Donating will give you access to some exclusive features. You can read about them on the [patreon page](https://patreon.com/join/toastie_t0ast) Donating will give you access to some exclusive features. You can read about them on the [patreon page](https://patreon.com/join/elliebot)
""") """)
.AddField("Donation Instructions", .AddField("Donation Instructions",
$""" $"""
@ -559,7 +559,7 @@ public sealed partial class Help : EllieModule<HelpService>
**Step 1:** Pledge on Patreon **Step 1:** Pledge on Patreon
`1.` Go to <https://patreon.com/join/toastie_t0ast> and choose a tier. `1.` Go to <https://patreon.com/join/elliebot> and choose a tier.
`2.` Make sure your payment is processed and accepted. `2.` Make sure your payment is processed and accepted.
**Step 2** 🤝 Connect your Discord account 🤝 **Step 2** 🤝 Connect your Discord account 🤝

View file

@ -130,7 +130,7 @@ public class CryptoService : IEService
await _getCryptoLock.WaitAsync(); await _getCryptoLock.WaitAsync();
try try
{ {
var data = await _cache.GetOrAddAsync(new("nadeko:crypto_data"), var data = await _cache.GetOrAddAsync(new("ellie:crypto_data"),
async () => async () =>
{ {
try try

View file

@ -0,0 +1,102 @@
using EllieBot.Modules.Searches.Common;
using System.Net.Http.Json;
using System.Text.Json.Serialization;
namespace EllieBot.Modules.Searches;
public partial class Searches
{
public partial class ReligiousCommands : EllieModule
{
private readonly IHttpClientFactory _httpFactory;
public ReligiousCommands(IHttpClientFactory httpFactory)
{
_httpFactory = httpFactory;
}
[Cmd]
[RequireContext(ContextType.Guild)]
public async Task Bible(string book, string chapterAndVerse)
{
var obj = new BibleVerses();
try
{
using var http = _httpFactory.CreateClient();
obj = await http.GetFromJsonAsync<BibleVerses>($"https://bible-api.com/{book} {chapterAndVerse}");
}
catch
{
}
if (obj.Error is not null || obj.Verses is null || obj.Verses.Length == 0)
await Response().Error(obj.Error ?? "No verse found.").SendAsync();
else
{
var v = obj.Verses[0];
await Response()
.Embed(_sender.CreateEmbed()
.WithOkColor()
.WithTitle($"{v.BookName} {v.Chapter}:{v.Verse}")
.WithDescription(v.Text))
.SendAsync();
}
}
[Cmd]
[RequireContext(ContextType.Guild)]
public async Task Quran(string ayah)
{
using var http = _httpFactory.CreateClient();
var obj = await http.GetFromJsonAsync<QuranResponse<QuranAyah>>($"https://api.alquran.cloud/v1/ayah/{Uri.EscapeDataString(ayah)}/editions/en.asad,ar.alafasy");
if(obj is null or not { Code: 200 })
{
await Response().Error("No verse found.").SendAsync();
return;
}
var english = obj.Data[0];
var arabic = obj.Data[1];
await using var audio = await http.GetStreamAsync(arabic.Audio);
await Response()
.Embed(_sender.CreateEmbed()
.WithOkColor()
.AddField("Arabic", arabic.Text)
.AddField("English", english.Text)
.WithFooter(arabic.Number.ToString()))
.File(audio, Uri.EscapeDataString(ayah) + ".mp3")
.SendAsync();
}
}
}
public sealed class QuranResponse<T>
{
[JsonPropertyName("code")]
public int Code { get; set; }
[JsonPropertyName("status")]
public string Status { get; set; }
[JsonPropertyName("data")]
public T[] Data { get; set; }
}
public sealed class QuranAyah
{
[JsonPropertyName("number")]
public int Number { get; set; }
[JsonPropertyName("audio")]
public string Audio { get; set; }
[JsonPropertyName("name")]
public string Name { get; set; }
[JsonPropertyName("text")]
public string Text { get; set; }
}

View file

@ -528,34 +528,6 @@ public partial class Searches : EllieModule<SearchesService>
} }
} }
[Cmd]
[RequireContext(ContextType.Guild)]
public async Task Bible(string book, string chapterAndVerse)
{
var obj = new BibleVerses();
try
{
using var http = _httpFactory.CreateClient();
obj = await http.GetFromJsonAsync<BibleVerses>($"https://bible-api.com/{book} {chapterAndVerse}");
}
catch
{
}
if (obj.Error is not null || obj.Verses is null || obj.Verses.Length == 0)
await Response().Error(obj.Error ?? "No verse found.").SendAsync();
else
{
var v = obj.Verses[0];
await Response()
.Embed(_sender.CreateEmbed()
.WithOkColor()
.WithTitle($"{v.BookName} {v.Chapter}:{v.Verse}")
.WithDescription(v.Text))
.SendAsync();
}
}
[Cmd] [Cmd]
public async Task Steam([Leftover] string query) public async Task Steam([Leftover] string query)
{ {

View file

@ -126,7 +126,7 @@ public class SearchesService : IEService
{ {
query = query.Trim().ToLowerInvariant(); query = query.Trim().ToLowerInvariant();
return await _c.GetOrAddAsync(new($"nadeko_weather_{query}"), return await _c.GetOrAddAsync(new($"ellie_weather_{query}"),
async () => await GetWeatherDataFactory(query), async () => await GetWeatherDataFactory(query),
TimeSpan.FromHours(3)); TimeSpan.FromHours(3));
} }
@ -156,7 +156,7 @@ public class SearchesService : IEService
public Task<((string Address, DateTime Time, string TimeZoneName), TimeErrors?)> GetTimeDataAsync(string arg) public Task<((string Address, DateTime Time, string TimeZoneName), TimeErrors?)> GetTimeDataAsync(string arg)
=> GetTimeDataFactory(arg); => GetTimeDataFactory(arg);
//return _cache.GetOrAddCachedDataAsync($"nadeko_time_{arg}", //return _cache.GetOrAddCachedDataAsync($"ellie_time_{arg}",
// GetTimeDataFactory, // GetTimeDataFactory,
// arg, // arg,
// TimeSpan.FromMinutes(1)); // TimeSpan.FromMinutes(1));

View file

@ -196,14 +196,14 @@ public sealed class AiAssistantService
if (guild is not SocketGuild sg) if (guild is not SocketGuild sg)
return false; return false;
var nadekoId = _client.CurrentUser.Id; var ellieId = _client.CurrentUser.Id;
var channel = msg.Channel as ITextChannel; var channel = msg.Channel as ITextChannel;
if (channel is null) if (channel is null)
return false; return false;
var normalMention = $"<@{nadekoId}> "; var normalMention = $"<@{ellieId}> ";
var nickMention = $"<@!{nadekoId}> "; var nickMention = $"<@!{ellieId}> ";
string query; string query;
if (msg.Content.StartsWith(normalMention, StringComparison.InvariantCulture)) if (msg.Content.StartsWith(normalMention, StringComparison.InvariantCulture))
query = msg.Content[normalMention.Length..].Trim(); query = msg.Content[normalMention.Length..].Trim();
@ -251,7 +251,7 @@ public sealed class AiAssistantService
return false; return false;
await _cbs.RunChatterBot(sg, msg, channel, sess, query); await _cbs.RunChatterBot(sg, msg, channel, sess, query);
return false; return true;
} }
var commandString = GetCommandString(model); var commandString = GetCommandString(model);
@ -293,7 +293,7 @@ public sealed class AiAssistantService
GetCommandErrorResult.RateLimitHit GetCommandErrorResult.RateLimitHit
=> "You've spent your daily requests quota.", => "You've spent your daily requests quota.",
GetCommandErrorResult.NotAuthorized GetCommandErrorResult.NotAuthorized
=> "In order to use this command you have to have a 5$ or higher subscription at <https://patreon.com/nadekobot>", => "In order to use this command you have to have a 5$ or higher subscription at <https://patreon.com/elliebot>",
GetCommandErrorResult.Unknown GetCommandErrorResult.Unknown
=> "The service is temporarily unavailable.", => "The service is temporarily unavailable.",
_ => throw new ArgumentOutOfRangeException() _ => throw new ArgumentOutOfRangeException()

View file

@ -8,7 +8,7 @@ public partial class Utility
private readonly IEnumerable<IConfigService> _settingServices; private readonly IEnumerable<IConfigService> _settingServices;
public ConfigCommands(IEnumerable<IConfigService> settingServices) public ConfigCommands(IEnumerable<IConfigService> settingServices)
=> _settingServices = settingServices.Where(x => x.Name != "medusa"); => _settingServices = settingServices.Where(x => x.Name != "marmalade");
[Cmd] [Cmd]
[OwnerOnly] [OwnerOnly]

View file

@ -459,47 +459,82 @@ public partial class Utility : EllieModule
public async Task StickerAdd(string name = null, string description = null, params string[] tags) public async Task StickerAdd(string name = null, string description = null, params string[] tags)
{ {
string format; string format;
Stream stream; Stream stream = null;
if (ctx.Message.Stickers.Count is 1 && ctx.Message.Stickers.First() is SocketSticker ss)
{
name ??= ss.Name;
description = ss.Description;
tags = tags is null or { Length: 0 } ? ss.Tags.ToArray() : tags;
format = FormatToExtension(ss.Format);
using var http = _httpFactory.CreateClient();
stream = await http.GetStreamAsync(ss.GetStickerUrl());
}
else
{
await Response().Error(strs.sticker_error).SendAsync();
return;
}
try try
{ {
if (tags.Length == 0) if (ctx.Message.Stickers.Count is 1 && ctx.Message.Stickers.First() is SocketSticker ss)
tags = [name]; {
name ??= ss.Name;
description = ss.Description;
tags = tags is null or { Length: 0 } ? ss.Tags.ToArray() : tags;
format = FormatToExtension(ss.Format);
await ctx.Guild.CreateStickerAsync( using var http = _httpFactory.CreateClient();
name, stream = await http.GetStreamAsync(ss.GetStickerUrl());
stream, }
$"{name}.{format}", else if (ctx.Message.Attachments.Count is 1 && name is not null)
tags, {
string.IsNullOrWhiteSpace(description) ? "Missing description" : description if (tags.Length == 0)
); tags = [name];
await ctx.OkAsync(); if (ctx.Message.Attachments.Count != 1)
} {
catch (Exception ex) await Response().Error(strs.sticker_error).SendAsync();
{ return;
Log.Warning(ex, "Error occurred while adding a sticker: {Message}", ex.Message); }
await Response().Error(strs.error_occured).SendAsync();
var attach = ctx.Message.Attachments.First();
if (attach.Size > 512_000 || attach.Width != 300 || attach.Height != 300)
{
await Response().Error(strs.sticker_error).SendAsync();
return;
}
format = attach.Filename
.Split('.')
.Last()
.ToLowerInvariant();
if (string.IsNullOrWhiteSpace(format) || (format != "png" && format != "apng"))
{
await Response().Error(strs.sticker_error).SendAsync();
return;
}
using var http = _httpFactory.CreateClient();
stream = await http.GetStreamAsync(attach.Url);
}
else
{
await Response().Error(strs.sticker_error).SendAsync();
return;
}
try
{
await ctx.Guild.CreateStickerAsync(
name,
stream,
$"{name}.{format}",
tags,
string.IsNullOrWhiteSpace(description) ? "Missing description" : description
);
await ctx.OkAsync();
}
catch
(Exception ex)
{
Log.Warning(ex, "Error occurred while adding a sticker: {Message}", ex.Message);
await Response().Error(strs.error_occured).SendAsync();
}
} }
finally finally
{ {
await stream.DisposeAsync(); await (stream?.DisposeAsync() ?? ValueTask.CompletedTask);
} }
} }

View file

@ -34,7 +34,7 @@ public sealed class Creds : IBotCredentials
Go to https://dashy.elliebot.net/me and login with your discord account Go to https://dashy.elliebot.net/me and login with your discord account
Go to the Keys page and click "Generate New Key" and copy it here Go to the Keys page and click "Generate New Key" and copy it here
You and anyone else with the permission to run `.prompt` command will be able to use natural language to run bot's commands. You and anyone else with the permission to run `.prompt` command will be able to use natural language to run bot's commands.
For example '@Bot how's the weather in Paris' will return the current weather in Paris as if you were to run `.weather Paris` command For example '@Bot how's the weather in Paris' will return the current weather in Paris as if you were to run `.weather Paris` command.
""")] """)]
public string EllieAiToken { get; set; } public string EllieAiToken { get; set; }

View file

@ -11,5 +11,5 @@ public abstract class DbService
public abstract Task SetupAsync(); public abstract Task SetupAsync();
public abstract DbContext CreateRawDbContext(string dbType, string connString); public abstract DbContext CreateRawDbContext(string dbType, string connString);
public abstract DbContext GetDbContext(); public abstract EllieContext GetDbContext();
} }

View file

@ -29,7 +29,8 @@ public class EllieInteractionService : IEllieInteractionService, IEService
Func<SocketMessageComponent, T, Task> onTrigger, Func<SocketMessageComponent, T, Task> onTrigger,
in T state, in T state,
bool singleUse = true, bool singleUse = true,
bool clearAfter = true) bool clearAfter = true
)
=> Create(userId, => Create(userId,
button, button,
((Func<T, Func<SocketMessageComponent, Task>>)((data) ((Func<T, Func<SocketMessageComponent, Task>>)((data)

View file

@ -10,7 +10,7 @@ public sealed class EllieButtonInteractionHandler : EllieInteractionBase
bool onlyAuthor, bool onlyAuthor,
bool singleUse = true, bool singleUse = true,
bool clearAfter = true) bool clearAfter = true)
: base(client, authorId, button.CustomId, onAction, onlyAuthor, singleUse) : base(client, authorId, button.CustomId, onAction, onlyAuthor, singleUse, clearAfter)
{ {
Button = button; Button = button;
} }

View file

@ -19,11 +19,11 @@ public class FilterAdapter : PreconditionAttribute
CommandInfo command, CommandInfo command,
IServiceProvider services) IServiceProvider services)
{ {
var medusaContext = ContextAdapterFactory.CreateNew(context, var marmaladeContext = ContextAdapterFactory.CreateNew(context,
_strings, _strings,
services); services);
var result = await _filterAttribute.CheckAsync(medusaContext); var result = await _filterAttribute.CheckAsync(marmaladeContext);
if (!result) if (!result)
return PreconditionResult.FromError($"Precondition '{_filterAttribute.GetType().Name}' failed."); return PreconditionResult.FromError($"Precondition '{_filterAttribute.GetType().Name}' failed.");

View file

@ -1,4 +1,4 @@
# DO NOT CHANGE # DO NOT CHANGE
version: 7 version: 7
# Bot token. Do not share with anyone ever -> https://discordapp.com/developers/applications/ # Bot token. Do not share with anyone ever -> https://discordapp.com/developers/applications/
token: "" token: ""
@ -18,6 +18,7 @@ totalShards: 1
# Go to the Keys page and click "Generate New Key" and copy it here # Go to the Keys page and click "Generate New Key" and copy it here
# You and anyone else with the permission to run `.prompt` command will be able to use natural language to run bot's commands. # You and anyone else with the permission to run `.prompt` command will be able to use natural language to run bot's commands.
# For example '@Bot how's the weather in Paris' will return the current weather in Paris as if you were to run `.weather Paris` command. # For example '@Bot how's the weather in Paris' will return the current weather in Paris as if you were to run `.weather Paris` command.
# ⚠ This does not currently work and is a work in progress.
ellieAiToken: ellieAiToken:
# Login to https://console.cloud.google.com, create a new project, go to APIs & Services -> Library -> YouTube Data API and enable it. # Login to https://console.cloud.google.com, create a new project, go to APIs & Services -> Library -> YouTube Data API and enable it.
# Then, go to APIs and Services -> Credentials and click Create credentials -> API key. # Then, go to APIs and Services -> Credentials and click Create credentials -> API key.

View file

@ -1192,6 +1192,8 @@ xpreset:
- xpreset - xpreset
bible: bible:
- bible - bible
quran:
- quran
edit: edit:
- edit - edit
delete: delete:

View file

@ -64,7 +64,7 @@ greetmsg:
desc: |- desc: |-
Sets a new join announcement message which will be shown in the current channel. Sets a new join announcement message which will be shown in the current channel.
Write `%user.mention%` if you want to mention the new member. Write `%user.mention%` if you want to mention the new member.
Full list of placeholders can be found here <https://docs.elliebot.net/ellie/placeholders/> Full list of placeholders can be found here <https://docs.elliebot.net/ellie/features/placeholders/>
Using this command with no message will show the current greet message. Using this command with no message will show the current greet message.
You can use embed json from <https://eb.elliebot.net/> instead of regular text, if you want the message to be embedded. You can use embed json from <https://eb.elliebot.net/> instead of regular text, if you want the message to be embedded.
ex: ex:
@ -82,7 +82,7 @@ byemsg:
desc: |- desc: |-
Sets a new leave announcement message which will be shown in the current channel. Sets a new leave announcement message which will be shown in the current channel.
Type `%user.name%` to show the name of the user who left. Type `%user.name%` to show the name of the user who left.
Full list of placeholders can be found here <https://docs.elliebot.net/ellie/placeholders/> Full list of placeholders can be found here <https://docs.elliebot.net/ellie/features/placeholders/>
Using this command with no message will show the current bye message. Using this command with no message will show the current bye message.
You can use embed json from <https://eb.elliebot.net/> instead of regular text, if you want the message to be embedded. You can use embed json from <https://eb.elliebot.net/> instead of regular text, if you want the message to be embedded.
ex: ex:
@ -138,7 +138,7 @@ boostmsg:
desc: |- desc: |-
Sets a new boost announcement message which will be shown in the current channel. Sets a new boost announcement message which will be shown in the current channel.
Type `%user.mention%` if you want to mention the booster. Type `%user.mention%` if you want to mention the booster.
Full list of placeholders can be found here <https://docs.elliebot.net/ellie/placeholders/> Full list of placeholders can be found here <https://docs.elliebot.net/ellie/features/placeholders/>
Using this command with no message will show the current boost message. Using this command with no message will show the current boost message.
You can use embed json from <https://eb.elliebot.net/> instead of regular text, if you want the message to be embedded. You can use embed json from <https://eb.elliebot.net/> instead of regular text, if you want the message to be embedded.
ex: ex:
@ -378,7 +378,7 @@ iamnot:
- role: - role:
desc: "The role to remove." desc: "The role to remove."
expradd: expradd:
desc: 'Add an expression with a trigger and a response. Bot will post a response whenever someone types the trigger word. Running this command in a server requires the Administrator permission. Running this command in DM is Bot Owner only and adds a new global expression. Guide [here](<https://docs.elliebot.net/ellie/expressions/>)' desc: 'Add an expression with a trigger and a response. Bot will post a response whenever someone types the trigger word. Running this command in a server requires the Administrator permission. Running this command in DM is Bot Owner only and adds a new global expression. Guide [here](<https://docs.elliebot.net/ellie/features/expressions/>)'
ex: ex:
- '"hello" Hi there %user.mention%' - '"hello" Hi there %user.mention%'
params: params:
@ -387,7 +387,7 @@ expradd:
response: response:
desc: "The text of the message that shows up when a user types the trigger word." desc: "The text of the message that shows up when a user types the trigger word."
expraddserver: expraddserver:
desc: 'Add an expression with a trigger and a response in this server. Bot will post a response whenever someone types the trigger word. This command is useful if you want to lower the permission requirement for managing expressions by using `{0}dpo`. Guide [here](<https://docs.elliebot.net/ellie/expressions/>).' desc: 'Add an expression with a trigger and a response in this server. Bot will post a response whenever someone types the trigger word. This command is useful if you want to lower the permission requirement for managing expressions by using `{0}dpo`. Guide [here](<https://docs.elliebot.net/ellie/features/expressions/>).'
ex: ex:
- '"hello" Hi there %user.mention%' - '"hello" Hi there %user.mention%'
params: params:
@ -2180,7 +2180,7 @@ delallquotes:
- keyword: - keyword:
desc: "The keyword to search for in the text." desc: "The keyword to search for in the text."
greetdmmsg: greetdmmsg:
desc: Sets a new join announcement message which will be sent to the user who joined. Type `%user.mention%` if you want to mention the new member. Using it with no message will show the current DM greet message. You can use embed json from <https://eb.elliebot.net/> instead of a regular text, if you want the message to be embedded. desc: Sets a new join announcement message which will be sent to the user who joined. Type `%user.mention%` if you want to mention the new member. Using it with no message will show the current DM greet message. You can use embed json from <https://eb.elliebot.net> instead of a regular text, if you want the message to be embedded.
ex: ex:
- Welcome to the server, %user.mention% - Welcome to the server, %user.mention%
params: params:
@ -2289,7 +2289,11 @@ emojiremove:
- emotes: - emotes:
desc: "The list of emojis to be removed from the server." desc: "The list of emojis to be removed from the server."
stickeradd: stickeradd:
desc: Adds the sticker from your message to this server. Send the sticker along with this command (in the same message). desc: |-
Adds the sticker from your message to this server.
Send the sticker along with this command (in the same message).
Alternatively you can upload an image along with this command but you have to specify the name.
The image must be 300x300 in .png or .apng format and up to 512KB in size.
ex: ex:
- '' - ''
- name "description" tag1 tag2 tagN - name "description" tag1 tag2 tagN
@ -4076,6 +4080,16 @@ bible:
desc: "The name of the biblical book being referenced." desc: "The name of the biblical book being referenced."
chapterAndVerse: chapterAndVerse:
desc: "The reference to a specific passage in the Bible, such as 'Genesis 3:15'" desc: "The reference to a specific passage in the Bible, such as 'Genesis 3:15'"
quran:
desc: |-
Shows the text of an ayah of the Quran, as well as the recitation by Alafasy.
Supply surah:ayah, or ayah number. For instance, 262 or 2:255 will both get you Ayat Al Kursi
ex:
- 2:255
- 262
params:
- ayah:
desc: "The number of the ayah in the Quran, for example 2:255."
edit: edit:
desc: Edits bot's message, you have to specify message ID and new text. You can optionally specify target channel. Supports embeds. desc: Edits bot's message, you have to specify message ID and new text. You can optionally specify target channel. Supports embeds.
ex: ex:
@ -4200,7 +4214,7 @@ marmaladeunload:
desc: "The name of a specific marmalade to be unloaded." desc: "The name of a specific marmalade to be unloaded."
marmaladeinfo: marmaladeinfo:
desc: |- desc: |-
Shows information about the specified marmalade such as the author, name, description, list of sneks, number of commands etc. Shows information about the specified marmalade such as the author, name, description, list of canaries, number of commands etc.
Provide no name to see the basic information about all loaded marmalades. Provide no name to see the basic information about all loaded marmalades.
Read about the marmalade system [here](https://docs.elliebot.net/ellie/marmalade/creating-a-marmalade/) Read about the marmalade system [here](https://docs.elliebot.net/ellie/marmalade/creating-a-marmalade/)
ex: ex:

View file

@ -1067,8 +1067,7 @@
"xpshop_already_owned": "You already own this item.", "xpshop_already_owned": "You already own this item.",
"xpshop_item_not_found": "An item with that key doesn't exist.", "xpshop_item_not_found": "An item with that key doesn't exist.",
"xpshop_website": "You can see the list of all Xp Shop items here: <https://beta.elliebot.net>", "xpshop_website": "You can see the list of all Xp Shop items here: <https://beta.elliebot.net>",
"sticker_invalid_size": "Stickers must be exactly 300x300 pixels.", "sticker_error": "You must either send a sticker along with this command, or upload a 300x300 .png or .apng image. Up to 512KB in size.",
"sticker_error": "You must either send a sticker along with this command, or upload a 300x300 .png or .apng image.",
"sticker_missing_name": "Please specify a name for the sticker.", "sticker_missing_name": "Please specify a name for the sticker.",
"thread_deleted": "Thread Deleted", "thread_deleted": "Thread Deleted",
"thread_created": "Thread Created", "thread_created": "Thread Created",