In this PR, I have added StringBuilder extensions to avoid `.Append`
reuse such as `.Append("- ").AppendLine()`
Closes #205
---------
Signed-off-by: mctaylors <cantsendmails@mctaylors.ru>
Closes #183
In addition to the fix, now no error will be returned if the message
doesn't exist in the cache as that is a (relatively) normal occurrence
and isn't an indicator of an issue with the bot or its configuration
This PR fixes an issue that is currently causing CI to fail in all pull
requests:
`Notice: "[CA1854] Prefer a 'TryGetValue' call over a Dictionary indexer
access guarded by a 'ContainsKey' check to avoid double lookup" on
/home/runner/work/Octobot/Octobot/src/Services/Update/ScheduledEventUpdateService.cs(107,4168)`
The issue is resolved by following the advice mentioned in the notice.
This PR fixes an issue that caused REST errors to occur in
MemberUpdateService if the bot tries to interact with a member it can't
interact with. The issue is fixed by returning from TickMemberDataAsync
early if the member cannot be interacted with. An error message was
planned, but it requires adding a lot of services and severely
increasing the complexity. Contributors may feel free to add one if they
deem so necessary.
This PR replaces usages of the `FeedbackService` implementation with the
`IFeedbackService` interface. Using concrete implementations breaks the
whole point of dependency injection, so it doesn't make sense to use
them
Previously, any errors in guild data load will cause the bot to be
unusable in that guild. It didn't help that the end users had no
information that something was wrong! Now, any errors will be logged
better (with the full path to the file that couldn't be loaded), and the
users will receive a message saying that functionality is degraded
The old way to save objects was to serialize them directly into streams
opened by `File#Create`. This can cause problems if the serialization
isn't completed, because `File#Create` overwrites the file with an empty
one on the spot. Now, objects are first deserialized into a temporary
file, then the original is replaced by the temporary, then the temporary
is deleted.
Errors during guild data load would sometimes cause the bot to replace
the corrupted file with a default one whenever a save is triggered. Now,
guilds with load errors won't have their data saved to aid in debugging
---------
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
Signed-off-by: mctaylors <mctaylxrs@outlook.com>
With this PR, whenever command execution fails, the user will get an
error message with details of the error that can be passed on to
developers
An unrelated minor change: errors caused by task cancellations will no
longer be logged
---------
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
Signed-off-by: mctaylors <mctaylxrs@outlook.com>
_There are times when you want to be sure of what you've destroyed._
Therefore, in this PR I added the output of the text of the deleted
reminder along with its position in the list, because you can make a
mistake with deleting a reminder and forget about what you needed to be
reminded about.
---------
Signed-off-by: mctaylors <mctaylxrs@outlook.com>
This PR fixes an issue that prevented scheduled event status updates
from running again if they failed. This happened because, before each
update, the events would be synchronized. The `ScheduleOnStatusUpdated`
field would be set even if it's already `true`, which will cause it to
be set to `false` for unsuccessful updates. The issue is fixed by
surrounding the field set call with a condition that will prevent
setting the field from `true` to `false`
Closes #164
This PR adds an optional argument to `/clear` - `author` of type User.
If the user is specified, only messages sent by that user will be
cleared. Simple as that.
Closes #163
Discord's API sucks a lot. You ask it for a member, but it won't give
you a member. This is why this PR updates the
`GetEventNotificationMentions` method used to determine what roles and
users should get pinged for a scheduled event. Previously, the bot asked
Discord to provide the member for each subscriber to determine whether
or not they have the event notification role (to avoid pinging people
personally when the role would already ping them). With this pull
request, the bot uses MemberData, it's own member storage, for that
purpose (if you're wondering why, refer to the first two sentences)
Closes #124
Before we dive into this PR, let's explain a few rules of serialization
and deserialization in System.Text.Json (often referred as STJ).
The important rule of serialization is that fields are ***not***
serialized unless the serializer gets passed an instance of
`JsonSerializerOptions` that explicitly tells it to include fields.
However, properties are serialized by default.
The important rule of ***de***serialization is that class members are
only deserialized if:
1) In case of properties, they must have a setter
2) If they do not have a setter, the constructor must have an argument,
required or optional, that will act as the setter for that property.
Unfortunately, both of these rules were ignored in some commit that
refactored reminders. This PR is here to fix that issue by:
1) Converting fields in `Reminder.cs` to properties
2) Adding an optional argument to the `MemberData` constructor
This PR fixes an issue that caused syntax highlighting to be applied to
incorrect lines in the message edit log. The fix is to prepend a
zero-width space before every line of input text. This prevents Discord
from highlighting the line when it wasn't edited
This PR splits the extension methods contained in `Extensions.cs` into
separate classes in the `Octobot.Extensions` namespace. This was done
for multiple reasons:
1) The `Extensions.cs` violates SRP (Single Responsibility Principle) -
it takes upon itself every extension method for many types
2) Having a separate class for each extended type is a standard practice
- take a look at
[Remora.Discord](https://github.com/Remora/Remora.Discord/tree/main/Backend/Remora.Discord.Rest/Extensions)
or [osu!](https://github.com/ppy/osu/tree/master/osu.Game/Extensions)
3) Having all extension methods in one file makes it hard to find the
method you want
In this PR, I added `Markdown.Sanitize` for every `scheduledEvent.Name`
and `eventData.Name`. That means, every title in scheduled event embeds
will be sanitized.
I did that because in scheduled event list event title displays only as
sanitized string.
Signed-off-by: Macintosh II <mctaylxrs@outlook.com>
note: there are still instances of `IUser user` because I could not find
a better name for them
---------
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
Updates in /random:
- Set default minimum number to 0.
- Show maximum & minimum numbers.
- Recolor & display a message when user tries to use exact same number in first and second fields for some reason.
- Mention user in small title.
- Automatically detect max & min numbers.
- Add `long` support.
- Show what default number is.
---------
Signed-off-by: Macintosh II <95250141+mctaylors@users.noreply.github.com>
Signed-off-by: Apceniy <53149450+Apceniy@users.noreply.github.com>
Signed-off-by: Macintosh II <mctaylxrs@outlook.com>
Co-authored-by: Apceniy <53149450+Apceniy@users.noreply.github.com>
I added a string interpolation for SongUpdateService for
better activity status configuration. So now you can add some characters
if you want, or even swap the song title and author.
Also songs are now displaying in Splatoon™ style.
---------
Signed-off-by: Macintosh II <95250141+mctaylors@users.noreply.github.com>
This PR moves all code related to synchronization of scheduled events to
ScheduledEventUpdateService. Just like #130, this reduces possible
points of failures, maintenance burden and reliance on gateway events
This PR makes it so that roles in MemberData are updated only in
MemberUpdateService. This reduces possible points of failures,
maintenance burden and reliance on gateway events
Because the Discord API sucks at providing data when needed, this PR
makes it so that events are synchronized every tick instead of on
gateway events. This fixes an issue where the bot won't know about a
scheduled event if it was created before the bot was started
We're moving!
---------
Signed-off-by: Macintosh II <mctaylxrs@outlook.com>
Signed-off-by: Macintosh II <95250141+mctaylors@users.noreply.github.com>
Co-authored-by: Octol1ttle <l1ttleofficial@outlook.com>
Signed-off-by: Macintosh II <mctaylxrs@outlook.com>
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
Co-authored-by: Octol1ttle <l1ttleofficial@outlook.com>
Flipping `>` to `<=` changed null handling semantics within the
operator, causing the unban/unmute code to always run
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
If a user was muted using the `MuteRole` method and then banned, the
UnknownMember warning will flood your logs when `DateTimeOffset.UtcNow >
data.MutedUntil` becomes true, because there is no user in the server to
unmute.
---------
Signed-off-by: Macintosh II <95250141+mctaylors@users.noreply.github.com>
Signed-off-by: Macintosh II <mctaylxrs@outlook.com>
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
Co-authored-by: Octol1ttle <l1ttleofficial@outlook.com>
Signed-off-by: Macintosh II <mctaylxrs@outlook.com>
Signed-off-by: Macintosh II <95250141+mctaylors@users.noreply.github.com>
Co-authored-by: Octol1ttle <l1ttleofficial@outlook.com>
- Added support for `MuteRole`, now if you add any role to this setting,
then try to mute a member, all his roles will be removed except for the
one you set in this setting.
- Fixed `/unmute`, that tried to set target's display name to unmute
reason.
---------
Signed-off-by: Macintosh II <mctaylxrs@outlook.com>
This PR closes #105
This PR fixes exceptions caused by changing a collection's contents
while it is being enumerated. This can often happen with Guild- and
MemberDatas. By using `ToArray()` on these global collections and using
it in the `foreach` loop, we create a new copy of the collection,
preventing any modification to it.
While this does introduce a lot of memory allocations, there is no
fixing that. Usually, the fix to these exceptions would be to convert
the `foreach` to a reverse-`for`. However, because indices cannot be
used on these collections, that is not possible.
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
This PR fixes an issue that caused guild data JSON files to be corrupted
upon saving. As it turns out `File.OpenWrite(string)` does not clear the
file before writing to it. That means, if a file contains `{"MyKey":
"MyValue"}` and I write `{}` to it using `File.OpenWrite(string)`, the
contents of the file will be `{}MyKey": "MyValue"}`. This is a malformed
JSON and will cause an error upon next bot startup.
In addition, this PR blacklists the `File.OpenWrite` method using
`CodeAnalysis/BannedSymbols.txt` to prevent its accidental use in the
future.
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
This PR adds autocomplete for setting keys in `/editsetting` slash
command. The usage of options provided by auto-complete is enforced
client-side.
Closes #97
Closes #95
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
This PR adds a BackgroundGuildDataSaverService which will save all guild
data to disk every 5 minutes using a PeriodicTimer.
Closes #96
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
This PR renames commands `/settingslist` and `/settings` to
`/listsettings` and `/editsettings` respectively. This helps avoid
confusion and accidental use of the wrong command while conforming to a
common naming style, similar to remind commands.
cc @mctaylors wiki needs updating
---------
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
This PR fixes an exception that would occur when deserialization of
ScheduledEventData would be attempted. The exception is fixed by
providing a constructor containing all properties and adding the
`[JsonConstructor]` attribute.
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
This PR closes #85.
This PR fixes many issues related to scheduled events. Most importantly,
scheduled events that are no longer present in the guild, but still have
data related to them, won't be left rotting.
This requires deletion of `ScheduledEvents.json` files in all guilds.
Maybe I'll start writing datafixers one day...
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
Without this embed, if there are no reminders created by the user, the
bot will endlessly think because StringBuilder will be empty and normal
embed will not be shown.
This PR fixes EventEarlyNotification timestamp not displaying correctly
and also has some other changes:
- Add xmldocs for `/settingslist`'s `page` option in
SettingsCommandGroup.cs
- Add option description for `index` for `/delreminder`
- Some corrections of grammatical errors in Messages.tt-ru.resx
- Fix `ArgumentOutOfRangeException` in `/settingslist` that appears if
the user uses a negative integer in `page`
---------
Signed-off-by: Macintosh II <95250141+mctaylors@users.noreply.github.com>
Co-authored-by: Octol1ttle <l1ttleofficial@outlook.com>
This PR fixes a fatal crash that occurs when the bot tries to send an
early notification for a scheduled event. An exception is thrown by
`string#Format` when the argument list provided doesn't match the
argument list in the template. This is fixed by correcting the template
and the argument list
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
This PR closes #61.
This PR adds the `/listremind` command and the `/delremind` command. The
`/delremind` command requires an index to determine which reminder to
delete.
cc @mctaylors review embed design and feature experience
---------
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
This PR fixes the following bugs in ScheduledEventUpdateService:
- When a scheduled event is first added into ScheduledEventData, its
status update code will be skipped. This results in some messages not
being sent to the EventNotificationChannel
- When the status update code returns an unsuccessful Result, the status
in ScheduledEventData will still be updated, meaning that the code will
not retry.
---------
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
GuildUpdateService is a service that contains way too many
responsibilities with everything strictly coupled to each other. The
code is buggy, hard to refactor and swallows errors. This prompted me to
make this PR, which splits it into three independant services:
- SongUpdateService (responsible for changing songs presence);
- MemberUpdateService (responsible for updating member datas: unbanning
users, adding the default role, sending reminders, filtering nicknames);
- ScheduledEventUpdateService (responsible for updating scheduled
events: sending notifications, automatically starting events).
All of these services and their methods use Results to push errors all
the way up in the stack, making sure no error is missed. To make logging
and debugging easier, an extension method for `ILogger` was created -
`LogResult`. The method checks if the result was successful or if its
failure was caused by a user or environment error before logging
anything - providing cleaner code and logs. `ExceptionError`s will also
have their exception stacktrace and type logged (except in Remora code).
This PR also fixes an issue that prevented banned users from being
unbanned when their punishment was over.
---------
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
This PR backfills member data when a guild is loaded or a new member
joins it. The reason for that is some actions that happen on member tick
(default role grant, nickname filtering) would only occur if a member
had data related to them (due to being banned or setting a reminder). In
addition, the `.editorconfig` was updated with new inspections provided
by a new release of Rider, 2023.2
See explanations for some changes in comments.
---------
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
This PR fixes an issue where, if the `PublicFeedbackChannel` wasn't set
or the welcome message was disabled, `GuildMemberJoinedResponder` would
`return` early, causing roles to not be granted back if
`ReturnRolesOnRejoin` was enabled, by moving the `if return` after the
code that grants back roles
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
*I'll start working on features and bugfixes after this PR, I promise*
very short summary:
- no more braceless statements
- braces are on new lines now
- `sealed` on everything that can be `sealed`
- no more awkwardly looking alignment of fields/parameters
- no more `Service` suffix on service fields. yeah.
- no more `else`s. who needs them?
- code style is now enforced by CI
---------
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
This PR adds some soundtracks from five different games to the bot's
listening activity.
List of soundtracks added:
- Jukio Kallio, Daniel Hagström - Fall 'n' Roll (Fall Guys)
- SCATTLE - Hypertension (Super Meat Boy)
- KEYGEN CHURCH - Tenebre Rosso Sangue (ULTRAKILL)
- Chipzel - Swing Me Another 6 (Dicey Dungeons)
- ~Floex - The Glasshouse With Butterfly (Machinarium)~
- Noisecream - Mist of Rage (My Friend Pedro)
This PR removes the wrapping of in-house created result errors with
`Result.FromError` (I did not know this was possible until today, lol),
which reduces code verbosity and makes it easier to read, and replaces
`ArgumentNullError` with `ArgumentInvalidError` when retrieving IDs from
a command context, which, in my opinion, is more correct.
---------
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
This PR is mainly aimed at improving /settingslist and fixing /settings
List of things to do before merging:
- [x] #62
- [x] Add the pages feature to /settingslist
- [x] Add bullets like these ->
![](https://github.com/TeamOctolings/Boyfriend/assets/95250141/fdf1a3b8-bb64-473d-9f57-bc6e34812811)
And since the development has already been taking more than 2 days, I
suggest splitting the PR into 2 parts.
List of other things that will be in the future PR:
- mctaylors#1
- Fix bot not answering when an invalid setting is specified in
/settings
- Options list for /settings
---------
Signed-off-by: Macintosh II <95250141+mctaylors@users.noreply.github.com>
This PR adds the `color` and `isPublic` parameters to
`UtilityService#LogActionAsync`. Previously, all embeds would be sent in
both public and private feedback channels with the green embed color. It
is now possible to change these fields, allowing for usage of the red
color in punishment commands and allowing to hide the result of `/clear`
from public eyes.
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
This PR allows passing mentions as an input to SnowflakeOption by
removing non-number characters from the input string.
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
This PR uses a new feature of the InspectCode action: extensions. By
adding the `Cognitive Complexity` extension, InspectCode can provide
warnings and force the contributors to follow standards regarding
complex methods. This functionality was previously provided by
CodeFactor, but it is no longer used. Here's the full changelog of this
PR:
- Allowed Actions to run on push for any branch, not just `master`;
- Added `Cognitive Complexity` plugin for InspectCode;
- Significantly reduced complexity of KickCommandGroup, MuteCommandGroup
and GuildUpdateService
---------
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
Depends on #54
This PR reduces the method complexity of
`ClearCommandGroup#ClearMessagesAsync` by doing the following:
- Splitting the command method into 2 parts: ExecuteClear and
ClearMessagesAsync. The former will check all the needed results and
will pass the outputs into the latter;
- Using the recently extracted method `UtilityService#LogActionAsync` to
log deletion of messages.
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
Depends on #55
These changes are really small and I don't know how to reasonably split
them into multiple PRs, but here's the changelog:
- The result of the `UtilityService#LogActionAsync` call in
`BanCommandGroup#BanUserAsync` is no longer ignored and it's errors will
now prevent feedback from being sent;
- Converted `if` statement to a `return` with ternary in
`LanguageOption`;
- Slightly decreased method complexity of
`MessageDeletedResponder#RespondAsync` by cleverly using Results;
- Changed the order of parameters for `UtilityService#LogActionAsync` to
flow better;
- Removed the exemption for `ConvertIfToReturnStatement` for
InspectCode.
---------
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
This PR reduces method complexity in
`UtilityService#CheckInteractionsAsync` by splitting the method into two
parts, similar to `/ban` and `/unban`. Additionally, it converts the
last `if` statement to a `return` with ternary.
After this is merged, status checks on #54 should succeed and that PR
should be merged.
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
This PR does numerous things to reduce method complexity:
- Created an extension method
`FeedbackService#SendContextualEmbedResultAsync`, which directly takes a
`Result<Embed>` and checks it once in the extension method instead of
multiple times in all commands;
- Split the command methods for `/ban` and `/unban` into 2 parts:
`Execute(Un)Ban` and `(Un)BanUserAsync`. The former will check all the
needed results and will pass the outputs into the latter;
- Extracted the method for logging an action into Private- and
PublicFeedbackChannels. It now resides in UtilityService. Right now,
only `/ban` and `/unban` make use of that method;
- Created an extension method `Snowflake#EmptyOrEqualTo`, that combines
the task of checking if a Snowflake is empty and checking if that
Snowflake is equal to another Snowflake.
Similar changes will be made to other command groups in future PRs. This
is not done here to make the reviewing process easier.
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
This PR splits `LoggingPreparationErrorEvent` and
`ErrorLoggingPostExecutionEvent` classes into separate files and puts
these files in a separate namespace: `Boyfriend.Commands.Events`. This
makes these classes easier to find and distinguish from commands groups.
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
This PR prevents usage of `/about`, `/ping` and `/remind` inside DMs.
While it may look like these commands do not require a guild attached to
them, the language system depends on having a guild to fetch settings
from, so it is not possible to use these commands in DMs.
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
mctaylors:
- updated readme 7 times (and only adding new logo from /about)
-
[removed](aeeb3d4399)
bot footer from created event embed on the second try
-
[changed](4b9b91d9e4)
cdn from discord to upload.systems
Octol1ttle:
- Guild settings code has been overhauled. Instead of instances of a
`GuildConfiguration` class being (de-)serialized when used with listing
and setting options provided by reflection, there are now multiple
`Option` classes responsible for the type of option they are storing.
The classes support getting a value, validating and setting values with
Results, and getting a user-friendly representation of these values.
This makes use of polymorphism, providing clean and easier to use and
refactor code.
- Gateway event responders have been split into their own separate
files, which should make it easier to find and modify responders when
needed.
- Warning suppressions regarding unused and never instantiated classes
have been replaced by `[ImplicitUse]` annotations provided by
`JetBrains.Annotations`. This avoids hiding real issues and provides a
better way to suppress false warnings while being explicit.
- It is no longer possible to execute some slash commands if they are
run without the correct permissions
- Dependencies are now more explicitly defined
neroduckale:
- Made easter eggs case-insensitive
---------
Signed-off-by: Macintosh II <95250141+mctaylors@users.noreply.github.com>
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
Co-authored-by: Octol1ttle <l1ttleofficial@outlook.com>
Co-authored-by: nrdk <neroduck@vk.com>