1
0
Fork 1
mirror of https://github.com/TeamOctolings/Octobot.git synced 2025-04-20 00:43:36 +03:00

Merge branch 'master' into profile-editremind

Signed-off-by: Macintxsh <95250141+mctaylors@users.noreply.github.com>
This commit is contained in:
Macintxsh 2024-01-28 18:39:34 +03:00 committed by GitHub
commit bdef465a41
Signed by: GitHub
GPG key ID: B5690EEEBB952194
15 changed files with 164 additions and 35 deletions

4
.github/CODEOWNERS vendored
View file

@ -1,2 +1,2 @@
* @LabsDevelopment/octobot * @TeamOctolings/octobot
/docs/ @LabsDevelopment/octobot-docs /docs/ @TeamOctolings/octobot-docs

View file

@ -23,7 +23,7 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: ReSharper CLI InspectCode - name: ReSharper CLI InspectCode
uses: muno92/resharper_inspectcode@1.11.1 uses: muno92/resharper_inspectcode@1.11.5
with: with:
solutionPath: ./Octobot.sln solutionPath: ./Octobot.sln
ignoreIssueType: InvertIf, ConvertIfStatementToSwitchStatement, ConvertToPrimaryConstructor ignoreIssueType: InvertIf, ConvertIfStatementToSwitchStatement, ConvertToPrimaryConstructor

View file

@ -9,11 +9,11 @@
<Title>Octobot</Title> <Title>Octobot</Title>
<Authors>Octol1ttle, mctaylors, neroduckale</Authors> <Authors>Octol1ttle, mctaylors, neroduckale</Authors>
<Copyright>AGPLv3</Copyright> <Copyright>AGPLv3</Copyright>
<PackageProjectUrl>https://github.com/LabsDevelopment/Octobot</PackageProjectUrl> <PackageProjectUrl>https://github.com/TeamOctolings/Octobot</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/LabsDevelopment/Octobot/blob/master/LICENSE</PackageLicenseUrl> <PackageLicenseUrl>https://github.com/TeamOctolings/Octobot/blob/master/LICENSE</PackageLicenseUrl>
<RepositoryUrl>https://github.com/LabsDevelopment/Octobot</RepositoryUrl> <RepositoryUrl>https://github.com/TeamOctolings/Octobot</RepositoryUrl>
<RepositoryType>github</RepositoryType> <RepositoryType>github</RepositoryType>
<Company>LabsDevelopment</Company> <Company>TeamOctolings</Company>
<NeutralLanguage>en</NeutralLanguage> <NeutralLanguage>en</NeutralLanguage>
<Description>A general-purpose Discord bot for moderation written in C#</Description> <Description>A general-purpose Discord bot for moderation written in C#</Description>
<ApplicationIcon>docs/octobot.ico</ApplicationIcon> <ApplicationIcon>docs/octobot.ico</ApplicationIcon>

View file

