├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ └── feature_request.md
└── workflows
│ ├── ClemBot.Api-Deployment.yml
│ ├── ClemBot.Api-Integration.yml
│ ├── ClemBot.Bot-Deployment.yml
│ ├── ClemBot.Bot-Integration.yml
│ ├── ClemBot.Site-Deployment.yml
│ ├── ClemBot.Site-Integration.yml.yml
│ ├── Pull-Request-Comment.yaml
│ └── codeql-analysis.yml
├── .gitignore
├── Branding
└── ClemBot.png
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── ClemBot.Api
├── .config
│ └── dotnet-tools.json
├── .dockerignore
├── .editorconfig
├── ClemBot.Api.Common
│ ├── Claims.cs
│ ├── ClemBot.Api.Common.csproj
│ ├── ConfigurationKeys.cs
│ ├── Enums
│ │ ├── BotAuthClaims.cs
│ │ ├── CommandRestrictionType.cs
│ │ ├── DesignatedChannels.cs
│ │ ├── GuildConfig.cs
│ │ └── InfractionType.cs
│ ├── Exceptions
│ │ └── ConfigurationException.cs
│ ├── Extensions
│ │ ├── CacheExtensions.cs
│ │ └── EnumerableExtensions.cs
│ ├── IResponseModel.cs
│ ├── Security
│ │ ├── ApiKey.cs
│ │ ├── JwtToken
│ │ │ ├── IJwtAuthManager.cs
│ │ │ ├── JwtAuthManager.cs
│ │ │ └── JwtTokenConfig.cs
│ │ ├── OAuth
│ │ │ ├── DiscordAuthManager.cs
│ │ │ ├── DiscordUser.cs
│ │ │ ├── IDiscordAuthManager.cs
│ │ │ └── OAuthUser
│ │ │ │ ├── Application.cs
│ │ │ │ ├── DiscordOAuthModel.cs
│ │ │ │ ├── Guild.cs
│ │ │ │ └── User.cs
│ │ └── Policies
│ │ │ ├── BotMaster
│ │ │ ├── BotMasterAuthHandler.cs
│ │ │ ├── BotMasterAuthorizeAttribute.cs
│ │ │ └── BotMasterRequirement.cs
│ │ │ ├── GuildSandbox
│ │ │ ├── GuildSandboxAuthHandler.cs
│ │ │ ├── GuildSandboxAuthorizeAttribute.cs
│ │ │ ├── GuildSandboxModel.cs
│ │ │ ├── GuildSandboxPolicyParser.cs
│ │ │ ├── GuildSandboxPolicyProvider.cs
│ │ │ ├── GuildSandboxRequirement.cs
│ │ │ ├── IGuildSandboxModel.cs
│ │ │ └── IGuildUserSandboxModel.cs
│ │ │ ├── IPolicyParser.cs
│ │ │ └── Policies.cs
│ ├── SeqConstants.cs
│ └── Utilities
│ │ ├── AuthorizeResult.cs
│ │ ├── AuthorizeStatus.cs
│ │ ├── IResult.cs
│ │ ├── LinqExtensions.cs
│ │ ├── QueryResult.cs
│ │ ├── QueryStatus.cs
│ │ └── Result.cs
├── ClemBot.Api.Core
│ ├── Behaviors
│ │ ├── CacheRequestBehavior.cs
│ │ ├── GuildSandboxAuthorizeBehavior.cs
│ │ └── LoggingBehavior.cs
│ ├── ClemBot.Api.Core.csproj
│ ├── Features
│ │ ├── Authorization
│ │ │ ├── AuthorizeController.cs
│ │ │ ├── BotAuthorize.cs
│ │ │ ├── GetSiteUser.cs
│ │ │ └── SiteLogin.cs
│ │ ├── Channels
│ │ │ ├── Bot
│ │ │ │ ├── Create.cs
│ │ │ │ ├── Delete.cs
│ │ │ │ ├── Details.cs
│ │ │ │ ├── Edit.cs
│ │ │ │ └── Index.cs
│ │ │ └── ChannelsController.cs
│ │ ├── ClaimMappings
│ │ │ ├── Bot
│ │ │ │ ├── Create.cs
│ │ │ │ └── Delete.cs
│ │ │ └── ClaimsController.cs
│ │ ├── Commands
│ │ │ ├── Bot
│ │ │ │ ├── AddInvocation.cs
│ │ │ │ ├── Details.cs
│ │ │ │ ├── Disable.cs
│ │ │ │ ├── Enable.cs
│ │ │ │ └── Status.cs
│ │ │ └── CommandsController.cs
│ │ ├── CustomPrefixes
│ │ │ ├── Bot
│ │ │ │ └── Delete.cs
│ │ │ ├── CustomPrefixesController.cs
│ │ │ └── Set.cs
│ │ ├── DesignatedChannels
│ │ │ ├── Bot
│ │ │ │ ├── Delete.cs
│ │ │ │ ├── Details.cs
│ │ │ │ ├── Index.cs
│ │ │ │ └── Register.cs
│ │ │ └── DesignatedChannelsController.cs
│ │ ├── EmoteBoardPosts
│ │ │ ├── Bot
│ │ │ │ ├── Create.cs
│ │ │ │ ├── Delete.cs
│ │ │ │ ├── Details.cs
│ │ │ │ ├── Leaderboard
│ │ │ │ │ ├── Popular.cs
│ │ │ │ │ ├── Posts.cs
│ │ │ │ │ └── Reactions.cs
│ │ │ │ └── React.cs
│ │ │ └── EmoteBoardPostsController.cs
│ │ ├── EmoteBoards
│ │ │ ├── Bot
│ │ │ │ ├── Create.cs
│ │ │ │ ├── Delete.cs
│ │ │ │ ├── Details.cs
│ │ │ │ ├── Edit.cs
│ │ │ │ └── Index.cs
│ │ │ └── EmoteBoardsController.cs
│ │ ├── GuildSettings
│ │ │ ├── Details.cs
│ │ │ ├── GuildSettingsController.cs
│ │ │ ├── Index.cs
│ │ │ └── Set.cs
│ │ ├── Guilds
│ │ │ ├── Bot
│ │ │ │ ├── AddUser.cs
│ │ │ │ ├── Channels.cs
│ │ │ │ ├── Create.cs
│ │ │ │ ├── Delete.cs
│ │ │ │ ├── DeleteWelcomeMessage.cs
│ │ │ │ ├── DesignatedChannels.cs
│ │ │ │ ├── Details.cs
│ │ │ │ ├── Edit.cs
│ │ │ │ ├── GetSlotsScores.cs
│ │ │ │ ├── Index.cs
│ │ │ │ ├── Infractions.cs
│ │ │ │ ├── RemoveUser.cs
│ │ │ │ ├── Roles.cs
│ │ │ │ ├── Threads.cs
│ │ │ │ ├── UpdateChannels.cs
│ │ │ │ ├── UpdateRoleUserMappings.cs
│ │ │ │ ├── UpdateRoles.cs
│ │ │ │ ├── UpdateThreads.cs
│ │ │ │ └── UpdateUsers.cs
│ │ │ ├── GetCustomPrefixes.cs
│ │ │ ├── GetCustomTagPrefixes.cs
│ │ │ ├── GetWelcomeMessage.cs
│ │ │ ├── GuildsController.cs
│ │ │ ├── SetWelcomeMessage.cs
│ │ │ └── Tags.cs
│ │ ├── HealthCheck
│ │ │ └── HealthCheckController.cs
│ │ ├── Infractions
│ │ │ ├── Bot
│ │ │ │ ├── Create.cs
│ │ │ │ ├── Deactivate.cs
│ │ │ │ ├── Delete.cs
│ │ │ │ └── Details.cs
│ │ │ └── InfractionsController.cs
│ │ ├── Messages
│ │ │ ├── Bot
│ │ │ │ ├── Count.cs
│ │ │ │ ├── Create.cs
│ │ │ │ ├── Details.cs
│ │ │ │ └── Edit.cs
│ │ │ └── MessagesController.cs
│ │ ├── Public
│ │ │ ├── GlobalStats.cs
│ │ │ └── PublicController.cs
│ │ ├── Reminders
│ │ │ ├── Bot
│ │ │ │ ├── Create.cs
│ │ │ │ ├── Details.cs
│ │ │ │ ├── Dispatch.cs
│ │ │ │ └── Index.cs
│ │ │ └── RemindersController.cs
│ │ ├── Roles
│ │ │ ├── Bot
│ │ │ │ ├── Claims.cs
│ │ │ │ ├── Create.cs
│ │ │ │ ├── Delete.cs
│ │ │ │ ├── Details.cs
│ │ │ │ ├── Edit.cs
│ │ │ │ └── Index.cs
│ │ │ └── RolesController.cs
│ │ ├── SlotScores
│ │ │ ├── Bot
│ │ │ │ └── AddScore.cs
│ │ │ └── SlotScoresController.cs
│ │ ├── Tags
│ │ │ ├── Bot
│ │ │ │ ├── DeleteCustomTagPrefix.cs
│ │ │ │ ├── Invoke.cs
│ │ │ │ └── Search.cs
│ │ │ ├── Create.cs
│ │ │ ├── Delete.cs
│ │ │ ├── Details.cs
│ │ │ ├── Edit.cs
│ │ │ ├── SetCustomTagPrefix.cs
│ │ │ └── TagsController.cs
│ │ ├── Threads
│ │ │ ├── Bot
│ │ │ │ ├── Create.cs
│ │ │ │ ├── Delete.cs
│ │ │ │ ├── Details.cs
│ │ │ │ ├── Edit.cs
│ │ │ │ └── Index.cs
│ │ │ └── ThreadsController.cs
│ │ └── Users
│ │ │ ├── Bot
│ │ │ ├── Create.cs
│ │ │ ├── CreateBulk.cs
│ │ │ ├── Details.cs
│ │ │ ├── Edit.cs
│ │ │ ├── GetGuildClaims.cs
│ │ │ ├── GetSlotsScores.cs
│ │ │ ├── Index.cs
│ │ │ ├── Infractions.cs
│ │ │ ├── Reminders.cs
│ │ │ └── UpdateRoles.cs
│ │ │ └── UsersController.cs
│ ├── GlobalUsings.cs
│ ├── Program.cs
│ ├── Properties
│ │ └── launchSettings.json
│ ├── appsettings.Development.json
│ ├── appsettings.json
│ └── nuget.config
├── ClemBot.Api.Data
│ ├── ClemBot.Api.Data.csproj
│ ├── Contexts
│ │ ├── ClemBotContext.cs
│ │ └── ClemBotContextDesignFactory.cs
│ ├── Exceptions
│ │ └── EntityStateException.cs
│ ├── Extensions
│ │ └── UserExtensions.cs
│ ├── Migrations
│ │ ├── 20210608221705_InitialCreate.Designer.cs
│ │ ├── 20210608221705_InitialCreate.cs
│ │ ├── 20210702040249_ChangedGuildUserJunctionRelationship.Designer.cs
│ │ ├── 20210702040249_ChangedGuildUserJunctionRelationship.cs
│ │ ├── 20210702042014_ChangedGuildRoleJunctionRelationship.Designer.cs
│ │ ├── 20210702042014_ChangedGuildRoleJunctionRelationship.cs
│ │ ├── 20210721005557_AddedCommandInvocationTable.Designer.cs
│ │ ├── 20210721005557_AddedCommandInvocationTable.cs
│ │ ├── 20210906231053_AddThreadChannelEntityData.Designer.cs
│ │ ├── 20210906231053_AddThreadChannelEntityData.cs
│ │ ├── 20211217231226_AddDashboardEditClaim.Designer.cs
│ │ ├── 20211217231226_AddDashboardEditClaim.cs
│ │ ├── 20211218015002_AddOwnerIdToGuildEntity.Designer.cs
│ │ ├── 20211218015002_AddOwnerIdToGuildEntity.cs
│ │ ├── 20211220234422_AddDashboardEditClaimAndEmbedLink.Designer.cs
│ │ ├── 20211220234422_AddDashboardEditClaimAndEmbedLink.cs
│ │ ├── 20220103205011_AddSlotScoresTable.Designer.cs
│ │ ├── 20220103205011_AddSlotScoresTable.cs
│ │ ├── 20220111013158_AddViewSelfInfractionClaim.Designer.cs
│ │ ├── 20220111013158_AddViewSelfInfractionClaim.cs
│ │ ├── 20220123020435_DropCommandInvocationFkConstraint.Designer.cs
│ │ ├── 20220123020435_DropCommandInvocationFkConstraint.cs
│ │ ├── 20220419035353_AddInlineTagprefix.Designer.cs
│ │ ├── 20220419035353_AddInlineTagprefix.cs
│ │ ├── 20220801012041_UpdateRemindersTable.Designer.cs
│ │ ├── 20220801012041_UpdateRemindersTable.cs
│ │ ├── 20220821175300_AddTagTransferClaim.Designer.cs
│ │ ├── 20220821175300_AddTagTransferClaim.cs
│ │ ├── 20220827014754_AddCommandRestrictions.Designer.cs
│ │ ├── 20220827014754_AddCommandRestrictions.cs
│ │ ├── 20220924190055_AddCommandRestrictionWhitelist.Designer.cs
│ │ ├── 20220924190055_AddCommandRestrictionWhitelist.cs
│ │ ├── 20221030165404_AddAutoRoleAssignedProp.Designer.cs
│ │ ├── 20221030165404_AddAutoRoleAssignedProp.cs
│ │ ├── 20221111235523_UpdateEntityNullability.Designer.cs
│ │ ├── 20221111235523_UpdateEntityNullability.cs
│ │ ├── 20230530011136_AddEmoteBoards.Designer.cs
│ │ ├── 20230530011136_AddEmoteBoards.cs
│ │ ├── 20231021011235_AddEmoteBoardPostReaction.Designer.cs
│ │ ├── 20231021011235_AddEmoteBoardPostReaction.cs
│ │ ├── 20231115224838_DropSubjectForeignKeyInfractions.Designer.cs
│ │ ├── 20231115224838_DropSubjectForeignKeyInfractions.cs
│ │ └── ClemBotContextModelSnapshot.cs
│ └── Models
│ │ ├── Channel.cs
│ │ ├── ClaimsMapping.cs
│ │ ├── CommandInvocation.cs
│ │ ├── CommandRestriction.cs
│ │ ├── CustomPrefix.cs
│ │ ├── CustomTagPrefix.cs
│ │ ├── DesignatedChannelMapping.cs
│ │ ├── EmoteBoard.cs
│ │ ├── EmoteBoardMessage.cs
│ │ ├── EmoteBoardPost.cs
│ │ ├── EmoteBoardPostReaction.cs
│ │ ├── Guild.cs
│ │ ├── GuildSetting.cs
│ │ ├── GuildUser.cs
│ │ ├── Infraction.cs
│ │ ├── Message.cs
│ │ ├── MessageContent.cs
│ │ ├── Reminder.cs
│ │ ├── Role.cs
│ │ ├── RoleUser.cs
│ │ ├── SlotScore.cs
│ │ ├── Tag.cs
│ │ ├── TagUse.cs
│ │ └── User.cs
├── ClemBot.Api.Services
│ ├── Authorization
│ │ ├── GuildSandboxAuthorizeService.cs
│ │ └── IGuildSandboxAuthorizeService.cs
│ ├── Caching
│ │ ├── Channels
│ │ │ ├── ChannelCacheHandlers.cs
│ │ │ └── Models
│ │ │ │ ├── ChannelExistsRequest.cs
│ │ │ │ └── ClearChannelRequest.cs
│ │ ├── Commands
│ │ │ ├── CommandCacheHandler.cs
│ │ │ └── Models
│ │ │ │ ├── ClearCommandRestrictionRequest.cs
│ │ │ │ └── GetCommandRestrictionRequest.cs
│ │ ├── CustomPrefix
│ │ │ ├── CustomPrefixHandler.cs
│ │ │ └── Models
│ │ │ │ ├── ClearCustomPrefixRequest.cs
│ │ │ │ └── GetCustomPrefixRequest.cs
│ │ ├── CustomTagPrefix
│ │ │ ├── CustomTagPrefixHandlers.cs
│ │ │ └── Models
│ │ │ │ ├── ClearCustomTagPrefixRequest.cs
│ │ │ │ └── GetCustomTagPrefixRequest.cs
│ │ ├── DesignatedChannels
│ │ │ ├── DesignatedChannelsDetailsCacheHandler.cs
│ │ │ └── Models
│ │ │ │ ├── ClearDesignatedChannelDetailRequest.cs
│ │ │ │ └── GetDesignatedChannelDetailRequest.cs
│ │ ├── EmoteBoardPosts
│ │ │ ├── EmoteBoardPostCacheHandler.cs
│ │ │ └── Models
│ │ │ │ ├── ClearEmoteBoardPostRequest.cs
│ │ │ │ └── EmoteBoardPostExistsRequest.cs
│ │ ├── EmoteBoards
│ │ │ ├── GuildBoardsCacheHandler.cs
│ │ │ └── Models
│ │ │ │ ├── ClearGuildBoardsRequest.cs
│ │ │ │ └── GetGuildBoardsRequest.cs
│ │ ├── GlobalStats
│ │ │ ├── GetGlobalCommandStats.cs
│ │ │ ├── GetGlobalGuildStats.cs
│ │ │ ├── GetGlobalUserStats.cs
│ │ │ └── Models
│ │ │ │ ├── GlobalCommandStatsRequest.cs
│ │ │ │ ├── GlobalGuildStatsRequest.cs
│ │ │ │ └── GlobalUserStatsRequest.cs
│ │ ├── Guilds
│ │ │ ├── CheckGuildExistsHandler.cs
│ │ │ └── Models
│ │ │ │ └── GuildExistsRequest.cs
│ │ ├── ICacheRequest.cs
│ │ ├── Messages
│ │ │ ├── CheckMessageExistsHandler.cs
│ │ │ └── Models
│ │ │ │ └── MessageExistsRequest.cs
│ │ └── Users
│ │ │ ├── CheckUserExists.cs
│ │ │ └── Models
│ │ │ └── UserExistsRequest.cs
│ ├── ClemBot.Api.Services.csproj
│ ├── GuildSettings
│ │ ├── GuildSettingsService.cs
│ │ └── IGuildSettingsService.cs
│ └── Jobs
│ │ └── MessageContentDeletionJob.cs
├── ClemBot.Api.sln
└── Dockerfile
├── ClemBot.Bot
├── .dockerignore
├── .vscode
│ ├── launch.json
│ ├── settings.json
│ ├── settings.json.default
│ └── tasks.json
├── BotSecrets.json.template
├── ClemBot.code-workspace
├── Dockerfile
├── bot
│ ├── __init__.py
│ ├── __main__.py
│ ├── api
│ │ ├── __init__.py
│ │ ├── api_client.py
│ │ ├── base_route.py
│ │ ├── channel_route.py
│ │ ├── claim_route.py
│ │ ├── commands_route.py
│ │ ├── custom_prefix_route.py
│ │ ├── custom_tag_prefix_route.py
│ │ ├── designated_channel_route.py
│ │ ├── emote_board_route.py
│ │ ├── guild_route.py
│ │ ├── health_check_route.py
│ │ ├── message_route.py
│ │ ├── moderation_route.py
│ │ ├── reminder_route.py
│ │ ├── role_route.py
│ │ ├── slots_score_route.py
│ │ ├── tag_route.py
│ │ ├── thread_route.py
│ │ ├── user_route.py
│ │ └── welcome_message_route.py
│ ├── bot_secrets.py
│ ├── clem_bot.py
│ ├── cogs
│ │ ├── __init__.py
│ │ ├── assignable_roles_cog.py
│ │ ├── bot_info_cog.py
│ │ ├── calculator_cog.py
│ │ ├── choose_cog.py
│ │ ├── claims_authorization_cog.py
│ │ ├── command_cog.py
│ │ ├── custom_prefix_cog.py
│ │ ├── designated_channels_cog.py
│ │ ├── emote_board_cog.py
│ │ ├── emote_cog.py
│ │ ├── eval_cog.py
│ │ ├── example_cog.py
│ │ ├── grades_cog
│ │ │ ├── __init__.py
│ │ │ ├── assets
│ │ │ │ ├── 2014_Fall.csv
│ │ │ │ ├── 2014_Spring.csv
│ │ │ │ ├── 2015_Fall.csv
│ │ │ │ ├── 2015_Spring.csv
│ │ │ │ ├── 2016_Fall.csv
│ │ │ │ ├── 2016_Spring.csv
│ │ │ │ ├── 2017_Fall.csv
│ │ │ │ ├── 2017_Spring.csv
│ │ │ │ ├── 2018_Fall.csv
│ │ │ │ ├── 2018_Spring.csv
│ │ │ │ ├── 2019_Fall.csv
│ │ │ │ ├── 2019_Spring.csv
│ │ │ │ ├── 2020_Fall.csv
│ │ │ │ ├── 2021_Fall.csv
│ │ │ │ ├── 2021_Spring.csv
│ │ │ │ └── 2022_Spring.csv
│ │ │ └── grades_cog.py
│ │ ├── guild_info_cog.py
│ │ ├── help_cog.py
│ │ ├── info_cog.py
│ │ ├── moderation_cog
│ │ │ ├── __init__.py
│ │ │ ├── ban_cog.py
│ │ │ ├── infractions_cog.py
│ │ │ ├── mute_cog.py
│ │ │ └── warn_cog.py
│ │ ├── owner_cog.py
│ │ ├── ping_pong_cog.py
│ │ ├── random_cog
│ │ │ ├── __init__.py
│ │ │ ├── assets
│ │ │ │ └── phrases.txt
│ │ │ ├── og_slots_cog.py
│ │ │ └── slots_cog.py
│ │ ├── remind_cog.py
│ │ ├── rock_paper_scissors_cog.py
│ │ ├── search_cog.py
│ │ ├── source_code_cog.py
│ │ ├── tags_cog.py
│ │ ├── trivia_cog.py
│ │ └── welcome_message_cog.py
│ ├── consts.py
│ ├── custom_prefix.py
│ ├── errors.py
│ ├── extensions.py
│ ├── messaging
│ │ ├── __init__.py
│ │ ├── events.py
│ │ └── messenger.py
│ ├── models
│ │ ├── channel_models.py
│ │ ├── claim_models.py
│ │ ├── clem_bot_model.py
│ │ ├── command_models.py
│ │ ├── emote_board_models.py
│ │ ├── guild_models.py
│ │ ├── message_models.py
│ │ ├── moderation_models.py
│ │ ├── reminder_models.py
│ │ ├── role_models.py
│ │ ├── tag_models.py
│ │ ├── thread_models.py
│ │ └── user_models.py
│ ├── services
│ │ ├── __init__.py
│ │ ├── base_service.py
│ │ ├── bot_ping_help_service.py
│ │ ├── channel_handling_service.py
│ │ ├── claims_service.py
│ │ ├── command_service.py
│ │ ├── delete_message_service.py
│ │ ├── designated_channel_service.py
│ │ ├── emote_board_service.py
│ │ ├── example_service.py
│ │ ├── fuzzy_matching_service.py
│ │ ├── guild_handling_service.py
│ │ ├── message_handling_service.py
│ │ ├── moderation_service.py
│ │ ├── paginate_service.py
│ │ ├── reminder_service.py
│ │ ├── role_handling_service.py
│ │ ├── startup_service.py
│ │ ├── tag_service.py
│ │ ├── thread_handling_service.py
│ │ ├── user_handling_service.py
│ │ └── welcome_message_service.py
│ └── utils
│ │ ├── converters.py
│ │ ├── displayable_path.py
│ │ ├── helpers.py
│ │ ├── log_serializers.py
│ │ ├── logging_utils.py
│ │ ├── scheduler.py
│ │ ├── trigrams.py
│ │ └── user_choice.py
├── poetry.lock
├── pyproject.toml
├── scripts
│ └── normalize_grade_data.py
├── stubs
│ ├── humps
│ │ └── camel
│ │ │ └── __init__.pyi
│ ├── logging
│ │ ├── __init__.pyi
│ │ ├── config.pyi
│ │ └── handlers.pyi
│ ├── markdownify
│ │ └── __init__.pyi
│ ├── nltk
│ │ └── __init__.pyi
│ └── seqlog
│ │ ├── __init__.pyi
│ │ ├── consumer.pyi
│ │ └── structured_logging.pyi
└── tests
│ ├── __init__.py
│ └── bot
│ ├── __init__.py
│ ├── messaging
│ ├── __init__.py
│ └── messenger_test.py
│ └── utils
│ ├── __init__.py
│ └── scheduler_test.py
├── ClemBot.Docs
├── .gitignore
├── README.md
├── babel.config.js
├── docs
│ ├── Claims.md
│ ├── CommandRestrictions.md
│ ├── CustomPrefix.md
│ ├── DesignatedChannels.md
│ ├── EmoteBoards.md
│ ├── Intro.md
│ ├── Moderation
│ │ ├── Ban.md
│ │ ├── Mute.md
│ │ ├── Overview.md
│ │ └── Warning.md
│ ├── Roles
│ │ ├── AutoAssignedRoles.md
│ │ └── UserAssignableRoles.md
│ ├── Tags.md
│ └── WelcomeMessage.md
├── docusaurus.config.js
├── package-lock.json
├── package.json
├── sidebars.js
├── src
│ ├── components
│ │ └── temp.txt
│ ├── css
│ │ └── custom.scss
│ └── pages
│ │ ├── index.module.css
│ │ └── markdown-page.md
├── static
│ ├── .nojekyll
│ └── img
│ │ ├── ClemBotLogo.png
│ │ ├── ClemBotLogo.svg
│ │ └── favicon.ico
├── tsconfig.json
└── yarn.lock
├── ClemBot.Site
├── .dockerignore
├── .editorconfig
├── .eslintrc.js
├── .gitignore
├── .prettierrc
├── .vscode
│ └── launch.json
├── Dockerfile
├── README.md
├── assets
│ ├── README.md
│ ├── buefy.png
│ └── css
│ │ └── main.scss
├── components
│ ├── DiscordLogo.vue
│ ├── FeatureCard.vue
│ ├── Footer.vue
│ ├── GuildDropdown.vue
│ ├── Logo.vue
│ ├── UserDisplay.vue
│ ├── dashboard
│ │ └── TagsTable.vue
│ └── support
│ │ ├── Contributors.vue
│ │ ├── SponsorButton.vue
│ │ └── Stargazers.vue
├── layouts
│ ├── README.md
│ ├── dashboard.vue
│ └── default.vue
├── middleware
│ ├── DashboardAuthCheck.ts
│ ├── HomeAuthCheck.ts
│ └── README.md
├── nuxt.config.js
├── package-lock.json
├── package.json
├── pages
│ ├── README.md
│ ├── dashboard
│ │ └── _id
│ │ │ ├── guild.vue
│ │ │ ├── index.vue
│ │ │ └── tags.vue
│ ├── index.vue
│ ├── login.vue
│ ├── privacy.vue
│ ├── status.vue
│ └── support.vue
├── plugins
│ ├── Api.ts
│ ├── Axios.js
│ └── README.md
├── services
│ ├── Claims.ts
│ ├── GuildSettings.ts
│ ├── Models
│ │ └── Guild.ts
│ ├── Utilities.ts
│ └── api
│ │ ├── ApiClient.ts
│ │ └── routes
│ │ ├── CustomPrefix.ts
│ │ ├── GuildSetting.ts
│ │ ├── Public.ts
│ │ ├── Tags.ts
│ │ └── WelcomeMessage.ts
├── static
│ ├── ClemBotLogo.png
│ ├── ClemBotLogo.svg
│ ├── ClemBotLogoHat.svg
│ ├── ClemBotLogoNoBkg.svg
│ ├── FeatureImages
│ │ ├── AssignRoles.png
│ │ ├── CustomPrefix.png
│ │ ├── MemberBan.png
│ │ ├── MemberMute.png
│ │ ├── MessageEdit.png
│ │ ├── SlotMachineInvoke.png
│ │ ├── TagInvoke.png
│ │ └── UserJoinEmbed.png
│ ├── README.md
│ ├── SplashBkg.svg
│ └── favicon.ico
├── store
│ ├── README.md
│ └── index.ts
├── tempt.txt
└── tsconfig.json
├── LICENSE
├── README.md
└── docker-compose.yml
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [ClemBotProject, Jay-Madden]
4 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/workflows/ClemBot.Api-Integration.yml:
--------------------------------------------------------------------------------
1 | name: ClemBot.Api-integration
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 | paths:
7 | - "ClemBot.Api/**"
8 | pull_request:
9 | branches: [ master ]
10 | paths:
11 | - "ClemBot.Api/**"
12 | workflow_dispatch:
13 |
14 | jobs:
15 | build:
16 | runs-on: ubuntu-latest
17 | defaults:
18 | run:
19 | shell: bash
20 | working-directory: ClemBot.Api
21 | steps:
22 | - uses: actions/checkout@v4
23 |
24 | - name: Checkout repo
25 | uses: actions/checkout@v4
26 |
27 | - name: Add dotnet-format problem matcher
28 | uses: xt0rted/dotnet-format-problem-matcher@v1
29 |
30 | - name: Restore format dependencies
31 | run: dotnet tool restore
32 |
33 | - name: Run dotnet format
34 | run: dotnet tool run dotnet-format
35 |
--------------------------------------------------------------------------------
/.github/workflows/ClemBot.Site-Integration.yml.yml:
--------------------------------------------------------------------------------
1 | name: ClemBot.Site-integraion
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 | paths:
7 | - "ClemBot.Site/**"
8 | pull_request:
9 | branches: [ master ]
10 | paths:
11 | - "ClemBot.Site/**"
12 | workflow_dispatch:
13 |
14 | jobs:
15 | build:
16 | runs-on: ubuntu-latest
17 | defaults:
18 | run:
19 | shell: bash
20 | working-directory: ClemBot.Site
21 | steps:
22 | - name: Lint with Prettier
23 | uses: creyD/prettier_action@v3.3
24 | with:
25 | dry: true
26 | only_changed: true
27 |
28 |
--------------------------------------------------------------------------------
/Branding/ClemBot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ClemBotProject/ClemBot/5b169b1d45d581660fff4438aab920fb4101f64b/Branding/ClemBot.png
--------------------------------------------------------------------------------
/ClemBot.Api/.config/dotnet-tools.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 1,
3 | "isRoot": true,
4 | "tools": {
5 | "dotnet-format": {
6 | "version": "5.1.225507",
7 | "commands": [
8 | "dotnet-format"
9 | ]
10 | },
11 | "dotnet-ef": {
12 | "version": "7.0.0",
13 | "commands": [
14 | "dotnet-ef"
15 | ]
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/ClemBot.Api/.dockerignore:
--------------------------------------------------------------------------------
1 | bin/
2 | obj/
3 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Common/Claims.cs:
--------------------------------------------------------------------------------
1 | namespace ClemBot.Api.Common;
2 |
3 | public static class Claims
4 | {
5 | public const string ContextGuildId = "ContextGuildId";
6 | public const string BotApiKey = "BotClaim";
7 | public const string DiscordBearer = "DiscordBearer";
8 | public const string DiscordUserId = "DiscordUserId";
9 | }
10 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Common/ClemBot.Api.Common.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 | enable
7 | 12
8 | True
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Common/ConfigurationKeys.cs:
--------------------------------------------------------------------------------
1 | namespace ClemBot.Api.Common;
2 |
3 | public static class ConfigurationKeys
4 | {
5 | public static string DbConnectionString => "ClemBotConnectionString";
6 |
7 | public static string BotApiKey => "BotApiKey";
8 |
9 | public static string SeqUrl => "SeqUrl";
10 |
11 | public static string SeqApiKey => "SeqApiKey";
12 | }
13 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Common/Enums/BotAuthClaims.cs:
--------------------------------------------------------------------------------
1 | namespace ClemBot.Api.Common.Enums;
2 |
3 | public enum BotAuthClaims
4 | {
5 | designated_channel_view,
6 | designated_channel_modify,
7 | custom_prefix_set,
8 | welcome_message_view,
9 | welcome_message_modify,
10 | tag_add,
11 | tag_delete,
12 | tag_transfer,
13 | assignable_roles_add,
14 | assignable_roles_delete,
15 | delete_message,
16 | emote_add,
17 | claims_view,
18 | claims_modify,
19 | manage_class_add,
20 | moderation_warn,
21 | moderation_ban,
22 | moderation_mute,
23 | moderation_purge,
24 | moderation_infraction_view,
25 | moderation_infraction_view_self,
26 | dashboard_view,
27 | dashboard_edit,
28 | guild_settings_view,
29 | guild_settings_edit,
30 | custom_tag_prefix_set,
31 | command_restrictions_edit,
32 | bypass_disabled_commands,
33 | manage_emote_boards,
34 | }
35 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Common/Enums/CommandRestrictionType.cs:
--------------------------------------------------------------------------------
1 | namespace ClemBot.Api.Common.Enums;
2 |
3 | public enum CommandRestrictionType
4 | {
5 | WhiteList,
6 | BlackList
7 | }
8 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Common/Enums/DesignatedChannels.cs:
--------------------------------------------------------------------------------
1 | namespace ClemBot.Api.Common.Enums;
2 |
3 | public enum DesignatedChannels
4 | {
5 | message_log,
6 | moderation_log,
7 | user_join_log,
8 | user_leave_log,
9 | starboard,
10 | server_join_log,
11 | bot_dm_log
12 | }
13 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Common/Enums/InfractionType.cs:
--------------------------------------------------------------------------------
1 | namespace ClemBot.Api.Common.Enums;
2 |
3 | public enum InfractionType
4 | {
5 | Ban,
6 | Mute,
7 | Warn
8 | }
9 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Common/Exceptions/ConfigurationException.cs:
--------------------------------------------------------------------------------
1 | namespace ClemBot.Api.Common.Exceptions;
2 |
3 | public class ConfigurationException : Exception
4 | {
5 | public ConfigurationException(string message) : base(message)
6 | {
7 |
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Common/Extensions/CacheExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using LazyCache;
3 | using Microsoft.Extensions.Caching.Memory;
4 |
5 | namespace ClemBot.Api.Common.Extensions;
6 |
7 | public static class CacheExtensions
8 | {
9 | public static int Size(this IAppCache cache)
10 | {
11 | var memCache = cache.CacheProvider.GetType().GetField("cache", BindingFlags.NonPublic | BindingFlags.Instance)?
12 | .GetValue(cache.CacheProvider) as MemoryCache;
13 |
14 | if (memCache is null)
15 | {
16 | throw new InvalidOperationException("Getting Mem-cache instance failed");
17 | }
18 |
19 | return memCache.Count;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Common/Extensions/EnumerableExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace ClemBot.Api.Common.Extensions;
2 |
3 | public static class EnumerableExtensions
4 | {
5 | public static IEnumerable WhereNotNull(this IEnumerable enumerable)
6 | {
7 | foreach (var val in enumerable)
8 | {
9 | if (val is not null)
10 | {
11 | yield return val;
12 | }
13 | }
14 | }
15 |
16 | public static IEnumerable WhereNotNull(this IEnumerable enumerable) where T : struct
17 | {
18 | foreach (var val in enumerable)
19 | {
20 | if (val is not null)
21 | {
22 | yield return (T)val;
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Common/IResponseModel.cs:
--------------------------------------------------------------------------------
1 | namespace ClemBot.Api.Common;
2 |
3 | public interface IResponseModel
4 | {
5 | }
6 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Common/Security/ApiKey.cs:
--------------------------------------------------------------------------------
1 | namespace ClemBot.Api.Common.Security;
2 |
3 | ///
4 | /// Class to encapsulate the Api key to accept from the Bot process to
5 | /// authorize requests
6 | ///
7 | public class ApiKey
8 | {
9 | public string Key { get; init; } = null!;
10 | }
11 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Common/Security/JwtToken/IJwtAuthManager.cs:
--------------------------------------------------------------------------------
1 | using System.Security.Claims;
2 |
3 | namespace ClemBot.Api.Common.Security.JwtToken;
4 |
5 | public interface IJwtAuthManager
6 | {
7 | ///
8 | /// Generates a JWT Token for a given enumerable of claims and date
9 | ///
10 | ///
11 | ///
12 | ///
13 | public string GenerateToken(IEnumerable claims, DateTime now);
14 | }
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Common/Security/JwtToken/JwtAuthManager.cs:
--------------------------------------------------------------------------------
1 | using System.IdentityModel.Tokens.Jwt;
2 | using System.Security.Claims;
3 | using System.Text;
4 | using Microsoft.IdentityModel.Tokens;
5 |
6 | namespace ClemBot.Api.Common.Security.JwtToken;
7 |
8 | public class JwtAuthManager : IJwtAuthManager
9 | {
10 | private readonly JwtTokenConfig _jwtTokenConfig;
11 | private readonly byte[] _secret;
12 |
13 | public JwtAuthManager(JwtTokenConfig jwtTokenConfig)
14 | {
15 | _jwtTokenConfig = jwtTokenConfig;
16 | _secret = Encoding.ASCII.GetBytes(_jwtTokenConfig.Secret);
17 | }
18 |
19 | ///
20 | public string GenerateToken(IEnumerable claims, DateTime expires)
21 | {
22 | var tokenDescriptor = new SecurityTokenDescriptor
23 | {
24 | Subject = new ClaimsIdentity(claims),
25 | Expires = expires,
26 | SigningCredentials = new SigningCredentials(
27 | new SymmetricSecurityKey(_secret),
28 | SecurityAlgorithms.HmacSha256Signature),
29 | Audience = _jwtTokenConfig.Audience,
30 | Issuer = _jwtTokenConfig.Issuer
31 | };
32 |
33 | var tokenHandler = new JwtSecurityTokenHandler();
34 | var securityToken = tokenHandler.CreateToken(tokenDescriptor);
35 | var token = tokenHandler.WriteToken(securityToken);
36 | return token;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Common/Security/JwtToken/JwtTokenConfig.cs:
--------------------------------------------------------------------------------
1 | namespace ClemBot.Api.Common.Security.JwtToken;
2 |
3 | public class JwtTokenConfig
4 | {
5 | public string Secret { get; set; } = null!;
6 | public string Issuer { get; set; } = null!;
7 | public string Audience { get; set; } = null!;
8 | public int AccessTokenExpiration { get; set; }
9 | public int RefreshTokenExpiration { get; set; }
10 | }
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Common/Security/OAuth/DiscordUser.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 |
3 | namespace ClemBot.Api.Common.Security.OAuth
4 | {
5 | public class DiscordUser
6 | {
7 | [JsonPropertyName("id")]
8 | public string? Id { get; set; }
9 |
10 | [JsonPropertyName("username")]
11 | public string? Username { get; set; }
12 |
13 | [JsonPropertyName("avatar")]
14 | public string? Avatar { get; set; }
15 |
16 | [JsonPropertyName("discriminator")]
17 | public string? Discriminator { get; set; }
18 |
19 | [JsonPropertyName("public_flags")]
20 | public int PublicFlags { get; set; }
21 | }}
22 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Common/Security/OAuth/IDiscordAuthManager.cs:
--------------------------------------------------------------------------------
1 | using ClemBot.Api.Common.Security.OAuth.OAuthUser;
2 |
3 | namespace ClemBot.Api.Common.Security.OAuth
4 | {
5 | public interface IDiscordAuthManager
6 | {
7 | Task CheckTokenIsUserAsync(string bearer);
8 | Task GetDiscordUserAsync(string bearer);
9 | Task?> GetDiscordUserGuildsAsync(string bearer);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Common/Security/OAuth/OAuthUser/Application.cs:
--------------------------------------------------------------------------------
1 | namespace ClemBot.Api.Common.Security.OAuth.OAuthUser
2 | {
3 | public class Application
4 | {
5 | public string Id { get; set; } = null!;
6 | public string Name { get; set; }= null!;
7 | public string Icon { get; set; }= null!;
8 | public string Description { get; set; }= null!;
9 | public string Summary { get; set; }= null!;
10 | public bool Hook { get; set; }
11 | public bool BotPublic { get; set; }
12 | public bool BotRequireCodeGrant { get; set; }
13 | public string VerifyKey { get; set; }= null!;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Common/Security/OAuth/OAuthUser/DiscordOAuthModel.cs:
--------------------------------------------------------------------------------
1 | namespace ClemBot.Api.Common.Security.OAuth.OAuthUser
2 | {
3 | public class DiscordOAuthModel
4 | {
5 | public Application Application { get; set; } = null!;
6 | public List? Scopes { get; set; }
7 | public DateTime Expires { get; set; }
8 | public User User { get; set; } = null!;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Common/Security/OAuth/OAuthUser/Guild.cs:
--------------------------------------------------------------------------------
1 | namespace ClemBot.Api.Common.Security.OAuth.OAuthUser;
2 |
3 | public class Guild
4 | {
5 | public string Id { get; init; } = null!;
6 | public string Name { get; init; } = null!;
7 | public string Icon { get; init; } = null!;
8 | public bool Owner { get; init; }
9 | public int Permissions { get; init; }
10 | public List Features { get; init; } = null!;
11 | public List Claims { get; set; } = new();
12 | public bool IsAdded { get; set; }
13 | }
14 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Common/Security/OAuth/OAuthUser/User.cs:
--------------------------------------------------------------------------------
1 | namespace ClemBot.Api.Common.Security.OAuth.OAuthUser
2 | {
3 | public class User
4 | {
5 | public string Id { get; set; } = null!;
6 | public string Username { get; set; } = null!;
7 | public string Avatar { get; set; } = null!;
8 | public string Discriminator { get; set; } = null!;
9 | public int PublicFlags { get; set; }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Common/Security/Policies/BotMaster/BotMasterAuthHandler.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Authorization;
2 | using Microsoft.AspNetCore.Http;
3 | using Microsoft.Extensions.Logging;
4 |
5 | namespace ClemBot.Api.Common.Security.Policies.BotMaster;
6 |
7 | public class BotMasterAuthHandler : AuthorizationHandler
8 | {
9 | private readonly ILogger _logger;
10 | private readonly HttpContext? _requestContext;
11 |
12 | public BotMasterAuthHandler(ILogger logger, IHttpContextAccessor requestContext)
13 | {
14 | _logger = logger;
15 | _requestContext = requestContext.HttpContext;
16 | }
17 |
18 | protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
19 | BotMasterRequirement requirement)
20 | {
21 | if (context.User.HasClaim(c => c.Type == Claims.BotApiKey))
22 | {
23 | context.Succeed(requirement);
24 | }
25 |
26 | return Task.CompletedTask;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Common/Security/Policies/BotMaster/BotMasterAuthorizeAttribute.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Authorization;
2 |
3 | namespace ClemBot.Api.Common.Security.Policies.BotMaster;
4 |
5 | public class BotMasterAuthorizeAttribute : AuthorizeAttribute
6 | {
7 | public BotMasterAuthorizeAttribute()
8 | {
9 | Policy = Policies.BotMaster;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Common/Security/Policies/BotMaster/BotMasterRequirement.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Authorization;
2 |
3 | namespace ClemBot.Api.Common.Security.Policies.BotMaster;
4 |
5 | ///
6 | /// Marker class to define a request that only the bot process is allowed to access
7 | ///
8 | public class BotMasterRequirement : IAuthorizationRequirement
9 | {
10 | }
11 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Common/Security/Policies/GuildSandbox/GuildSandboxAuthorizeAttribute.cs:
--------------------------------------------------------------------------------
1 | using ClemBot.Api.Common.Enums;
2 |
3 | namespace ClemBot.Api.Common.Security.Policies.GuildSandbox;
4 |
5 | public class GuildSandboxAuthorizeAttribute : Attribute //: AuthorizeAttribute
6 | {
7 | private readonly GuildSandboxPolicyParser _parser = new();
8 |
9 | public IEnumerable Claims {get; set; }
10 |
11 | public GuildSandboxAuthorizeAttribute(params BotAuthClaims[] claims)
12 | {
13 | Claims = claims.ToList();
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Common/Security/Policies/GuildSandbox/GuildSandboxModel.cs:
--------------------------------------------------------------------------------
1 | namespace ClemBot.Api.Common.Security.Policies.GuildSandbox;
2 |
3 | public record GuildSandboxModel : IGuildSandboxModel
4 | {
5 | public ulong GuildId { get; init; }
6 |
7 | public GuildSandboxModel()
8 | {
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Common/Security/Policies/GuildSandbox/GuildSandboxPolicyParser.cs:
--------------------------------------------------------------------------------
1 | using ClemBot.Api.Common.Enums;
2 |
3 | namespace ClemBot.Api.Common.Security.Policies.GuildSandbox;
4 |
5 | public class GuildSandboxPolicyParser : IPolicyParser>
6 | {
7 | public const string POLICY_PREFIX = Policies.GuildSandbox;
8 |
9 | ///
10 | public string Serialize(IEnumerable? t)
11 | => $"{POLICY_PREFIX}{string.Join(';', t ?? new List())}";
12 |
13 | ///
14 | public IEnumerable? Deserialize(string val)
15 | {
16 | if (!val.StartsWith(POLICY_PREFIX))
17 | {
18 | return null;
19 | }
20 |
21 | var claimsStr = val.Substring(POLICY_PREFIX.Length);
22 |
23 | return string.IsNullOrEmpty(claimsStr)
24 | ? new List()
25 | : claimsStr.Split(';').Select(Enum.Parse);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Common/Security/Policies/GuildSandbox/GuildSandboxRequirement.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Authorization;
2 |
3 | namespace ClemBot.Api.Common.Security.Policies.GuildSandbox;
4 |
5 | ///
6 | /// Marker class to define a request that is sandboxed to a particular guild
7 | ///
8 | public class GuildSandboxRequirement : IAuthorizationRequirement
9 | {
10 | }
11 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Common/Security/Policies/GuildSandbox/IGuildSandboxModel.cs:
--------------------------------------------------------------------------------
1 | namespace ClemBot.Api.Common.Security.Policies.GuildSandbox;
2 |
3 | public interface IGuildSandboxModel
4 | {
5 | ///
6 | /// Defines a GuildId to check in a policy to ensure a request
7 | /// isn't escaping the bounds of its authorized guild
8 | ///
9 | ulong GuildId { get; init; }
10 | }
11 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Common/Security/Policies/GuildSandbox/IGuildUserSandboxModel.cs:
--------------------------------------------------------------------------------
1 | namespace ClemBot.Api.Common.Security.Policies.GuildSandbox;
2 |
3 | public interface IGuildUserSandboxModel : IGuildSandboxModel
4 | {
5 | ///
6 | /// Defines a UserId to check in a policy to ensure a request
7 | /// isn't escaping the bounds of its authorized user
8 | ///
9 | public ulong UserId { get; init; }
10 | }
11 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Common/Security/Policies/IPolicyParser.cs:
--------------------------------------------------------------------------------
1 | namespace ClemBot.Api.Common.Security.Policies;
2 |
3 | public interface IPolicyParser
4 | {
5 | ///
6 | /// Defines a method to Parse out a given policies value
7 | /// that is provided to the PolicyProvider
8 | ///
9 | /// Value to Serialize
10 | /// Serialized Policy Values
11 | public string Serialize(T? t);
12 |
13 | ///
14 | /// Defines a method to Deserialize out a given policies value
15 | /// that is provided to the PolicyProvider
16 | ///
17 | /// String to deserialize into a T
18 | /// Deserialized Policy Values
19 | public T? Deserialize(string val);
20 | }
21 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Common/Security/Policies/Policies.cs:
--------------------------------------------------------------------------------
1 | namespace ClemBot.Api.Common.Security.Policies;
2 |
3 | public static class Policies
4 | {
5 | public const string BotMaster = "BotMaster";
6 | public const string RequireClaim = "RequireClaim";
7 | public const string GuildSandbox = "GuildSandbox";
8 | public const string UserId = "UserId";
9 | }
10 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Common/SeqConstants.cs:
--------------------------------------------------------------------------------
1 | namespace ClemBot.Api.Common;
2 |
3 | public static class Constants
4 | {
5 |
6 | ///
7 | /// Configured max seq logging body size, if a log message exceeds this we will lose the message
8 | ///
9 | public static int SeqBodySizeMax => 262144;
10 | }
11 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Common/Utilities/AuthorizeResult.cs:
--------------------------------------------------------------------------------
1 | namespace ClemBot.Api.Common.Utilities;
2 |
3 | public class AuthorizeResult : IResult
4 | {
5 | public T? Value { get; }
6 |
7 | public AuthorizeStatus Status { get; }
8 |
9 | private AuthorizeResult(AuthorizeStatus status)
10 | {
11 | Status = status;
12 | }
13 |
14 | private AuthorizeResult(T? val, AuthorizeStatus status)
15 | {
16 | Status = status;
17 | Value = val;
18 | }
19 |
20 | public static AuthorizeResult Success(T? val = default)
21 | => new(val, AuthorizeStatus.Success);
22 |
23 | public static AuthorizeResult Forbidden()
24 | => new(AuthorizeStatus.Forbidden);
25 |
26 | public static AuthorizeResult Failure()
27 | => new(AuthorizeStatus.Failure);
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Common/Utilities/AuthorizeStatus.cs:
--------------------------------------------------------------------------------
1 | namespace ClemBot.Api.Common.Utilities;
2 |
3 | public enum AuthorizeStatus
4 | {
5 | Success,
6 | Forbidden,
7 | Failure
8 | }
9 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Common/Utilities/IResult.cs:
--------------------------------------------------------------------------------
1 | namespace ClemBot.Api.Common.Utilities;
2 |
3 | public interface IResult
4 | {
5 | T? Value { get; }
6 | U Status { get; }
7 | }
8 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Common/Utilities/LinqExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace ClemBot.Api.Common
2 | {
3 | public static class LinqExtensions
4 | {
5 | public static IQueryable QueryIfElse(this IQueryable query,
6 | Func predicate,
7 | Func, IQueryable> @if,
8 | Func, IQueryable> @else) =>
9 | predicate() ? @if(query) : @else(query);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Common/Utilities/QueryResult.cs:
--------------------------------------------------------------------------------
1 | namespace ClemBot.Api.Common.Utilities;
2 |
3 | public class QueryResult : IResult
4 | {
5 | public T? Value { get; }
6 | public QueryStatus Status { get; }
7 |
8 | public QueryResult(QueryStatus status)
9 | {
10 | Status = status;
11 | }
12 |
13 | private QueryResult(T? val, QueryStatus status)
14 | {
15 | Value = val;
16 | Status = status;
17 | }
18 |
19 | public static QueryResult Success(T? val = default)
20 | => new(val, QueryStatus.Success);
21 |
22 | public static QueryResult Invalid(T? val = default)
23 | => new(val, QueryStatus.Invalid);
24 |
25 | public static QueryResult NotFound()
26 | => new(default, QueryStatus.NotFound);
27 |
28 | public static QueryResult Conflict()
29 | => new(default, QueryStatus.Conflict);
30 |
31 | public static QueryResult Forbidden()
32 | => new(default, QueryStatus.Forbidden);
33 |
34 | public static QueryResult NoContent()
35 | => new(default, QueryStatus.NoContent);
36 | }
37 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Common/Utilities/QueryStatus.cs:
--------------------------------------------------------------------------------
1 | namespace ClemBot.Api.Common.Utilities;
2 |
3 | public enum QueryStatus
4 | {
5 | Success,
6 | Invalid,
7 | NotFound,
8 | Conflict,
9 | Forbidden,
10 | NoContent
11 | }
12 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Common/Utilities/Result.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace ClemBot.Api.Common.Utilities;
3 |
4 | ///
5 | /// Result class that allows for Monad inspired result reporting
6 | ///
7 | ///
8 | ///
9 | public class Result : IResult
10 | {
11 | public T? Value { get; }
12 |
13 | public U Status { get; }
14 |
15 | public Result(T? val, U status)
16 | {
17 | Value = val;
18 | Status = status;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Core/Behaviors/CacheRequestBehavior.cs:
--------------------------------------------------------------------------------
1 | using ClemBot.Api.Common.Extensions;
2 | using ClemBot.Api.Services.Caching;
3 | using LazyCache;
4 |
5 | namespace ClemBot.Api.Core.Behaviors;
6 |
7 | public class CacheRequestBehavior :
8 | IPipelineBehavior where TRequest : ICacheRequest, IRequest
9 | {
10 | private readonly ILogger> _logger;
11 |
12 | private readonly IAppCache _cache;
13 |
14 | public CacheRequestBehavior(ILogger> logger, IAppCache cache)
15 | {
16 | _logger = logger;
17 | _cache = cache;
18 | }
19 |
20 | public async Task Handle(TRequest request,
21 | RequestHandlerDelegate next,
22 | CancellationToken cancellationToken)
23 | {
24 | _logger.LogInformation("Cache Request: {Request} for Id: {Id}", typeof(TRequest).Name, request.Id);
25 | var result = await next();
26 | _logger.LogInformation("{Cache} request for Id: {Id} completed with result: {Result}",
27 | typeof(TRequest).Name,
28 | request.Id,
29 | result);
30 |
31 | _logger.LogInformation("Current API internal cache size: {Size}", _cache.Size());
32 |
33 | return result;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Core/Behaviors/GuildSandboxAuthorizeBehavior.cs:
--------------------------------------------------------------------------------
1 | using System.Security.Claims;
2 | using ClemBot.Api.Common;
3 | using ClemBot.Api.Common.Security.Policies.GuildSandbox;
4 | using ClemBot.Api.Common.Utilities;
5 | using ClemBot.Api.Services.Authorization;
6 | using Microsoft.AspNetCore.Http;
7 | using Microsoft.AspNetCore.Mvc;
8 | using Microsoft.EntityFrameworkCore.Metadata.Internal;
9 |
10 | namespace ClemBot.Api.Core.Behaviors;
11 |
12 | public class GuildSandboxAuthorizeBehavior : IPipelineBehavior
13 | where TRequest : IGuildSandboxModel, IRequest
14 | {
15 | private readonly IGuildSandboxAuthorizeService _authorizeService;
16 |
17 | public GuildSandboxAuthorizeBehavior(IGuildSandboxAuthorizeService authorizeService)
18 | {
19 | _authorizeService = authorizeService;
20 | }
21 |
22 | public async Task Handle(TRequest request,
23 | RequestHandlerDelegate next,
24 | CancellationToken cancellationToken)
25 | {
26 | if (!await _authorizeService.AuthorizeUser(request))
27 | {
28 | var genericResultType = typeof(TResponse).GetGenericArguments().First();
29 | var resultType = typeof(QueryResult<>).MakeGenericType(genericResultType);
30 | return (TResponse)Activator.CreateInstance(resultType, QueryStatus.Forbidden)!;
31 | }
32 |
33 | return await next();
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Core/Behaviors/LoggingBehavior.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json;
2 | using ClemBot.Api.Common;
3 |
4 | namespace ClemBot.Api.Core.Behaviors;
5 |
6 | public class LoggingBehavior : IPipelineBehavior
7 | where TRequest : IRequest
8 | {
9 | private readonly ILogger> _logger;
10 |
11 | public LoggingBehavior(ILogger> logger)
12 | {
13 | _logger = logger;
14 | }
15 |
16 | public async Task Handle(TRequest request,
17 | RequestHandlerDelegate next,
18 | CancellationToken cancellationToken)
19 | {
20 | _logger.LogInformation("{Request} Request Data: {@Data}", typeof(TRequest), request);
21 | var response = await next();
22 |
23 | var bodyJson = JsonSerializer.Serialize(response);
24 |
25 | if (sizeof(char) * bodyJson.Length < Constants.SeqBodySizeMax)
26 | {
27 | _logger.LogInformation("{Response} Response Data: {Body}", typeof(TRequest), bodyJson);
28 | }
29 | else
30 | {
31 | _logger.LogInformation("Response Data with type {Type} exceeded max logging body size", typeof(TResponse));
32 | }
33 |
34 | return response;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Core/Features/Channels/Bot/Details.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using ClemBot.Api.Common.Utilities;
5 | using ClemBot.Api.Data.Contexts;
6 | using MediatR;
7 | using Microsoft.EntityFrameworkCore;
8 |
9 | namespace ClemBot.Api.Core.Features.Channels.Bot;
10 |
11 | public class Details
12 | {
13 | public class Query : IRequest>
14 | {
15 | public ulong Id { get; init; }
16 | }
17 |
18 | public class Model
19 | {
20 | public ulong Id { get; init; }
21 |
22 | public string? Name { get; init; }
23 |
24 | public ulong GuildId { get; init; }
25 | }
26 |
27 | public record QueryHandler(ClemBotContext _context) : IRequestHandler>
28 | {
29 | public async Task> Handle(Query request, CancellationToken cancellationToken)
30 | {
31 | var channel = await _context.Channels
32 | .Where(x => x.Id == request.Id && !x.IsThread)
33 | .FirstOrDefaultAsync();
34 |
35 | if (channel is null)
36 | {
37 | return QueryResult.NotFound();
38 | }
39 |
40 | return QueryResult.Success(new Model()
41 | {
42 | Id = channel.Id,
43 | Name = channel.Name,
44 | GuildId = channel.GuildId
45 | });
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Core/Features/Channels/Bot/Edit.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 | using ClemBot.Api.Common.Utilities;
4 | using ClemBot.Api.Data.Contexts;
5 | using FluentValidation;
6 | using MediatR;
7 | using Microsoft.EntityFrameworkCore;
8 |
9 | namespace ClemBot.Api.Core.Features.Channels.Bot;
10 |
11 | public class Edit
12 | {
13 | public class Validator : AbstractValidator
14 | {
15 | public Validator()
16 | {
17 | RuleFor(p => p.Id).NotNull();
18 | RuleFor(p => p.Name).NotNull();
19 | }
20 | }
21 |
22 | public class Command : IRequest>
23 |
24 | {
25 | public ulong Id { get; set; }
26 |
27 | public string Name { get; set; } = null!;
28 | }
29 |
30 | public record Handler(ClemBotContext _context) : IRequestHandler>
31 | {
32 | public async Task> Handle(Command request, CancellationToken cancellationToken)
33 | {
34 | var channel = await _context.Channels
35 | .FirstOrDefaultAsync(g => g.Id == request.Id);
36 |
37 | if (channel is null)
38 | {
39 | return QueryResult.NotFound();
40 | }
41 |
42 | channel.Name = request.Name;
43 | await _context.SaveChangesAsync();
44 |
45 | return QueryResult.Success(channel.Id);
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Core/Features/Channels/Bot/Index.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 | using ClemBot.Api.Common.Utilities;
6 | using ClemBot.Api.Data.Contexts;
7 | using MediatR;
8 | using Microsoft.EntityFrameworkCore;
9 |
10 | namespace ClemBot.Api.Core.Features.Channels.Bot;
11 |
12 | public class Index
13 | {
14 | public class Query : IRequest>>
15 | {
16 | }
17 |
18 | public class Model
19 | {
20 | public ulong Id { get; set; }
21 |
22 | public string Name { get; set; } = null!;
23 |
24 | public ulong GuildId { get; set; }
25 | }
26 |
27 | public record Handler(ClemBotContext _context) : IRequestHandler>>
28 | {
29 | public async Task>> Handle(Query request, CancellationToken cancellationToken)
30 | {
31 | var channels = await _context.Channels
32 | .Where(x => !x.IsThread)
33 | .ToListAsync();
34 |
35 | if (!channels.Any())
36 | {
37 | return QueryResult>.NotFound();
38 | }
39 |
40 | return QueryResult>.Success(
41 | channels.Select(x => new Model()
42 | {
43 | Id = x.Id,
44 | Name = x.Name,
45 | GuildId = x.GuildId
46 | }));
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Core/Features/ClaimMappings/ClaimsController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using ClemBot.Api.Common.Security.Policies.BotMaster;
4 | using ClemBot.Api.Common.Utilities;
5 | using MediatR;
6 | using Microsoft.AspNetCore.Authorization;
7 | using Microsoft.AspNetCore.Mvc;
8 |
9 | namespace ClemBot.Api.Core.Features.ClaimMappings;
10 |
11 | [ApiController]
12 | [Route("api")]
13 | public class ClaimMappingsController : ControllerBase
14 | {
15 | private readonly IMediator _mediator;
16 |
17 | public ClaimMappingsController(IMediator mediator)
18 | {
19 | _mediator = mediator;
20 | }
21 |
22 | [HttpPost("bot/[controller]")]
23 | [BotMasterAuthorize]
24 | public async Task Create(Bot.Create.Command command) =>
25 | await _mediator.Send(command) switch
26 | {
27 | { Status: QueryStatus.Success } result => Ok(result.Value),
28 | { Status: QueryStatus.Conflict } => Conflict(),
29 | _ => throw new InvalidOperationException()
30 | };
31 |
32 | [HttpDelete("bot/[controller]")]
33 | [BotMasterAuthorize]
34 | public async Task Delete(Bot.Delete.Command command) =>
35 | await _mediator.Send(command) switch
36 | {
37 | { Status: QueryStatus.Success } result => Ok(result.Value),
38 | { Status: QueryStatus.NotFound } => NotFound(),
39 | _ => throw new InvalidOperationException()
40 | };
41 | }
42 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Core/Features/Commands/Bot/AddInvocation.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using ClemBot.Api.Common.Utilities;
5 | using ClemBot.Api.Data.Contexts;
6 | using ClemBot.Api.Data.Models;
7 | using MediatR;
8 | using NodaTime;
9 | using NodaTime.Extensions;
10 |
11 | namespace ClemBot.Api.Core.Features.Commands.Bot;
12 |
13 | public class AddInvocation
14 | {
15 | public class Command : IRequest>
16 | {
17 | public string CommandName { get; set; } = null!;
18 |
19 | public ulong GuildId { get; set; }
20 |
21 | public ulong ChannelId { get; set; }
22 |
23 | public ulong UserId { get; set; }
24 | }
25 |
26 | public record Handler(ClemBotContext _context, IMediator _mediator)
27 | : IRequestHandler>
28 | {
29 | public async Task> Handle(Command request, CancellationToken cancellationToken)
30 | {
31 | var invocationEntity = new CommandInvocation
32 | {
33 | CommandName = request.CommandName,
34 | Time = SystemClock.Instance.InZone(DateTimeZone.Utc).GetCurrentLocalDateTime(),
35 | GuildId = request.GuildId,
36 | ChannelId = request.ChannelId,
37 | UserId = request.UserId
38 | };
39 |
40 | _context.CommandInvocations.Add(invocationEntity);
41 |
42 | await _context.SaveChangesAsync();
43 |
44 | return QueryResult.Success(invocationEntity.Id);
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Core/Features/CustomPrefixes/CustomPrefixesController.cs:
--------------------------------------------------------------------------------
1 | using ClemBot.Api.Common.Security.Policies.BotMaster;
2 | using Microsoft.AspNetCore.Mvc;
3 |
4 | namespace ClemBot.Api.Core.Features.CustomPrefixes;
5 |
6 | [ApiController]
7 | [Route("api")]
8 | public class CustomPrefixesController : ControllerBase
9 | {
10 | private readonly IMediator _mediator;
11 |
12 | public CustomPrefixesController(IMediator mediator)
13 | {
14 | _mediator = mediator;
15 | }
16 |
17 | [HttpPost("[controller]/Add")]
18 | [GuildSandboxAuthorize(BotAuthClaims.custom_prefix_set)]
19 | public async Task Add(Set.Command command) =>
20 | await _mediator.Send(command) switch
21 | {
22 | { Status: QueryStatus.Success } result => Ok(result.Value),
23 | { Status: QueryStatus.Forbidden } => Forbid(),
24 | _ => throw new InvalidOperationException()
25 | };
26 |
27 | [HttpDelete("bot/[controller]/Delete")]
28 | [BotMasterAuthorize]
29 | public async Task Delete(Bot.Delete.Command command) =>
30 | await _mediator.Send(command) switch
31 | {
32 | { Status: QueryStatus.Success } result => Ok(result.Value),
33 | _ => throw new InvalidOperationException()
34 | };
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Core/Features/DesignatedChannels/Bot/Details.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 | using ClemBot.Api.Common.Utilities;
6 | using ClemBot.Api.Data.Contexts;
7 | using ClemBot.Api.Services.Caching.DesignatedChannels.Models;
8 | using MediatR;
9 | using Microsoft.EntityFrameworkCore;
10 |
11 | namespace ClemBot.Api.Core.Features.DesignatedChannels.Bot;
12 |
13 | public class Details
14 | {
15 | public class Command : IRequest>
16 | {
17 | public ulong GuildId { get; init; }
18 |
19 | public Common.Enums.DesignatedChannels Designation { get; set; }
20 | }
21 |
22 | public class Model
23 | {
24 | public IEnumerable Mappings { get; set; } = null!;
25 | }
26 |
27 | public class QueryHandler : IRequestHandler>
28 | {
29 | private readonly IMediator _mediator;
30 |
31 | public QueryHandler(IMediator mediator)
32 | {
33 | _mediator = mediator;
34 | }
35 |
36 | public async Task> Handle(Command request, CancellationToken cancellationToken)
37 | {
38 | var channels = await _mediator.Send(new GetDesignatedChannelDetailRequest { Id = request.GuildId, Designation = request.Designation });
39 |
40 | return QueryResult.Success(new Model
41 | {
42 | Mappings = channels.Select(x => x.ChannelId)
43 | });
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Core/Features/DesignatedChannels/Bot/Index.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 | using ClemBot.Api.Common.Utilities;
6 | using ClemBot.Api.Data.Contexts;
7 | using MediatR;
8 | using Microsoft.EntityFrameworkCore;
9 |
10 | namespace ClemBot.Api.Core.Features.DesignatedChannels.Bot;
11 |
12 | public class Index
13 | {
14 | public class Query : IRequest>>
15 | {
16 | public Common.Enums.DesignatedChannels Designation { get; set; }
17 | }
18 |
19 | public class QueryHandler :
20 | IRequestHandler>>
21 | {
22 | private ClemBotContext _context { get; init; }
23 |
24 | public QueryHandler(ClemBotContext context)
25 | {
26 | _context = context;
27 | }
28 |
29 | public async Task>> Handle(Query request, CancellationToken cancellationToken)
30 | {
31 | var designatedChannels = await _context.DesignatedChannelMappings
32 | .Where(x => x.Type == request.Designation)
33 | .Select(y => y.ChannelId)
34 | .ToListAsync();
35 |
36 | return QueryResult>.Success(designatedChannels);
37 | }
38 |
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Core/Features/GuildSettings/Details.cs:
--------------------------------------------------------------------------------
1 | using ClemBot.Api.Common;
2 | using ClemBot.Api.Data.Contexts;
3 | using ClemBot.Api.Services.GuildSettings;
4 |
5 | namespace ClemBot.Api.Core.Features.GuildSettings;
6 |
7 | public class Details
8 | {
9 | public class Query : IGuildSandboxModel, IRequest>
10 | {
11 | public ulong GuildId { get; init; }
12 |
13 | public ConfigSettings Setting { get; init; }
14 | }
15 |
16 | public class Model : IResponseModel
17 | {
18 | public ConfigSettings Setting { get; init; }
19 |
20 | public object? Value { get; init; }
21 | }
22 |
23 | public class QueryHandler : IRequestHandler>
24 | {
25 | private readonly IGuildSettingsService _settingsService;
26 |
27 | public QueryHandler(IGuildSettingsService settingsService)
28 | {
29 | _settingsService = settingsService;
30 | }
31 |
32 | public async Task> Handle(Query request, CancellationToken cancellationToken)
33 | {
34 | var setting = await _settingsService.GetPropertyAsync(request.Setting, request.GuildId);
35 |
36 | return QueryResult.Success(
37 | new Model
38 | {
39 | Setting = request.Setting,
40 | Value = setting
41 | });
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Core/Features/GuildSettings/Index.cs:
--------------------------------------------------------------------------------
1 | using ClemBot.Api.Common;
2 | using ClemBot.Api.Services.GuildSettings;
3 |
4 | namespace ClemBot.Api.Core.Features.GuildSettings;
5 |
6 | public class Index
7 | {
8 | public class Query : IGuildSandboxModel, IRequest>
9 | {
10 | public ulong GuildId { get; init; }
11 |
12 | public ConfigSettings Setting { get; init; }
13 | }
14 |
15 | public class Model : IResponseModel
16 | {
17 | public Dictionary Settings { get; init; } = null!;
18 | }
19 |
20 | public class QueryHandler : IRequestHandler>
21 | {
22 | private readonly IGuildSettingsService _settingsService;
23 |
24 | public QueryHandler(IGuildSettingsService settingsService)
25 | {
26 | _settingsService = settingsService;
27 | }
28 |
29 | public async Task> Handle(Query request, CancellationToken cancellationToken)
30 | {
31 | var settings = await _settingsService.GetAllSettingsAsync(request.GuildId);
32 |
33 | return QueryResult.Success(new Model { Settings = settings });
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Core/Features/Guilds/Bot/Channels.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 | using ClemBot.Api.Common.Utilities;
6 | using ClemBot.Api.Data.Contexts;
7 | using MediatR;
8 | using Microsoft.EntityFrameworkCore;
9 |
10 | namespace ClemBot.Api.Core.Features.Guilds.Bot;
11 |
12 | public class Channels
13 | {
14 | public class Query : IRequest>>
15 | {
16 | public ulong Id { get; init; }
17 | }
18 |
19 | public class Model
20 | {
21 | public ulong Id { get; init; }
22 |
23 | public string? Name { get; init; }
24 | }
25 |
26 | public record QueryHandler(ClemBotContext _context)
27 | : IRequestHandler>>
28 | {
29 | public async Task>> Handle(Query request,
30 | CancellationToken cancellationToken)
31 | {
32 | var channels = await _context.Channels
33 | .Where(x => x.GuildId == request.Id && !x.IsThread)
34 | .ToListAsync();
35 |
36 | if (channels is null)
37 | {
38 | return QueryResult>.NotFound();
39 | }
40 |
41 | return QueryResult>.Success(channels
42 | .Select(c => new Model
43 | {
44 | Id = c.Id,
45 | Name = c.Name
46 | }));
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Core/Features/Guilds/Bot/Delete.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 | using ClemBot.Api.Common.Utilities;
4 | using ClemBot.Api.Data.Contexts;
5 | using MediatR;
6 | using Microsoft.EntityFrameworkCore;
7 |
8 | namespace ClemBot.Api.Core.Features.Guilds.Bot;
9 |
10 | public class Delete
11 | {
12 | public class Query : IRequest>
13 | {
14 | public ulong Id { get; set; }
15 | }
16 |
17 | public class Model
18 | {
19 | public ulong Id { get; init; }
20 |
21 | public string? Name { get; init; }
22 | }
23 |
24 | public record QueryHandler(ClemBotContext _context) : IRequestHandler>
25 | {
26 | public async Task> Handle(Query request, CancellationToken cancellationToken)
27 | {
28 | var guild = await _context.Guilds
29 | .FirstOrDefaultAsync(g => g.Id == request.Id);
30 |
31 | if (guild is null)
32 | {
33 | return QueryResult.NotFound();
34 | }
35 |
36 | _context.Guilds.Remove(guild);
37 | await _context.SaveChangesAsync();
38 |
39 | return QueryResult.Success(new Model { Id = guild.Id, Name = guild.Name });
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Core/Features/Guilds/Bot/DeleteWelcomeMessage.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using ClemBot.Api.Common.Utilities;
5 | using ClemBot.Api.Data.Contexts;
6 | using FluentValidation;
7 | using MediatR;
8 | using Microsoft.EntityFrameworkCore;
9 |
10 | namespace ClemBot.Api.Core.Features.Guilds.Bot;
11 |
12 | public class DeleteWelcomeMessage
13 | {
14 | public class Validator : AbstractValidator
15 | {
16 | public Validator()
17 | {
18 | RuleFor(p => p.Id).NotNull();
19 | }
20 | }
21 |
22 | public record Command : IRequest>
23 | {
24 | public ulong Id { get; set; }
25 | }
26 |
27 | public record Handler(ClemBotContext _context) : IRequestHandler>
28 | {
29 | public async Task> Handle(Command request, CancellationToken cancellationToken)
30 | {
31 | var guild = await _context.Guilds.FirstOrDefaultAsync(x => x.Id == request.Id);
32 |
33 | if (guild is null)
34 | {
35 | return QueryResult.NotFound();
36 | }
37 |
38 | guild.WelcomeMessage = null;
39 | await _context.SaveChangesAsync();
40 |
41 | return QueryResult.Success(guild.Id);
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Core/Features/Guilds/Bot/DesignatedChannels.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 | using ClemBot.Api.Common.Utilities;
6 | using ClemBot.Api.Data.Contexts;
7 | using MediatR;
8 | using Microsoft.EntityFrameworkCore;
9 |
10 | namespace ClemBot.Api.Core.Features.Guilds.Bot;
11 |
12 | public class DesignatedChannels
13 | {
14 | public class Query : IRequest>>
15 | {
16 | public ulong Id { get; set; }
17 | }
18 |
19 | public class Model
20 | {
21 | public Common.Enums.DesignatedChannels Designation { get; set; }
22 |
23 | public IEnumerable ChannelIds { get; set; } = new List();
24 | }
25 |
26 | public record Handler(ClemBotContext _context) :
27 | IRequestHandler>>
28 | {
29 | public async Task>> Handle(Query request, CancellationToken cancellationToken)
30 | {
31 | var designatedChannels = await _context.DesignatedChannelMappings
32 | .Where(x => x.Channel.Guild.Id == request.Id)
33 | .ToListAsync();
34 |
35 | return QueryResult>.Success(designatedChannels
36 | .GroupBy(y => y.Type)
37 | .Select(y => new Model()
38 | {
39 | Designation = y.Key,
40 | ChannelIds = y.Select(z => z.ChannelId)
41 | }));
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Core/Features/Guilds/Bot/Details.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 | using ClemBot.Api.Common.Utilities;
6 | using ClemBot.Api.Data.Contexts;
7 | using MediatR;
8 | using Microsoft.EntityFrameworkCore;
9 |
10 | namespace ClemBot.Api.Core.Features.Guilds.Bot;
11 |
12 | public class Details
13 | {
14 | public class Query : IRequest>
15 | {
16 | public ulong Id { get; set; }
17 | }
18 |
19 | public class Model
20 | {
21 | public ulong Id { get; set; }
22 |
23 | public string? Name { get; set; }
24 |
25 | public string? WelcomeMessage { get; set; }
26 | }
27 |
28 | public record QueryHandler(ClemBotContext _context) : IRequestHandler>
29 | {
30 | public async Task> Handle(Query request, CancellationToken cancellationToken)
31 | {
32 | var guild = await _context.Guilds
33 | .FirstOrDefaultAsync(x => x.Id == request.Id);
34 |
35 | if (guild is null)
36 | {
37 | return QueryResult.NotFound();
38 | }
39 |
40 | return QueryResult.Success(new Model
41 | {
42 | Id = guild.Id,
43 | Name = guild.Name,
44 | WelcomeMessage = guild.WelcomeMessage,
45 | });
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Core/Features/Guilds/Bot/Edit.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 | using ClemBot.Api.Common.Utilities;
4 | using ClemBot.Api.Data.Contexts;
5 | using FluentValidation;
6 | using MediatR;
7 | using Microsoft.EntityFrameworkCore;
8 |
9 | namespace ClemBot.Api.Core.Features.Guilds.Bot;
10 |
11 | public class Edit
12 | {
13 | public class Validator : AbstractValidator
14 | {
15 | public Validator()
16 | {
17 | RuleFor(p => p.Id).NotNull();
18 | RuleFor(p => p.Name).NotNull();
19 | }
20 | }
21 |
22 | public class Command : IRequest>
23 | {
24 | public ulong Id { get; set; }
25 |
26 | public string Name { get; set; } = null!;
27 |
28 | public ulong OwnerId { get; set; }
29 | }
30 |
31 | public record Handler(ClemBotContext _context) : IRequestHandler>
32 | {
33 | public async Task> Handle(Command request, CancellationToken cancellationToken)
34 | {
35 | var guild = await _context.Guilds
36 | .FirstOrDefaultAsync(g => g.Id == request.Id);
37 |
38 | if (guild is null)
39 | {
40 | return QueryResult.NotFound();
41 | }
42 |
43 | guild.Name = request.Name;
44 | guild.OwnerId = request.OwnerId;
45 | await _context.SaveChangesAsync();
46 |
47 | return QueryResult.Success(guild.Id);
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Core/Features/Guilds/Bot/Index.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 | using ClemBot.Api.Common.Utilities;
6 | using ClemBot.Api.Data.Contexts;
7 | using MediatR;
8 | using Microsoft.EntityFrameworkCore;
9 |
10 | namespace ClemBot.Api.Core.Features.Guilds.Bot;
11 |
12 | public class Index
13 | {
14 | public class Query : IRequest>>
15 | {
16 | }
17 |
18 | public class Model
19 | {
20 | public ulong Id { get; set; }
21 |
22 | public string Name { get; set; } = null!;
23 |
24 | public string? WelcomeMessage { get; set; }
25 | }
26 |
27 | public record Handler(ClemBotContext _context) : IRequestHandler>>
28 | {
29 | public async Task>> Handle(Query request,
30 | CancellationToken cancellationToken)
31 | {
32 | var guilds = await _context.Guilds.ToListAsync();
33 |
34 | if (!guilds.Any())
35 | {
36 | return QueryResult>.NotFound();
37 | }
38 |
39 | return QueryResult>.Success(
40 | guilds.Select(x => new Model { Id = x.Id, Name = x.Name, WelcomeMessage = x.WelcomeMessage }));
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Core/Features/Guilds/Bot/RemoveUser.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using ClemBot.Api.Common.Utilities;
5 | using ClemBot.Api.Data.Contexts;
6 | using ClemBot.Api.Data.Models;
7 | using FluentValidation;
8 | using LinqToDB;
9 | using MediatR;
10 | using Microsoft.EntityFrameworkCore;
11 |
12 | namespace ClemBot.Api.Core.Features.Guilds.Bot;
13 |
14 | public class RemoveUser
15 | {
16 | public class Validator : AbstractValidator
17 | {
18 | public Validator()
19 | {
20 | RuleFor(p => p.GuildId).NotNull();
21 | RuleFor(p => p.UserId).NotNull();
22 | }
23 | }
24 |
25 | public class Command : IRequest>
26 | {
27 | public ulong GuildId { get; set; }
28 |
29 | public ulong UserId { get; set; }
30 | }
31 |
32 | public record Handler(ClemBotContext _context) : IRequestHandler>
33 | {
34 | public async Task> Handle(Command request, CancellationToken cancellationToken)
35 | {
36 | await _context.RoleUser
37 | .Where(u => u.UserId == request.UserId && u.Role.GuildId == request.GuildId)
38 | .DeleteAsync();
39 |
40 | await _context.GuildUser
41 | .Where(u => u.UserId == request.UserId && u.GuildId == request.GuildId)
42 | .DeleteAsync();
43 |
44 | return QueryResult.Success(request.GuildId);
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Core/Features/Guilds/GetCustomPrefixes.cs:
--------------------------------------------------------------------------------
1 | using ClemBot.Api.Common;
2 | using ClemBot.Api.Common.Security.Policies.GuildSandbox;
3 | using ClemBot.Api.Data.Contexts;
4 | using ClemBot.Api.Services.Caching.CustomPrefix.Models;
5 |
6 | namespace ClemBot.Api.Core.Features.Guilds;
7 |
8 | public class GetCustomPrefixes
9 | {
10 | public class Query : IGuildSandboxModel, IRequest>
11 | {
12 | public ulong GuildId { get; init; }
13 | }
14 |
15 | public class Model : IResponseModel
16 | {
17 | public IEnumerable? Prefixes { get; set; }
18 | }
19 |
20 | public record QueryHandler(ClemBotContext _context, IMediator _mediator)
21 | : IRequestHandler>
22 | {
23 | public async Task> Handle(Query request,
24 | CancellationToken cancellationToken)
25 | {
26 | var prefixes = await _mediator.Send(new GetCustomPrefixRequest { Id = request.GuildId });
27 |
28 | return QueryResult.Success(new Model { Prefixes = prefixes });
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Core/Features/Guilds/GetCustomTagPrefixes.cs:
--------------------------------------------------------------------------------
1 | using ClemBot.Api.Common;
2 | using ClemBot.Api.Common.Security.Policies.GuildSandbox;
3 | using ClemBot.Api.Data.Contexts;
4 | using ClemBot.Api.Services.Caching.CustomTagPrefix.Models;
5 |
6 | namespace ClemBot.Api.Core.Features.Guilds;
7 |
8 | public class GetCustomTagPrefixes
9 | {
10 | public class Query : IGuildSandboxModel, IRequest>
11 | {
12 | public ulong GuildId { get; init; }
13 | }
14 |
15 | public class Model : IResponseModel
16 | {
17 | public IEnumerable? TagPrefixes { get; set; }
18 | }
19 |
20 | public record QueryHandler(ClemBotContext _context, IMediator _mediator)
21 | : IRequestHandler>
22 | {
23 | public async Task> Handle(Query request,
24 | CancellationToken cancellationToken)
25 | {
26 | var tagprefixes = await _mediator.Send(new GetCustomTagPrefixRequest { Id = request.GuildId });
27 |
28 | return QueryResult.Success(new Model { TagPrefixes = tagprefixes });
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Core/Features/Guilds/GetWelcomeMessage.cs:
--------------------------------------------------------------------------------
1 | using ClemBot.Api.Common;
2 | using ClemBot.Api.Data.Contexts;
3 | using FluentValidation;
4 | using Microsoft.EntityFrameworkCore;
5 |
6 | namespace ClemBot.Api.Core.Features.Guilds;
7 |
8 | public class GetWelcomeMessage
9 | {
10 | public class Validator : AbstractValidator
11 | {
12 | public Validator()
13 | {
14 | RuleFor(p => p.GuildId).NotNull();
15 | }
16 | }
17 |
18 | public record Command : IGuildSandboxModel, IRequest>
19 | {
20 | public ulong GuildId { get; init; }
21 | }
22 |
23 | public record Model : IResponseModel
24 | {
25 | public string? Message { get; init; }
26 | }
27 |
28 | public record Handler(ClemBotContext _context) : IRequestHandler>
29 | {
30 | public async Task> Handle(Command request, CancellationToken cancellationToken)
31 | {
32 | var guild = await _context.Guilds
33 | .Select(x => new { x.Id, x.WelcomeMessage })
34 | .FirstOrDefaultAsync(x => x.Id == request.GuildId);
35 |
36 | if (guild is null)
37 | {
38 | return QueryResult.NotFound();
39 | }
40 |
41 | return QueryResult.Success(new Model { Message = guild.WelcomeMessage });
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Core/Features/HealthCheck/HealthCheckController.cs:
--------------------------------------------------------------------------------
1 | using ClemBot.Api.Common.Security.Policies.BotMaster;
2 | using Microsoft.AspNetCore.Mvc;
3 |
4 | namespace ClemBot.Api.Core.Features.HealthCheck;
5 |
6 | [ApiController]
7 | [Route("api")]
8 | public class HealthCheckController : ControllerBase
9 | {
10 | [HttpGet("[controller]/ping")]
11 | [BotMasterAuthorize]
12 | public IActionResult Ping() => Ok("pong!");
13 | }
14 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Core/Features/Infractions/Bot/Deactivate.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 | using ClemBot.Api.Common.Utilities;
4 | using ClemBot.Api.Data.Contexts;
5 | using MediatR;
6 | using Microsoft.EntityFrameworkCore;
7 |
8 | namespace ClemBot.Api.Core.Features.Infractions.Bot;
9 |
10 | public class Deactivate
11 | {
12 | public class Query : IRequest>
13 | {
14 | public int Id { get; set; }
15 | }
16 |
17 | public record QueryHandler(ClemBotContext _context) : IRequestHandler>
18 | {
19 | public async Task> Handle(Query request, CancellationToken cancellationToken)
20 | {
21 | var infraction = await _context.Infractions
22 | .FirstOrDefaultAsync(x => x.Id == request.Id);
23 |
24 | if (infraction is null)
25 | {
26 | return QueryResult.NotFound();
27 | }
28 |
29 | infraction.IsActive = false;
30 | await _context.SaveChangesAsync();
31 |
32 | return QueryResult.Success(infraction.Id);
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Core/Features/Infractions/Bot/Delete.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 | using ClemBot.Api.Common.Utilities;
4 | using ClemBot.Api.Data.Contexts;
5 | using MediatR;
6 | using Microsoft.EntityFrameworkCore;
7 |
8 | namespace ClemBot.Api.Core.Features.Infractions.Bot;
9 |
10 | public class Delete
11 | {
12 | public class Command : IRequest>
13 | {
14 | public int Id { get; init; }
15 | }
16 |
17 | public record QueryHandler(ClemBotContext _context) : IRequestHandler>
18 | {
19 | public async Task> Handle(Command request, CancellationToken cancellationToken)
20 | {
21 | var infraction = await _context.Infractions
22 | .FirstOrDefaultAsync(x => x.Id == request.Id);
23 |
24 | if (infraction is null)
25 | {
26 | return QueryResult.NotFound();
27 | }
28 |
29 | _context.Infractions.Remove(infraction);
30 |
31 | await _context.SaveChangesAsync();
32 |
33 | return QueryResult.Success(infraction.Id);
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Core/Features/Public/GlobalStats.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 | using ClemBot.Api.Common.Utilities;
6 | using ClemBot.Api.Data.Contexts;
7 | using ClemBot.Api.Services.Caching.GlobalStats.Models;
8 | using MediatR;
9 | using Microsoft.EntityFrameworkCore;
10 |
11 | namespace ClemBot.Api.Core.Features.Public;
12 |
13 | public class GlobalStats
14 | {
15 | public class Query : IRequest>
16 | {
17 | }
18 |
19 | public class Model
20 | {
21 | public int Guilds { get; set; }
22 |
23 | public int Users { get; set; }
24 |
25 | public int Commands { get; set; }
26 | }
27 |
28 | public record Handler(ClemBotContext _context, IMediator _mediator) : IRequestHandler>
29 | {
30 | public async Task> Handle(Query request,
31 | CancellationToken cancellationToken)
32 | {
33 | var guildsCount = await _mediator.Send(new GlobalGuildStatsRequest());
34 |
35 | var usersCount = await _mediator.Send(new GlobalUserStatsRequest());
36 |
37 | var commandsCount = await _mediator.Send(new GlobalCommandStatsRequest());
38 |
39 | return QueryResult.Success(new Model()
40 | {
41 | Guilds = guildsCount,
42 | Users = usersCount,
43 | Commands = commandsCount
44 | });
45 | }
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Core/Features/Public/PublicController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using ClemBot.Api.Common.Utilities;
4 | using MediatR;
5 | using Microsoft.AspNetCore.Authorization;
6 | using Microsoft.AspNetCore.Mvc;
7 |
8 | namespace ClemBot.Api.Core.Features.Public;
9 |
10 | [ApiController]
11 | [Route("api/public")]
12 | public class PublicController : ControllerBase
13 | {
14 | private readonly IMediator _mediator;
15 |
16 | public PublicController(IMediator mediator)
17 | {
18 | _mediator = mediator;
19 | }
20 |
21 | [HttpGet("GlobalStats")]
22 | [AllowAnonymous]
23 | public async Task GlobalStats() =>
24 | await _mediator.Send(new GlobalStats.Query()) switch
25 | {
26 | { Status: QueryStatus.Success } result => Ok(result.Value),
27 | _ => throw new InvalidOperationException()
28 | };
29 | }
30 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Core/Features/Reminders/Bot/Dispatch.cs:
--------------------------------------------------------------------------------
1 | using ClemBot.Api.Data.Contexts;
2 | using FluentValidation;
3 | using Microsoft.EntityFrameworkCore;
4 |
5 | namespace ClemBot.Api.Core.Features.Reminders.Bot;
6 |
7 | public class Dispatch
8 | {
9 |
10 | public class Validator : AbstractValidator
11 | {
12 | public Validator()
13 | {
14 | RuleFor(q => q.Id).NotNull();
15 | }
16 | }
17 |
18 | public class Query : IRequest>
19 | {
20 | public int Id { get; set; }
21 | }
22 |
23 | public class Handler : IRequestHandler>
24 | {
25 |
26 | private readonly ClemBotContext _context;
27 |
28 | public Handler(ClemBotContext context)
29 | {
30 | _context = context;
31 | }
32 |
33 | public async Task> Handle(Query request, CancellationToken cancellationToken)
34 | {
35 | var reminder = await _context.Reminders
36 | .FirstOrDefaultAsync(r => r.Id == request.Id);
37 |
38 | if (reminder is null)
39 | {
40 | return QueryResult.NotFound();
41 | }
42 |
43 | if (reminder.Dispatched)
44 | {
45 | return QueryResult.Conflict();
46 | }
47 |
48 | reminder.Dispatched = true;
49 | await _context.SaveChangesAsync();
50 |
51 | return QueryResult.Success(reminder.Id);
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Core/Features/Reminders/Bot/Index.cs:
--------------------------------------------------------------------------------
1 | using ClemBot.Api.Common;
2 | using ClemBot.Api.Data.Contexts;
3 | using Microsoft.EntityFrameworkCore;
4 | using NodaTime;
5 |
6 | namespace ClemBot.Api.Core.Features.Reminders.Bot;
7 |
8 | public class Index
9 | {
10 | public class ReminderDto : IResponseModel
11 | {
12 | public int Id { get; set; }
13 |
14 | public LocalDateTime Time { get; set; }
15 | }
16 |
17 | public class Query : IRequest>>
18 | {
19 | // empty
20 | }
21 |
22 | public class Handler : IRequestHandler>>
23 | {
24 |
25 | private readonly ClemBotContext _context;
26 |
27 | public Handler(ClemBotContext context)
28 | {
29 | _context = context;
30 | }
31 |
32 | public async Task>> Handle(Query request, CancellationToken cancellationToken)
33 | {
34 | var reminders = await _context.Reminders
35 | .Where(r => !r.Dispatched)
36 | .Select(item => new ReminderDto
37 | {
38 | Id = item.Id,
39 | Time = item.Time
40 | })
41 | .ToListAsync();
42 |
43 | return QueryResult>.Success(reminders);
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Core/Features/Roles/Bot/Delete.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 | using ClemBot.Api.Common.Utilities;
4 | using ClemBot.Api.Data.Contexts;
5 | using MediatR;
6 | using Microsoft.EntityFrameworkCore;
7 |
8 | namespace ClemBot.Api.Core.Features.Roles.Bot;
9 |
10 | public class Delete
11 | {
12 | public class Query : IRequest>
13 | {
14 | public ulong Id { get; set; }
15 | }
16 |
17 | public class Model
18 | {
19 | public ulong Id { get; init; }
20 |
21 | public string? Name { get; init; }
22 | }
23 |
24 | public record QueryHandler(ClemBotContext _context) : IRequestHandler>
25 | {
26 | public async Task> Handle(Query request, CancellationToken cancellationToken)
27 | {
28 | var role = await _context.Roles
29 | .FirstOrDefaultAsync(g => g.Id == request.Id);
30 |
31 | if (role is null)
32 | {
33 | return QueryResult.NotFound();
34 | }
35 |
36 | _context.Roles.Remove(role);
37 | await _context.SaveChangesAsync();
38 |
39 | return QueryResult.Success(new Model()
40 | {
41 | Id = role.Id,
42 | Name = role.Name
43 | });
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Core/Features/Roles/Bot/Index.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 | using ClemBot.Api.Common.Utilities;
6 | using ClemBot.Api.Data.Contexts;
7 | using MediatR;
8 | using Microsoft.EntityFrameworkCore;
9 |
10 | namespace ClemBot.Api.Core.Features.Roles.Bot;
11 |
12 | public class Index
13 | {
14 | public class Query : IRequest>>
15 | {
16 | }
17 |
18 | public class Model
19 | {
20 | public ulong Id { get; set; }
21 |
22 | public string Name { get; set; } = null!;
23 |
24 | public ulong GuildId { get; set; }
25 | }
26 |
27 | public record Handler(ClemBotContext _context) : IRequestHandler>>
28 | {
29 | public async Task>> Handle(Query request, CancellationToken cancellationToken)
30 | {
31 | var users = await _context.Roles.ToListAsync();
32 |
33 | if (!users.Any())
34 | {
35 | return QueryResult>.NotFound();
36 | }
37 |
38 | return QueryResult>.Success(
39 | users.Select(x => new Model()
40 | {
41 | Id = x.Id,
42 | Name = x.Name,
43 | GuildId = x.GuildId
44 | }));
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Core/Features/SlotScores/Bot/AddScore.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using ClemBot.Api.Common.Utilities;
5 | using ClemBot.Api.Data.Contexts;
6 | using ClemBot.Api.Data.Models;
7 | using MediatR;
8 | using NodaTime;
9 | using NodaTime.Extensions;
10 |
11 | namespace ClemBot.Api.Core.Features.Commands.Bot;
12 |
13 | public class AddScore
14 | {
15 | public class Command : IRequest>
16 | {
17 | public ulong Score { get; set; }
18 |
19 | public ulong GuildId { get; set; }
20 |
21 | public ulong UserId { get; set; }
22 | }
23 |
24 | public record Handler(ClemBotContext _context, IMediator _mediator)
25 | : IRequestHandler>
26 | {
27 | public async Task> Handle(Command request, CancellationToken cancellationToken)
28 | {
29 | var score = new SlotScore
30 | {
31 | Score = request.Score,
32 | GuildId = request.GuildId,
33 | UserId = request.UserId,
34 | Time = SystemClock.Instance.InZone(DateTimeZone.Utc).GetCurrentLocalDateTime(),
35 | };
36 |
37 | _context.SlotScores.Add(score);
38 |
39 | await _context.SaveChangesAsync();
40 |
41 | return QueryResult.Success(score.Id);
42 |
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Core/Features/SlotScores/SlotScoresController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using ClemBot.Api.Common.Security.Policies.BotMaster;
4 | using ClemBot.Api.Common.Utilities;
5 | using ClemBot.Api.Core.Features.Commands.Bot;
6 | using ClemBot.Api.Core.Features.Public;
7 | using MediatR;
8 | using Microsoft.AspNetCore.Authorization;
9 | using Microsoft.AspNetCore.Mvc;
10 |
11 | namespace ClemBot.Api.Core.Features.Commands;
12 |
13 | [ApiController]
14 | [Route("api")]
15 | public class SlotScoresController : ControllerBase
16 | {
17 | private readonly IMediator _mediator;
18 |
19 | public SlotScoresController(IMediator mediator)
20 | {
21 | _mediator = mediator;
22 | }
23 |
24 | [HttpPost("bot/[controller]")]
25 | [BotMasterAuthorize]
26 | public async Task AddScore(AddScore.Command command) =>
27 | await _mediator.Send(command) switch
28 | {
29 | { Status: QueryStatus.Success } result => Ok(result.Value),
30 | _ => throw new InvalidOperationException()
31 | };
32 | }
33 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Core/Features/Tags/Delete.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 | using ClemBot.Api.Common.Utilities;
4 | using ClemBot.Api.Data.Contexts;
5 | using MediatR;
6 | using Microsoft.EntityFrameworkCore;
7 |
8 | namespace ClemBot.Api.Core.Features.Tags;
9 |
10 | public class Delete
11 | {
12 | public class Command : IRequest>
13 | {
14 | public ulong GuildId { get; set; }
15 |
16 | public string Name { get; set; } = null!;
17 | }
18 |
19 | public class Model
20 | {
21 | public ulong Id { get; init; }
22 |
23 | public string? Name { get; init; }
24 |
25 | public string? Content { get; init; }
26 | }
27 |
28 | public record QueryHandler(ClemBotContext _context) : IRequestHandler>
29 | {
30 | public async Task> Handle(Command request, CancellationToken cancellationToken)
31 | {
32 | var tag = await _context.Tags
33 | .FirstOrDefaultAsync(g => g.GuildId == request.GuildId && g.Name == request.Name);
34 |
35 | if (tag is null)
36 | {
37 | return QueryResult.NotFound();
38 | }
39 |
40 | _context.Tags.Remove(tag);
41 |
42 | await _context.SaveChangesAsync();
43 |
44 | return QueryResult.Success(new Model()
45 | {
46 | Id = tag.GuildId,
47 | Name = tag.Name,
48 | Content = tag.Content
49 | });
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Core/Features/Threads/Bot/Delete.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 | using ClemBot.Api.Common.Utilities;
4 | using ClemBot.Api.Data.Contexts;
5 | using MediatR;
6 | using Microsoft.EntityFrameworkCore;
7 |
8 | namespace ClemBot.Api.Core.Features.Threads.Bot;
9 |
10 | public class Delete
11 | {
12 | public class Query : IRequest>
13 | {
14 | public ulong Id { get; set; }
15 | }
16 |
17 | public class Model
18 | {
19 | public ulong Id { get; init; }
20 |
21 | public string? Name { get; init; }
22 | }
23 |
24 | public record QueryHandler(ClemBotContext _context) : IRequestHandler>
25 | {
26 | public async Task> Handle(Query request, CancellationToken cancellationToken)
27 | {
28 | var channel = await _context.Channels
29 | .FirstOrDefaultAsync(g => g.Id == request.Id);
30 |
31 | if (channel is null)
32 | {
33 | return QueryResult.NotFound();
34 | }
35 |
36 | _context.Channels.Remove(channel);
37 | await _context.SaveChangesAsync();
38 |
39 | return QueryResult.Success(new Model()
40 | {
41 | Id = channel.Id,
42 | Name = channel.Name
43 | });
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Core/Features/Threads/Bot/Details.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using ClemBot.Api.Common.Utilities;
5 | using ClemBot.Api.Data.Contexts;
6 | using MediatR;
7 | using Microsoft.EntityFrameworkCore;
8 |
9 | namespace ClemBot.Api.Core.Features.Threads.Bot;
10 |
11 | public class Details
12 | {
13 | public class Query : IRequest>
14 | {
15 | public ulong Id { get; init; }
16 | }
17 |
18 | public class Model
19 | {
20 | public ulong Id { get; init; }
21 |
22 | public string? Name { get; init; }
23 |
24 | public ulong GuildId { get; init; }
25 | }
26 |
27 | public record QueryHandler(ClemBotContext _context) : IRequestHandler>
28 | {
29 | public async Task> Handle(Query request, CancellationToken cancellationToken)
30 | {
31 | var channel = await _context.Channels
32 | .Where(x => x.Id == request.Id && x.IsThread)
33 | .FirstOrDefaultAsync();
34 |
35 | if (channel is null)
36 | {
37 | return QueryResult.NotFound();
38 | }
39 |
40 | return QueryResult.Success(new Model()
41 | {
42 | Id = channel.Id,
43 | Name = channel.Name,
44 | GuildId = channel.GuildId
45 | });
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Core/Features/Threads/Bot/Edit.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 | using ClemBot.Api.Common.Utilities;
4 | using ClemBot.Api.Data.Contexts;
5 | using FluentValidation;
6 | using MediatR;
7 | using Microsoft.EntityFrameworkCore;
8 |
9 | namespace ClemBot.Api.Core.Features.Threads.Bot;
10 |
11 | public class Edit
12 | {
13 | public class Validator : AbstractValidator
14 | {
15 | public Validator()
16 | {
17 | RuleFor(p => p.Id).NotNull();
18 | RuleFor(p => p.Name).NotNull();
19 | }
20 | }
21 |
22 | public class Command : IRequest>
23 |
24 | {
25 | public ulong Id { get; set; }
26 |
27 | public string Name { get; set; } = null!;
28 | }
29 |
30 | public record Handler(ClemBotContext _context) : IRequestHandler>
31 | {
32 | public async Task> Handle(Command request, CancellationToken cancellationToken)
33 | {
34 | var channel = await _context.Channels
35 | .FirstOrDefaultAsync(g => g.Id == request.Id);
36 |
37 | if (channel is null)
38 | {
39 | return QueryResult.NotFound();
40 | }
41 |
42 | channel.Name = request.Name;
43 | await _context.SaveChangesAsync();
44 |
45 | return QueryResult.Success(channel.Id);
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Core/Features/Users/Bot/Create.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using ClemBot.Api.Common.Utilities;
5 | using ClemBot.Api.Data.Contexts;
6 | using ClemBot.Api.Data.Models;
7 | using FluentValidation;
8 | using MediatR;
9 | using Microsoft.EntityFrameworkCore;
10 |
11 | namespace ClemBot.Api.Core.Features.Users.Bot;
12 |
13 | public class Create
14 | {
15 | public class Validator : AbstractValidator
16 | {
17 | public Validator()
18 | {
19 | RuleFor(p => p.Id).NotNull();
20 | RuleFor(p => p.Name).NotNull();
21 | }
22 | }
23 |
24 | public class Command : IRequest>
25 | {
26 | public ulong Id { get; set; }
27 |
28 | public string Name { get; set; } = null!;
29 | }
30 |
31 | public record Handler(ClemBotContext _context) : IRequestHandler>
32 | {
33 | public async Task> Handle(Command request, CancellationToken cancellationToken)
34 | {
35 | var user = new User()
36 | {
37 | Id = request.Id,
38 | Name = request.Name,
39 | };
40 |
41 | if (await _context.Roles.Where(x => x.Id == user.Id).AnyAsync())
42 | {
43 | return QueryResult.Conflict();
44 | }
45 |
46 | _context.Users.Add(user);
47 | await _context.SaveChangesAsync();
48 |
49 | return QueryResult.Success(user.Id);
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Core/Features/Users/Bot/Details.cs:
--------------------------------------------------------------------------------
1 | using ClemBot.Api.Common.Utilities;
2 | using ClemBot.Api.Data.Contexts;
3 | using MediatR;
4 | using Microsoft.EntityFrameworkCore;
5 |
6 | namespace ClemBot.Api.Core.Features.Users.Bot;
7 |
8 | public class Details
9 | {
10 | public class Query : IRequest>
11 | {
12 | public ulong Id { get; set; }
13 | }
14 |
15 | public class Model
16 | {
17 | public ulong Id { get; set; }
18 |
19 | public string? Name { get; set; }
20 |
21 | public List Guilds { get; set; } = new();
22 | }
23 |
24 | public record QueryHandler(ClemBotContext _context) : IRequestHandler>
25 | {
26 | public async Task> Handle(Query request, CancellationToken cancellationToken)
27 | {
28 | var user = await _context.Users
29 | .Where(x => x.Id == request.Id)
30 | .Include(y => y.Guilds)
31 | .FirstOrDefaultAsync();
32 |
33 | if (user is null)
34 | {
35 | return QueryResult.NotFound();
36 | }
37 |
38 | return QueryResult.Success(new Model()
39 | {
40 | Id = user.Id,
41 | Name = user.Name,
42 | Guilds = user.Guilds.Select(x => x.Id).ToList()
43 | });
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Core/Features/Users/Bot/Edit.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 | using ClemBot.Api.Common.Utilities;
4 | using ClemBot.Api.Data.Contexts;
5 | using FluentValidation;
6 | using MediatR;
7 | using Microsoft.EntityFrameworkCore;
8 |
9 | namespace ClemBot.Api.Core.Features.Users.Bot;
10 |
11 | public class Edit
12 | {
13 | public class Validator : AbstractValidator
14 | {
15 | public Validator()
16 | {
17 | RuleFor(p => p.Id).NotNull();
18 | RuleFor(p => p.Name).NotNull();
19 | }
20 | }
21 |
22 | public class Command : IRequest>
23 | {
24 | public ulong Id { get; set; }
25 |
26 | public string Name { get; set; } = null!;
27 | }
28 |
29 | public record Handler(ClemBotContext _context) : IRequestHandler>
30 | {
31 | public async Task> Handle(Command request, CancellationToken cancellationToken)
32 | {
33 | var user = await _context.Users
34 | .FirstOrDefaultAsync(g => g.Id == request.Id);
35 |
36 | if (user is null)
37 | {
38 | return QueryResult.NotFound();
39 | }
40 |
41 | user.Name = request.Name;
42 | await _context.SaveChangesAsync();
43 |
44 | return QueryResult.Success(user.Id);
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Core/Features/Users/Bot/GetGuildClaims.cs:
--------------------------------------------------------------------------------
1 | using ClemBot.Api.Data.Contexts;
2 | using ClemBot.Api.Data.Extensions;
3 | using Microsoft.EntityFrameworkCore;
4 |
5 | namespace ClemBot.Api.Core.Features.Users.Bot;
6 |
7 | public class GetGuildClaims
8 | {
9 | public class Query : IRequest>>
10 | {
11 | public ulong GuildId { get; init; }
12 |
13 | public ulong UserId { get; init; }
14 | }
15 |
16 | public record QueryHandler(ClemBotContext _context) : IRequestHandler>>
17 | {
18 | public async Task>> Handle(Query request, CancellationToken cancellationToken)
19 | {
20 | var claims = await _context.Users.GetUserGuildClaimsAsync(request.GuildId, request.UserId);
21 | return QueryResult>.Success(claims);
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/ClemBot.Api/ClemBot.Api.Core/Features/Users/Bot/GetSlotsScores.cs:
--------------------------------------------------------------------------------
1 | using ClemBot.Api.Common;
2 | using ClemBot.Api.Data.Contexts;
3 | using Microsoft.EntityFrameworkCore;
4 |
5 | namespace ClemBot.Api.Core.Features.Users.Bot;
6 |
7 | public class GetSlotsScores
8 | {
9 | public record Query : IRequest>
10 | {
11 | public ulong UserId { get; init; }
12 |
13 | public ulong GuildId { get; init; }
14 |
15 | public int Limit { get; init; }
16 | }
17 |
18 | public record Model : IResponseModel
19 | {
20 | public IEnumerable