Compare commits

...

8 Commits
5.1.2 ... 5.1.3

11 changed files with 219 additions and 68 deletions

View File

@@ -2,6 +2,22 @@
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] - 06.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 nadeko 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
## [5.1.2] - 29.06.2024 ## [5.1.2] - 29.06.2024
### Fixed ### Fixed

View File

@@ -96,6 +96,8 @@ public class ChatterBotService : IExecOnMessage
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 == nadekoId)
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 NadekoBot.Modules.Games.Common.ChatterBot; namespace NadekoBot.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";
@@ -45,7 +47,7 @@ public class OfficialGptSession : IChatterBotSession
_maxHistory = chatHistory; _maxHistory = chatHistory;
_maxTokens = maxTokens; _maxTokens = maxTokens;
_minTokens = minTokens; _minTokens = minTokens;
_nadekoUsername = nadekoUsername; _nadekoUsername = UsernameCleaner().Replace(nadekoUsername, "");
_encoding = GptEncoding.GetEncodingForModel(_model); _encoding = GptEncoding.GetEncodingForModel(_model);
messages.Add(new() messages.Add(new()
{ {
@@ -55,14 +57,21 @@ public class OfficialGptSession : IChatterBotSession
}); });
} }
[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);

View File

@@ -0,0 +1,102 @@
using NadekoBot.Modules.Searches.Common;
using System.Net.Http.Json;
using System.Text.Json.Serialization;
namespace NadekoBot.Modules.Searches;
public partial class Searches
{
public partial class ReligiousCommands : NadekoModule
{
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 : NadekoModule<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

@@ -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);

View File

@@ -459,47 +459,82 @@ public partial class Utility : NadekoModule
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

@@ -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.1</Version> <Version>5.1.3</Version>
<!-- Output/build --> <!-- Output/build -->
<RunWorkingDirectory>$(MSBuildProjectDirectory)</RunWorkingDirectory> <RunWorkingDirectory>$(MSBuildProjectDirectory)</RunWorkingDirectory>

View File

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

View File

@@ -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:

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://xpshop.nadeko.bot>", "xpshop_website": "You can see the list of all Xp Shop items here: <https://xpshop.nadeko.bot>",
"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",