@ -29,7 +29,7 @@ While pull requests from unaffiliated contributors are welcome, please note that
internal issues that haven't been published to the issue tracker yet. Reviewing PRs is done on a best-effort basis, so internal issues that haven't been published to the issue tracker yet. Reviewing PRs is done on a best-effort basis, so
please be aware that it may take a while before a core maintainer gets around to review your change. please be aware that it may take a while before a core maintainer gets around to review your change.
The [issue tracker](https://github.com/LabsDevelopment/Octobot/issues) should provide plenty of issues to start with. The [issue tracker](https://github.com/TeamOctolings/Octobot/issues) should provide plenty of issues to start with.
Make sure to check that an issue you're planning to resolve does not already have people working on it and that there Make sure to check that an issue you're planning to resolve does not already have people working on it and that there
are no PRs associated with it are no PRs associated with it

View file

@ -1,12 +1,12 @@
<p align="center"> <p align="center">
<img src="https://cdn.mctaylors.ru/octobot-banner.png" alt="Octobot banner"/> <img src="octobot-banner.png" alt="Octobot banner"/>
</p> </p>
<a href="https://github.com/LabsDevelopment/Octobot/blob/master/LICENSE"><img src="https://img.shields.io/github/license/LabsDevelopment/Octobot?logo=git"></img></a> <a href="https://github.com/TeamOctolings/Octobot/blob/master/LICENSE"><img src="https://img.shields.io/github/license/TeamOctolings/Octobot?logo=git"></img></a>
<a href="https://github.com/Remora/Remora.Discord"><img src="https://img.shields.io/badge/powered_by-Remora.Discord-blue"></img></a> <a href="https://github.com/Remora/Remora.Discord"><img src="https://img.shields.io/badge/powered_by-Remora.Discord-blue"></img></a>
<a href="https://github.com/LabsDevelopment/Octobot/commit/master"><img src="https://img.shields.io/github/last-commit/LabsDevelopment/Octobot?logo=github"></img></a> <a href="https://github.com/TeamOctolings/Octobot/commit/master"><img src="https://img.shields.io/github/last-commit/TeamOctolings/Octobot?logo=github"></img></a>
Veemo! I'm a general-purpose bot for moderation (formerly known as Boyfriend) written by [Labs Development Team](https://github.com/LabsDevelopment) in C# and Remora.Discord Veemo! I'm a general-purpose bot for moderation (formerly known as Boyfriend) written by [Team Octolings](https://github.com/TeamOctolings) in C# and Remora.Discord
## Features ## Features
@ -19,25 +19,13 @@ Veemo! I'm a general-purpose bot for moderation (formerly known as Boyfriend) wr
*...a-a-and more!* *...a-a-and more!*
[//]: # (if you are reading this, message @mctaylors and ask him to bring back the wiki)
## Invite Octobot <a href="https://github.com/LabsDevelopment/Octobot/deployments/production"><img src="https://img.shields.io/github/actions/workflow/status/LabsDevelopment/Octobot/.github/workflows/build-push.yml?logo=github&label=production"></img></a>
Did you know that Octobot is a public bot? You can invite it to your server and use it without building it!
<p align="center">
<a href="https://discord.com/api/oauth2/authorize?client_id=855023234407333888&permissions=1383382133894&scope=bot%20applications.commands"><img src="https://cdn.mctaylors.ru/discord-add-app.png"></img></a>
</p>
> [!IMPORTANT]
> The bot will not be able to respond in private channels unless you have configured permissions for the bot in those channels.
## Building Octobot ## Building Octobot
1. Install [.NET 8 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/8.0) 1. Install [.NET 8 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)
2. Go to the [Discord Developer Portal](https://discord.com/developers), create a new application and get a bot token. Don't forget to also enable all intents! 2. Go to the [Discord Developer Portal](https://discord.com/developers), create a new application and get a bot token. Don't forget to also enable all intents!
3. Clone this repository and open `Octobot` folder. 3. Clone this repository and open `Octobot` folder.
``` ```
git clone https://github.com/LabsDevelopment/Octobot git clone https://github.com/TeamOctolings/Octobot
cd Octobot cd Octobot
``` ```
4. Run Octobot using `dotnet` with `BOT_TOKEN` variable. 4. Run Octobot using `dotnet` with `BOT_TOKEN` variable.

BIN
docs/octobot-banner.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

View file

@ -591,4 +591,7 @@
<data name="UserInfoKicked" xml:space="preserve"> <data name="UserInfoKicked" xml:space="preserve">
<value>Kicked</value> <value>Kicked</value>
</data> </data>
<data name="ReminderEdited" xml:space="preserve">
<value>Reminder edited</value>
</data>
</root> </root>

View file

@ -591,4 +591,7 @@
<data name="UserInfoKicked" xml:space="preserve"> <data name="UserInfoKicked" xml:space="preserve">
<value>Выгнан</value> <value>Выгнан</value>
</data> </data>
<data name="ReminderEdited" xml:space="preserve">
<value>Напоминание отредактировано</value>
</data>
</root> </root>

View file

@ -591,4 +591,7 @@
<data name="UserInfoKicked" xml:space="preserve"> <data name="UserInfoKicked" xml:space="preserve">
<value>кикнут</value> <value>кикнут</value>
</data> </data>
<data name="ReminderEdited" xml:space="preserve">
<value>напоминалка подправлена</value>
</data>
</root> </root>

View file

@ -115,7 +115,7 @@ public class AboutCommandGroup : CommandGroup
.WithSmallTitle(string.Format(Messages.AboutBot, bot.Username), bot) .WithSmallTitle(string.Format(Messages.AboutBot, bot.Username), bot)
.WithDescription(builder.ToString()) .WithDescription(builder.ToString())
.WithColour(ColorsList.Cyan) .WithColour(ColorsList.Cyan)
.WithImageUrl("https://cdn.mctaylors.ru/octobot-banner.png") .WithImageUrl("https://i.ibb.co/fS6wZhh/octobot-banner.png")
.Build(); .Build();
var repositoryButton = new ButtonComponent( var repositoryButton = new ButtonComponent(

View file

@ -57,7 +57,7 @@ public class BanCommandGroup : CommandGroup
/// A slash command that bans a Discord user with the specified reason. /// A slash command that bans a Discord user with the specified reason.
/// </summary> /// </summary>
/// <param name="target">The user to ban.</param> /// <param name="target">The user to ban.</param>
/// <param name="stringDuration">The duration for this ban. The user will be automatically unbanned after this duration.</param> /// <param name="duration">The duration for this ban. The user will be automatically unbanned after this duration.</param>
/// <param name="reason"> /// <param name="reason">
/// The reason for this ban. Must be encoded with <see cref="StringExtensions.EncodeHeader" /> when passed to /// The reason for this ban. Must be encoded with <see cref="StringExtensions.EncodeHeader" /> when passed to
/// <see cref="IDiscordRestGuildAPI.CreateGuildBanAsync" />. /// <see cref="IDiscordRestGuildAPI.CreateGuildBanAsync" />.
@ -79,8 +79,7 @@ public class BanCommandGroup : CommandGroup
[Description("User to ban")] IUser target, [Description("User to ban")] IUser target,
[Description("Ban reason")] [MaxLength(256)] [Description("Ban reason")] [MaxLength(256)]
string reason, string reason,
[Description("Ban duration")] [Option("duration")] [Description("Ban duration")] string? duration = null)
string? stringDuration = null)
{ {
_profiler.Push("ban_command"); _profiler.Push("ban_command");
_profiler.Push("preparation"); _profiler.Push("preparation");
@ -120,7 +119,7 @@ public class BanCommandGroup : CommandGroup
Messages.Culture = GuildSettings.Language.Get(data.Settings); Messages.Culture = GuildSettings.Language.Get(data.Settings);
_profiler.Pop(); _profiler.Pop();
if (stringDuration is null) if (duration is null)
{ {
_profiler.Pop(); _profiler.Pop();
return _profiler.ReportWithResult(await BanUserAsync(executor, target, reason, null, guild, data, channelId, return _profiler.ReportWithResult(await BanUserAsync(executor, target, reason, null, guild, data, channelId,
@ -128,8 +127,8 @@ public class BanCommandGroup : CommandGroup
CancellationToken)); CancellationToken));
} }
var parseResult = TimeSpanParser.TryParse(stringDuration); var parseResult = TimeSpanParser.TryParse(duration);
if (!parseResult.IsDefined(out var duration)) if (!parseResult.IsDefined(out var timeSpan))
{ {
_profiler.Push("invalid_timespan_send"); _profiler.Push("invalid_timespan_send");
var failedEmbed = new EmbedBuilder() var failedEmbed = new EmbedBuilder()
@ -142,7 +141,7 @@ public class BanCommandGroup : CommandGroup
} }
_profiler.Pop(); _profiler.Pop();
return _profiler.ReportWithResult(await BanUserAsync(executor, target, reason, duration, guild, data, channelId, return _profiler.ReportWithResult(await BanUserAsync(executor, target, reason, timeSpan, guild, data, channelId,
bot, CancellationToken)); bot, CancellationToken));
} }

View file

@ -236,6 +236,132 @@ public class RemindCommandGroup : CommandGroup
return _profiler.PopWithResult(await _feedback.SendContextualEmbedResultAsync(embed, ct: ct)); return _profiler.PopWithResult(await _feedback.SendContextualEmbedResultAsync(embed, ct: ct));
} }
public enum Parameters
{
[UsedImplicitly] Time,
[UsedImplicitly] Text
}
/// <summary>
/// A slash command that edits a scheduled reminder using the specified text or time.
/// </summary>
/// <param name="position">The list position of the reminder to edit.</param>
/// <param name="parameter">The reminder's parameter to edit.</param>
/// <param name="value">The new value for the reminder as a text or time.</param>
/// <returns>A feedback sending result which may or may not have succeeded.</returns>
[Command("editremind")]
[Description("Edit a reminder")]
[DiscordDefaultDMPermission(false)]
[RequireContext(ChannelContext.Guild)]
[UsedImplicitly]
public async Task<Result> ExecuteEditReminderAsync(
[Description("Position in list")] [MinValue(1)]
int position,
[Description("Parameter to edit")] Parameters parameter,
[Description("Parameter's new value")] string value)
{
if (!_context.TryGetContextIDs(out var guildId, out _, out var executorId))
{
return new ArgumentInvalidError(nameof(_context), "Unable to retrieve necessary IDs from command context");
}
var botResult = await _userApi.GetCurrentUserAsync(CancellationToken);
if (!botResult.IsDefined(out var bot))
{
return Result.FromError(botResult);
}
var executorResult = await _userApi.GetUserAsync(executorId, CancellationToken);
if (!executorResult.IsDefined(out var executor))
{
return Result.FromError(executorResult);
}
var data = await _guildData.GetData(guildId, CancellationToken);
Messages.Culture = GuildSettings.Language.Get(data.Settings);
var memberData = data.GetOrCreateMemberData(executor.ID);
if (parameter is Parameters.Time)
{
return await EditReminderTimeAsync(position - 1, value, memberData, bot, executor, CancellationToken);
}
return await EditReminderTextAsync(position - 1, value, memberData, bot, executor, CancellationToken);
}
private async Task<Result> EditReminderTimeAsync(int index, string value, MemberData data,
IUser bot, IUser executor, CancellationToken ct = default)
{
if (index >= data.Reminders.Count)
{
var failedEmbed = new EmbedBuilder().WithSmallTitle(Messages.InvalidReminderPosition, bot)
.WithColour(ColorsList.Red)
.Build();
return await _feedback.SendContextualEmbedResultAsync(failedEmbed, ct: ct);
}
var parseResult = TimeSpanParser.TryParse(value);
if (!parseResult.IsDefined(out var timeSpan))
{
var failedEmbed = new EmbedBuilder()
.WithSmallTitle(Messages.InvalidTimeSpan, bot)
.WithColour(ColorsList.Red)
.Build();
return await _feedback.SendContextualEmbedResultAsync(failedEmbed, ct: ct);
}
var oldReminder = data.Reminders[index];
var remindAt = DateTimeOffset.UtcNow.Add(timeSpan);
data.Reminders.Add(oldReminder with { At = remindAt });
data.Reminders.RemoveAt(index);
var builder = new StringBuilder()
.AppendBulletPointLine(string.Format(Messages.ReminderText, Markdown.InlineCode(oldReminder.Text)))
.AppendBulletPoint(string.Format(Messages.ReminderTime, Markdown.Timestamp(remindAt)));
var embed = new EmbedBuilder().WithSmallTitle(
string.Format(Messages.ReminderEdited, executor.GetTag()), executor)
.WithDescription(builder.ToString())
.WithColour(ColorsList.Cyan)
.WithFooter(string.Format(Messages.ReminderPosition, data.Reminders.Count))
.Build();
return await _feedback.SendContextualEmbedResultAsync(embed, ct: ct);
}
private async Task<Result> EditReminderTextAsync(int index, string value, MemberData data,
IUser bot, IUser executor, CancellationToken ct = default)
{
if (index >= data.Reminders.Count)
{
var failedEmbed = new EmbedBuilder().WithSmallTitle(Messages.InvalidReminderPosition, bot)
.WithColour(ColorsList.Red)
.Build();
return await _feedback.SendContextualEmbedResultAsync(failedEmbed, ct: ct);
}
var oldReminder = data.Reminders[index];
data.Reminders.Add(oldReminder with { Text = value });
data.Reminders.RemoveAt(index);
var builder = new StringBuilder()
.AppendBulletPointLine(string.Format(Messages.ReminderText, Markdown.InlineCode(value)))
.AppendBulletPoint(string.Format(Messages.ReminderTime, Markdown.Timestamp(oldReminder.At)));
var embed = new EmbedBuilder().WithSmallTitle(
string.Format(Messages.ReminderEdited, executor.GetTag()), executor)
.WithDescription(builder.ToString())
.WithColour(ColorsList.Cyan)
.WithFooter(string.Format(Messages.ReminderPosition, data.Reminders.Count))
.Build();
return await _feedback.SendContextualEmbedResultAsync(embed, ct: ct);
}
/// <summary> /// <summary>
/// A slash command that deletes a reminder using its list position. /// A slash command that deletes a reminder using its list position.
/// </summary> /// </summary>

View file

@ -1052,5 +1052,11 @@ namespace Octobot {
return ResourceManager.GetString("UserInfoKicked", resourceCulture); return ResourceManager.GetString("UserInfoKicked", resourceCulture);
} }
} }
internal static string ReminderEdited {
get {
return ResourceManager.GetString("ReminderEdited", resourceCulture);
}
}
} }
} }

View file

@ -28,7 +28,7 @@ namespace Octobot;
public sealed class Octobot public sealed class Octobot
{ {
public const string RepositoryUrl = "https://github.com/LabsDevelopment/Octobot"; public const string RepositoryUrl = "https://github.com/TeamOctolings/Octobot";
public const string IssuesUrl = $"{RepositoryUrl}/issues"; public const string IssuesUrl = $"{RepositoryUrl}/issues";
public static readonly AllowedMentions NoMentions = new( public static readonly AllowedMentions NoMentions = new(

View file

@ -28,9 +28,10 @@ public class MessageCreateResponder : IResponder<IMessageCreate>
"whoami" => "`nobody`", "whoami" => "`nobody`",
"сука !!" => "`root`", "сука !!" => "`root`",
"воооо" => "`removing /...`", "воооо" => "`removing /...`",
"пон" => "https://cdn.upload.systems/uploads/2LNfUSwM.jpg", "пон" => "https://i.ibb.co/Kw6QVcw/parry.jpg",
"++++" => "#", "++++" => "#",
"осу" => "https://github.com/ppy/osu", "осу" => "https://github.com/ppy/osu",
"лан" => "https://i.ibb.co/VYH2QLc/lan.jpg",
_ => default(Optional<string>) _ => default(Optional<string>)
}); });
return Task.FromResult(Result.FromSuccess()); return Task.FromResult(Result.FromSuccess());