├── .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 Scores { get; init; } = null!; 21 | } 22 | 23 | public class QueryHandler : IRequestHandler> 24 | { 25 | private readonly ClemBotContext _context; 26 | 27 | public QueryHandler(ClemBotContext context) 28 | { 29 | _context = context; 30 | } 31 | 32 | public async Task> Handle(Query request, CancellationToken cancellationToken) 33 | { 34 | var scores = await _context.SlotScores 35 | .Where(x => x.UserId == request.UserId && x.GuildId == request.GuildId) 36 | .OrderByDescending(y => y.Score) 37 | .Take(request.Limit) 38 | .ToListAsync(); 39 | 40 | return QueryResult.Success(new Model 41 | { 42 | Scores = scores.Select(x => x.Score) 43 | }); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Core/Features/Users/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.Users.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 | 25 | public record Handler(ClemBotContext _context) : IRequestHandler>> 26 | { 27 | public async Task>> Handle(Query request, 28 | CancellationToken cancellationToken) 29 | { 30 | var users = await _context.Users.ToListAsync(); 31 | if (!users.Any()) 32 | { 33 | return QueryResult>.NotFound(); 34 | } 35 | 36 | return QueryResult>.Success( 37 | users.Select(x => new Model() 38 | { 39 | Id = x.Id, 40 | Name = x.Name 41 | })); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Core/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using System; 2 | global using System.Collections.Generic; 3 | global using System.Linq; 4 | global using System.Threading; 5 | global using System.Threading.Tasks; 6 | global using ClemBot.Api.Common.Enums; 7 | global using ClemBot.Api.Common.Security.Policies.GuildSandbox; 8 | global using ClemBot.Api.Common.Utilities; 9 | global using MediatR; 10 | global using Microsoft.Extensions.Logging; 11 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Core/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "profiles": { 4 | "ClemBot.Api.Core": { 5 | "commandName": "Project", 6 | "dotnetRunMessages": "true", 7 | "launchBrowser": true, 8 | "launchUrl": "swagger", 9 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 10 | "environmentVariables": { 11 | "ASPNETCORE_ENVIRONMENT": "Development" 12 | } 13 | }, 14 | "ClemBot.Api.Core (No Browser)": { 15 | "commandName": "Project", 16 | "dotnetRunMessages": "true", 17 | "launchBrowser": false, 18 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 19 | "environmentVariables": { 20 | "ASPNETCORE_ENVIRONMENT": "Development" 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Core/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Core/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "AllowedHosts": "*", 3 | "JwtTokenConfig": { 4 | "Issuer": "http://localhost:5000", 5 | "Audience": "http://localhost:5000" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Core/nuget.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Data/ClemBot.Api.Data.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0 5 | 9f2f92d0-9337-40e6-94dc-d7874e885acf 6 | True 7 | 12 8 | enable 9 | nullable 10 | 11 | 12 | 13 | 14 | runtime; build; native; contentfiles; analyzers; buildtransitive 15 | all 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Data/Contexts/ClemBotContextDesignFactory.cs: -------------------------------------------------------------------------------- 1 | using ClemBot.Api.Common; 2 | using Microsoft.EntityFrameworkCore; 3 | using Microsoft.EntityFrameworkCore.Design; 4 | using Microsoft.Extensions.Configuration; 5 | 6 | namespace ClemBot.Api.Data.Contexts; 7 | 8 | public class ClemBotContextDesignFactory : IDesignTimeDbContextFactory 9 | { 10 | public ClemBotContext CreateDbContext(string[] args) 11 | { 12 | var configuration = new ConfigurationBuilder() 13 | .AddUserSecrets() 14 | .Build(); 15 | 16 | var builder = new DbContextOptionsBuilder(); 17 | builder.UseNpgsql(configuration[ConfigurationKeys.DbConnectionString], 18 | o => o.UseNodaTime()); 19 | 20 | return new ClemBotContext(builder.Options); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Data/Exceptions/EntityStateException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ClemBot.Api.Data.Exceptions; 4 | 5 | public class EntityStateException : Exception 6 | { 7 | public T InvalidObject { get; set; } 8 | 9 | public EntityStateException(string message, T invalidEntity) 10 | :base(message) 11 | { 12 | InvalidObject = invalidEntity; 13 | } 14 | } -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Data/Migrations/20211218015002_AddOwnerIdToGuildEntity.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | #nullable disable 4 | 5 | namespace ClemBot.Api.Data.Migrations 6 | { 7 | public partial class AddOwnerIdToGuildEntity : Migration 8 | { 9 | protected override void Up(MigrationBuilder migrationBuilder) 10 | { 11 | migrationBuilder.AddColumn( 12 | name: "OwnerId", 13 | table: "Guilds", 14 | type: "numeric(20,0)", 15 | nullable: false, 16 | defaultValue: 0m); 17 | } 18 | 19 | protected override void Down(MigrationBuilder migrationBuilder) 20 | { 21 | migrationBuilder.DropColumn( 22 | name: "OwnerId", 23 | table: "Guilds"); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Data/Models/Channel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ClemBot.Api.Data.Models; 4 | 5 | public class Channel 6 | { 7 | public ulong Id { get; set; } 8 | 9 | public required string Name { get; set; } 10 | 11 | public ulong GuildId { get; set; } 12 | public Guild Guild { get; set; } = null!; 13 | 14 | public ulong? ParentId { get; set; } 15 | public Channel? Parent { get; set; } 16 | 17 | public bool IsThread { get; private set; } 18 | 19 | public List Messages { get; set; } = new(); 20 | 21 | public List DesignatedChannels { get; set; } = new(); 22 | 23 | public List EmoteBoards { get; set; } = new(); 24 | } 25 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Data/Models/ClaimsMapping.cs: -------------------------------------------------------------------------------- 1 | using ClemBot.Api.Common.Enums; 2 | 3 | namespace ClemBot.Api.Data.Models; 4 | 5 | public class ClaimsMapping 6 | { 7 | public int Id { get; set; } 8 | 9 | public required BotAuthClaims Claim { get; set; } 10 | 11 | public ulong RoleId { get; set; } 12 | public Role Role { get; set; } = null!; 13 | } 14 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Data/Models/CommandInvocation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations.Schema; 3 | using NodaTime; 4 | 5 | namespace ClemBot.Api.Data.Models; 6 | 7 | public class CommandInvocation 8 | { 9 | public int Id { get; set; } 10 | 11 | public string CommandName { get; set; } = null!; 12 | 13 | public LocalDateTime Time { get; set; } 14 | 15 | public ulong GuildId { get; set; } 16 | 17 | public ulong ChannelId { get; set; } 18 | 19 | public ulong UserId { get; set; } 20 | } 21 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Data/Models/CommandRestriction.cs: -------------------------------------------------------------------------------- 1 | using ClemBot.Api.Common.Enums; 2 | 3 | namespace ClemBot.Api.Data.Models; 4 | 5 | public class CommandRestriction 6 | { 7 | public int Id { get; set; } 8 | 9 | public required string CommandName { get; set; } 10 | 11 | public bool? SilentlyFail { get; set; } 12 | 13 | public CommandRestrictionType RestrictionType { get; set; } 14 | 15 | public ulong GuildId { get; set; } 16 | public Guild Guild { get; set; } = null!; 17 | 18 | public ulong? ChannelId { get; set; } 19 | public Channel? Channel { get; set; } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Data/Models/CustomPrefix.cs: -------------------------------------------------------------------------------- 1 | namespace ClemBot.Api.Data.Models; 2 | 3 | public class CustomPrefix 4 | { 5 | public int Id { get; set; } 6 | 7 | public required string Prefix { get; set; } 8 | 9 | public ulong GuildId { get; set; } 10 | public Guild Guild { get; set; } = null!; 11 | } 12 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Data/Models/CustomTagPrefix.cs: -------------------------------------------------------------------------------- 1 | namespace ClemBot.Api.Data.Models; 2 | 3 | public class CustomTagPrefix 4 | { 5 | public int Id { get; set; } 6 | 7 | public required string TagPrefix { get; set; } 8 | 9 | public ulong GuildId { get; set; } 10 | public Guild Guild { get; set; } = null!; 11 | } 12 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Data/Models/DesignatedChannelMapping.cs: -------------------------------------------------------------------------------- 1 | using ClemBot.Api.Common.Enums; 2 | 3 | namespace ClemBot.Api.Data.Models; 4 | 5 | public class DesignatedChannelMapping 6 | { 7 | public int Id { get; set; } 8 | 9 | public required DesignatedChannels Type { get; set; } 10 | 11 | public ulong ChannelId { get; set; } 12 | public Channel Channel { get; set; } = null!; 13 | } 14 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Data/Models/EmoteBoard.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ClemBot.Api.Data.Models; 4 | 5 | public class EmoteBoard 6 | { 7 | public int Id { get; set; } 8 | 9 | public ulong GuildId { get; set; } 10 | public Guild Guild { get; set; } = null!; 11 | 12 | public required string Name { get; set; } 13 | 14 | public required string Emote { get; set; } 15 | 16 | public uint ReactionThreshold { get; set; } = 4; 17 | 18 | public bool AllowBotPosts { get; set; } 19 | 20 | public List Channels { get; set; } = new(); 21 | } 22 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Data/Models/EmoteBoardMessage.cs: -------------------------------------------------------------------------------- 1 | namespace ClemBot.Api.Data.Models; 2 | 3 | /// 4 | /// Represents a message (typically an embed) sent by ClemBot to one EmoteBoard channel. 5 | /// 6 | public class EmoteBoardMessage 7 | { 8 | public int Id { get; set; } 9 | 10 | public ulong MessageId { get; set; } 11 | 12 | public ulong ChannelId { get; set; } 13 | public Channel Channel { get; set; } = null!; 14 | 15 | public int EmoteBoardPostId { get; set; } 16 | public EmoteBoardPost EmoteBoardPost { get; set; } = null!; 17 | } 18 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Data/Models/EmoteBoardPost.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ClemBot.Api.Data.Models; 4 | 5 | /// 6 | /// Represents a message (typically user-generated) that received enough reactions to be posted. 7 | /// 8 | public class EmoteBoardPost 9 | { 10 | public int Id { get; set; } 11 | 12 | public ulong UserId { get; set; } 13 | public User User { get; set; } = null!; 14 | 15 | public ulong MessageId { get; set; } 16 | 17 | public ulong ChannelId { get; set; } 18 | public Channel Channel { get; set; } = null!; 19 | 20 | public int EmoteBoardId { get; set; } 21 | public EmoteBoard EmoteBoard { get; set; } = null!; 22 | 23 | public List Reactions { get; set; } = new(); 24 | 25 | public List Messages { get; set; } = null!; 26 | } 27 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Data/Models/EmoteBoardPostReaction.cs: -------------------------------------------------------------------------------- 1 | namespace ClemBot.Api.Data.Models; 2 | 3 | public class EmoteBoardPostReaction 4 | { 5 | public int Id { get; set; } 6 | 7 | public int EmoteBoardPostId { get; set; } 8 | public EmoteBoardPost EmoteBoardPost { get; set; } = null!; 9 | 10 | public ulong UserId { get; set; } 11 | } 12 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Data/Models/Guild.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Reflection; 3 | 4 | namespace ClemBot.Api.Data.Models; 5 | 6 | public class Guild 7 | { 8 | public ulong Id { get; set; } 9 | 10 | public required string Name { get; set; } 11 | 12 | public string? WelcomeMessage { get; set; } 13 | 14 | public ulong OwnerId { get; set; } 15 | 16 | public List Users { get; set; } = new(); 17 | public List GuildUsers { get; set; } = new(); 18 | 19 | public List Channels { get; set; } = new(); 20 | 21 | public List Messages { get; set; } = new(); 22 | 23 | public List Tags { get; set; } = new(); 24 | 25 | public List Roles { get; set; } = new(); 26 | 27 | public List Infractions { get; set; } = new(); 28 | 29 | public List CustomPrefixes { get; set; } = new(); 30 | 31 | public List CustomTagPrefixes { get; set; } = new(); 32 | 33 | public List GuildSettings { get; set; } = new(); 34 | } 35 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Data/Models/GuildSetting.cs: -------------------------------------------------------------------------------- 1 | using ClemBot.Api.Common.Enums; 2 | 3 | namespace ClemBot.Api.Data.Models; 4 | 5 | public class GuildSetting 6 | { 7 | public int Id { get; set; } 8 | 9 | public ConfigSettings Setting { get; set; } 10 | 11 | public string Value { get; set; } = null!; 12 | 13 | public Guild Guild { get; set; } = null!; 14 | public ulong GuildId { get; set; } 15 | } 16 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Data/Models/GuildUser.cs: -------------------------------------------------------------------------------- 1 | namespace ClemBot.Api.Data.Models; 2 | 3 | public class GuildUser 4 | { 5 | public ulong GuildId { get; set; } 6 | public virtual Guild Guild { get; set; } = null!; 7 | 8 | public ulong UserId { get; set; } 9 | public User User { get; set; } = null!; 10 | } 11 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Data/Models/Infraction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ClemBot.Api.Common.Enums; 3 | using NodaTime; 4 | 5 | namespace ClemBot.Api.Data.Models; 6 | 7 | public class Infraction 8 | { 9 | public int Id { get; set; } 10 | 11 | public required InfractionType Type { get; set; } 12 | 13 | public string? Reason { get; set; } 14 | 15 | public bool? IsActive { get; set; } 16 | 17 | public LocalDateTime? Duration { get; set; } 18 | 19 | public required LocalDateTime Time { get; set; } 20 | 21 | public ulong GuildId { get; set; } 22 | public Guild Guild { get; set; } = null!; 23 | 24 | public ulong AuthorId { get; set; } 25 | public User Author { get; set; } = null!; 26 | 27 | public ulong? SubjectId { get; set; } 28 | public User? Subject { get; set; } 29 | } 30 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Data/Models/Message.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | 5 | namespace ClemBot.Api.Data.Models; 6 | 7 | public class Message 8 | { 9 | public ulong Id { get; set; } 10 | 11 | public List Contents { get; set; } = new(); 12 | 13 | public ulong GuildId { get; set; } 14 | public Guild Guild { get; set; } = null!; 15 | 16 | public ulong ChannelId { get; set; } 17 | public Channel Channel { get; set; } = null!; 18 | 19 | public ulong UserId { get; set; } 20 | public User User { get; set; } = null!; 21 | } 22 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Data/Models/MessageContent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NodaTime; 3 | 4 | namespace ClemBot.Api.Data.Models; 5 | 6 | public class MessageContent 7 | { 8 | public int Id { get; set; } 9 | 10 | public required string Content { get; set; } 11 | 12 | public LocalDateTime Time { get; set; } 13 | 14 | public ulong MessageId { get; set; } 15 | public Message Message { get; set; } = null!; 16 | } 17 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Data/Models/Reminder.cs: -------------------------------------------------------------------------------- 1 | using NodaTime; 2 | 3 | namespace ClemBot.Api.Data.Models; 4 | 5 | public class Reminder 6 | { 7 | public int Id { get; set; } 8 | 9 | public required string Link { get; set; } 10 | 11 | public string? Content { get; set; } 12 | 13 | public LocalDateTime Time { get; set; } 14 | 15 | public bool Dispatched { get; set; } = false; 16 | 17 | public ulong UserId { get; set; } 18 | public virtual User User { get; set; } = null!; 19 | } 20 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Data/Models/Role.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ClemBot.Api.Data.Models; 4 | 5 | public class Role 6 | { 7 | public ulong Id { get; set; } 8 | 9 | public required string Name { get; set; } 10 | 11 | public bool IsAssignable { get; set; } 12 | 13 | public bool IsAutoAssigned { get; set; } 14 | 15 | public bool Admin { get; set; } 16 | 17 | public ulong GuildId { get; set; } 18 | public Guild Guild { get; set; } = null!; 19 | 20 | public List Users { get; set; } = null!; 21 | public List RoleUsers { get; set; } = null!; 22 | 23 | public List Claims { get; set; } = null!; 24 | } 25 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Data/Models/RoleUser.cs: -------------------------------------------------------------------------------- 1 | namespace ClemBot.Api.Data.Models; 2 | 3 | public class RoleUser 4 | { 5 | public ulong RoleId { get; set; } 6 | public Role Role { get; set; } = null!; 7 | 8 | public ulong UserId { get; set; } 9 | public User User { get; set; } = null!; 10 | } 11 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Data/Models/SlotScore.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NodaTime; 3 | 4 | namespace ClemBot.Api.Data.Models; 5 | 6 | public class SlotScore 7 | { 8 | public int Id { get; set; } 9 | 10 | public ulong Score { get; set; } 11 | 12 | public LocalDateTime Time { get; set; } 13 | 14 | public Guild Guild { get; set; } = null!; 15 | public ulong GuildId { get; set; } 16 | 17 | public User User { get; set; } = null!; 18 | public ulong UserId { get; set; } 19 | } 20 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Data/Models/Tag.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using NodaTime; 4 | 5 | namespace ClemBot.Api.Data.Models; 6 | 7 | public class Tag 8 | { 9 | public int Id { get; set; } 10 | 11 | public required string Name { get; set; } 12 | 13 | public required string Content { get; set; } 14 | 15 | public LocalDateTime Time { get; set; } 16 | 17 | public ulong GuildId { get; set; } 18 | public Guild Guild { get; set; } = null!; 19 | 20 | public ulong UserId { get; set; } 21 | public User User { get; set; } = null!; 22 | 23 | public List TagUses { get; set; } = new(); 24 | } 25 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Data/Models/TagUse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ClemBot.Api.Data.Models; 4 | 5 | public class TagUse 6 | { 7 | public int Id { get; set; } 8 | 9 | public DateTime Time { get; set; } 10 | 11 | public ulong UserId { get; set; } 12 | public User User { get; set; } = null!; 13 | 14 | public int TagId { get; set; } 15 | public Tag Tag { get; set; } = null!; 16 | 17 | public ulong ChannelId { get; set; } 18 | public Channel Channel { get; set; } = null!; 19 | } 20 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Data/Models/User.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ClemBot.Api.Data.Models; 4 | 5 | public class User 6 | { 7 | public ulong Id { get; set; } 8 | 9 | public required string Name { get; set; } 10 | 11 | public List Guilds { get; set; } = new(); 12 | public List GuildUsers { get; set; } = new(); 13 | 14 | public List Roles { get; set; } = null!; 15 | public List RoleUsers { get; set; } = null!; 16 | 17 | public List Tags { get; set; } = new(); 18 | 19 | public List Messages { get; set; } = new(); 20 | 21 | public List EmoteBoardPosts { get; set; } = new(); 22 | } 23 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Services/Authorization/IGuildSandboxAuthorizeService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using ClemBot.Api.Common.Enums; 4 | using ClemBot.Api.Common.Security.Policies.GuildSandbox; 5 | 6 | namespace ClemBot.Api.Services.Authorization; 7 | 8 | public interface IGuildSandboxAuthorizeService 9 | { 10 | Task AuthorizeUser(IGuildSandboxModel model); 11 | } 12 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Services/Caching/Channels/ChannelCacheHandlers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using ClemBot.Api.Data.Contexts; 5 | using ClemBot.Api.Services.Caching.Channels.Models; 6 | using LazyCache; 7 | using MediatR; 8 | using Microsoft.EntityFrameworkCore; 9 | 10 | namespace ClemBot.Api.Services.Caching.Channels; 11 | 12 | public class ChannelCacheHandlers : 13 | IRequestHandler, 14 | IRequestHandler 15 | { 16 | private readonly IAppCache _cache; 17 | 18 | private readonly ClemBotContext _context; 19 | 20 | public ChannelCacheHandlers(IAppCache cache, ClemBotContext context) 21 | { 22 | _cache = cache; 23 | _context = context; 24 | } 25 | 26 | public async Task Handle(ChannelExistsRequest request, CancellationToken cancellationToken) 27 | => await _cache.GetOrAddAsync(GetCacheKey(request.Id), 28 | () => _context.Channels.AnyAsync(x => x.Id == request.Id), 29 | TimeSpan.FromHours(6)); 30 | 31 | public Task Handle(ClearChannelRequest request, CancellationToken cancellationToken) 32 | { 33 | _cache.Remove(GetCacheKey(request.Id)); 34 | return Task.CompletedTask; 35 | } 36 | 37 | private static string GetCacheKey(ulong id) 38 | => $"{nameof(ChannelExistsRequest)}:{id}"; 39 | 40 | } 41 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Services/Caching/Channels/Models/ChannelExistsRequest.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace ClemBot.Api.Services.Caching.Channels.Models; 4 | 5 | public class ChannelExistsRequest : ICacheRequest, IRequest 6 | { 7 | public ulong Id { get; init; } 8 | } 9 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Services/Caching/Channels/Models/ClearChannelRequest.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace ClemBot.Api.Services.Caching.Channels.Models; 4 | 5 | public class ClearChannelRequest : ICacheRequest, IRequest 6 | { 7 | public ulong Id { get; init; } 8 | } 9 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Services/Caching/Commands/Models/ClearCommandRestrictionRequest.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace ClemBot.Api.Services.Caching.Commands.Models; 4 | 5 | public class ClearCommandRestrictionRequest : ICacheRequest, IRequest 6 | { 7 | public ulong Id { get; init; } 8 | 9 | public string CommandName { get; set; } 10 | } 11 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Services/Caching/Commands/Models/GetCommandRestrictionRequest.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using ClemBot.Api.Data.Models; 3 | using MediatR; 4 | 5 | namespace ClemBot.Api.Services.Caching.Commands.Models; 6 | 7 | public class GetCommandRestrictionRequest : ICacheRequest, IRequest> 8 | { 9 | public ulong Id { get; init; } 10 | 11 | public string CommandName { get; set; } 12 | } 13 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Services/Caching/CustomPrefix/CustomPrefixHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using ClemBot.Api.Data.Contexts; 7 | using ClemBot.Api.Services.Caching.CustomPrefix.Models; 8 | using LazyCache; 9 | using MediatR; 10 | using Microsoft.EntityFrameworkCore; 11 | 12 | namespace ClemBot.Api.Services.Caching.CustomPrefix; 13 | 14 | public class CustomPrefixHandler : 15 | IRequestHandler, 16 | IRequestHandler> 17 | { 18 | private readonly IAppCache _cache; 19 | 20 | private readonly ClemBotContext _context; 21 | 22 | public CustomPrefixHandler(IAppCache cache, ClemBotContext context) 23 | { 24 | _cache = cache; 25 | _context = context; 26 | } 27 | 28 | public async Task> Handle(GetCustomPrefixRequest request, CancellationToken cancellationToken) 29 | => await _cache.GetOrAddAsync(GetCacheKey(request.Id), 30 | () => _context.CustomPrefixs 31 | .Where(x => x.Guild.Id == request.Id) 32 | .Select(y => y.Prefix) 33 | .ToListAsync(), 34 | TimeSpan.FromHours(12)); 35 | 36 | public Task Handle(ClearCustomPrefixRequest request, CancellationToken cancellationToken) 37 | { 38 | _cache.Remove(GetCacheKey(request.Id)); 39 | return Task.CompletedTask; 40 | } 41 | 42 | private static string GetCacheKey(ulong id) 43 | => $"{nameof(GetCustomPrefixRequest)}:{id}"; 44 | } 45 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Services/Caching/CustomPrefix/Models/ClearCustomPrefixRequest.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace ClemBot.Api.Services.Caching.CustomPrefix.Models; 4 | 5 | public class ClearCustomPrefixRequest : ICacheRequest, IRequest 6 | { 7 | public ulong Id { get; init; } 8 | } 9 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Services/Caching/CustomPrefix/Models/GetCustomPrefixRequest.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using MediatR; 3 | 4 | namespace ClemBot.Api.Services.Caching.CustomPrefix.Models; 5 | 6 | public class GetCustomPrefixRequest : ICacheRequest, IRequest> 7 | { 8 | public ulong Id { get; init; } 9 | } 10 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Services/Caching/CustomTagPrefix/Models/ClearCustomTagPrefixRequest.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace ClemBot.Api.Services.Caching.CustomTagPrefix.Models; 4 | 5 | public class ClearCustomTagPrefixRequest : ICacheRequest, IRequest 6 | { 7 | public ulong Id { get; init; } 8 | } 9 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Services/Caching/CustomTagPrefix/Models/GetCustomTagPrefixRequest.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using MediatR; 3 | 4 | namespace ClemBot.Api.Services.Caching.CustomTagPrefix.Models; 5 | 6 | public class GetCustomTagPrefixRequest : ICacheRequest, IRequest> 7 | { 8 | public ulong Id { get; init; } 9 | } 10 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Services/Caching/DesignatedChannels/Models/ClearDesignatedChannelDetailRequest.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace ClemBot.Api.Services.Caching.DesignatedChannels.Models; 4 | 5 | public class ClearDesignatedChannelDetailRequest : ICacheRequest, IRequest 6 | { 7 | public ulong Id { get; init; } 8 | 9 | public Common.Enums.DesignatedChannels Designation { get; init; } 10 | } 11 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Services/Caching/DesignatedChannels/Models/GetDesignatedChannelDetailRequest.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using ClemBot.Api.Data.Models; 3 | using MediatR; 4 | 5 | namespace ClemBot.Api.Services.Caching.DesignatedChannels.Models; 6 | 7 | public class GetDesignatedChannelDetailRequest : ICacheRequest, IRequest> 8 | { 9 | public ulong Id { get; init; } 10 | 11 | public Common.Enums.DesignatedChannels Designation { get; init; } 12 | } 13 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Services/Caching/EmoteBoardPosts/EmoteBoardPostCacheHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using ClemBot.Api.Data.Contexts; 5 | using ClemBot.Api.Services.Caching.EmoteBoardPosts.Models; 6 | using LazyCache; 7 | using MediatR; 8 | using Microsoft.EntityFrameworkCore; 9 | 10 | namespace ClemBot.Api.Services.Caching.EmoteBoardPosts; 11 | 12 | public class EmoteBoardPostCacheHandler : IRequestHandler, 13 | IRequestHandler 14 | { 15 | 16 | private readonly IAppCache _cache; 17 | private readonly ClemBotContext _context; 18 | 19 | public EmoteBoardPostCacheHandler(IAppCache cache, ClemBotContext context) 20 | { 21 | _cache = cache; 22 | _context = context; 23 | } 24 | 25 | public Task Handle(ClearEmoteBoardPostRequest request, CancellationToken cancellationToken) 26 | { 27 | _cache.Remove(GetCacheKey(request.BoardId, request.MessageId)); 28 | return Unit.Task; 29 | } 30 | 31 | public async Task Handle(EmoteBoardPostExistsRequest request, CancellationToken cancellationToken) => 32 | await _cache.GetOrAddAsync(GetCacheKey(request.BoardId, request.MessageId), () => _context.EmoteBoardPosts 33 | .AnyAsync(p => p.MessageId == request.MessageId && p.EmoteBoardId == request.BoardId), 34 | TimeSpan.FromHours(12)); 35 | 36 | private static string GetCacheKey(int boardId, ulong messageId) => 37 | $"{nameof(EmoteBoardPostCacheHandler)}:{boardId}:{messageId}"; 38 | } 39 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Services/Caching/EmoteBoardPosts/Models/ClearEmoteBoardPostRequest.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace ClemBot.Api.Services.Caching.EmoteBoardPosts.Models; 4 | 5 | public class ClearEmoteBoardPostRequest : IRequest 6 | { 7 | public int BoardId { get; init; } 8 | 9 | public ulong MessageId { get; init; } 10 | } 11 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Services/Caching/EmoteBoardPosts/Models/EmoteBoardPostExistsRequest.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace ClemBot.Api.Services.Caching.EmoteBoardPosts.Models; 4 | 5 | public class EmoteBoardPostExistsRequest : IRequest 6 | { 7 | public int BoardId { get; init; } 8 | 9 | public ulong MessageId { get; init; } 10 | } 11 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Services/Caching/EmoteBoards/GuildBoardsCacheHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using ClemBot.Api.Data.Contexts; 6 | using ClemBot.Api.Services.Caching.EmoteBoards.Models; 7 | using LazyCache; 8 | using MediatR; 9 | using Microsoft.EntityFrameworkCore; 10 | 11 | namespace ClemBot.Api.Services.Caching.EmoteBoards; 12 | 13 | public class GuildBoardsCacheHandler : IRequestHandler>, 14 | IRequestHandler 15 | { 16 | 17 | private readonly IAppCache _cache; 18 | private readonly ClemBotContext _context; 19 | 20 | public GuildBoardsCacheHandler(IAppCache cache, ClemBotContext context) 21 | { 22 | _cache = cache; 23 | _context = context; 24 | } 25 | 26 | public Task Handle(ClearGuildBoardsRequest request, CancellationToken cancellationToken) 27 | { 28 | _cache.Remove(GetCacheKey(request.GuildId)); 29 | return Unit.Task; 30 | } 31 | 32 | public async Task> Handle(GetGuildBoardsRequest request, CancellationToken cancellationToken) => 33 | await _context.EmoteBoards 34 | .Where(b => b.GuildId == request.GuildId) 35 | .ToDictionaryAsync(b => b.Name, b => b.Emote); 36 | 37 | private static string GetCacheKey(ulong guildId) => $"{nameof(GuildBoardsCacheHandler)}:{guildId}"; 38 | } 39 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Services/Caching/EmoteBoards/Models/ClearGuildBoardsRequest.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace ClemBot.Api.Services.Caching.EmoteBoards.Models; 4 | 5 | public class ClearGuildBoardsRequest : IRequest 6 | { 7 | public ulong GuildId { get; init; } 8 | } 9 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Services/Caching/EmoteBoards/Models/GetGuildBoardsRequest.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using MediatR; 3 | 4 | namespace ClemBot.Api.Services.Caching.EmoteBoards.Models; 5 | 6 | public class GetGuildBoardsRequest : IRequest> 7 | { 8 | public ulong GuildId { get; init; } 9 | } 10 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Services/Caching/GlobalStats/GetGlobalCommandStats.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using ClemBot.Api.Data.Contexts; 5 | using ClemBot.Api.Services.Caching.GlobalStats.Models; 6 | using LazyCache; 7 | using MediatR; 8 | using Microsoft.EntityFrameworkCore; 9 | 10 | namespace ClemBot.Api.Services.Caching.GlobalStats; 11 | 12 | public class GetGlobalCommandStats : IRequestHandler 13 | { 14 | private readonly IAppCache _cache; 15 | 16 | private readonly ClemBotContext _context; 17 | 18 | public GetGlobalCommandStats(IAppCache cache, ClemBotContext context) 19 | { 20 | _cache = cache; 21 | _context = context; 22 | } 23 | 24 | public async Task Handle(GlobalCommandStatsRequest request, CancellationToken cancellationToken) 25 | => await _cache.GetOrAddAsync(GetCacheKey(), 26 | () => _context.CommandInvocations.CountAsync(), 27 | DateTimeOffset.Now.AddMinutes(30)); 28 | 29 | private string GetCacheKey() 30 | => $"{nameof(GetGlobalCommandStats)}"; 31 | } 32 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Services/Caching/GlobalStats/GetGlobalGuildStats.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using ClemBot.Api.Data.Contexts; 5 | using ClemBot.Api.Services.Caching.GlobalStats.Models; 6 | using LazyCache; 7 | using MediatR; 8 | using Microsoft.EntityFrameworkCore; 9 | 10 | namespace ClemBot.Api.Services.Caching.GlobalStats; 11 | 12 | public class GetGlobalGuildStats : IRequestHandler 13 | { 14 | private readonly IAppCache _cache; 15 | 16 | private readonly ClemBotContext _context; 17 | 18 | public GetGlobalGuildStats(IAppCache cache, ClemBotContext context) 19 | { 20 | _cache = cache; 21 | _context = context; 22 | } 23 | 24 | public async Task Handle(GlobalGuildStatsRequest request, CancellationToken cancellationToken) 25 | => await _cache.GetOrAddAsync(GetCacheKey(), 26 | () => _context.Guilds.CountAsync(), 27 | DateTimeOffset.Now.AddHours(1)); 28 | 29 | private string GetCacheKey() 30 | => $"{nameof(GetGlobalGuildStats)}"; 31 | } 32 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Services/Caching/GlobalStats/GetGlobalUserStats.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using ClemBot.Api.Data.Contexts; 5 | using ClemBot.Api.Services.Caching.GlobalStats.Models; 6 | using LazyCache; 7 | using MediatR; 8 | using Microsoft.EntityFrameworkCore; 9 | 10 | namespace ClemBot.Api.Services.Caching.GlobalStats; 11 | 12 | public class GetGlobalUserStats : IRequestHandler 13 | { 14 | private readonly IAppCache _cache; 15 | 16 | private readonly ClemBotContext _context; 17 | 18 | public GetGlobalUserStats(IAppCache cache, ClemBotContext context) 19 | { 20 | _cache = cache; 21 | _context = context; 22 | } 23 | 24 | public async Task Handle(GlobalUserStatsRequest request, CancellationToken cancellationToken) 25 | => await _cache.GetOrAddAsync(GetCacheKey(), 26 | () => _context.GuildUser.CountAsync(), 27 | DateTimeOffset.Now.AddHours(3)); 28 | 29 | private string GetCacheKey() 30 | => $"{nameof(GetGlobalUserStats)}"; 31 | } 32 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Services/Caching/GlobalStats/Models/GlobalCommandStatsRequest.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace ClemBot.Api.Services.Caching.GlobalStats.Models; 4 | 5 | public class GlobalCommandStatsRequest : IRequest 6 | { 7 | } 8 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Services/Caching/GlobalStats/Models/GlobalGuildStatsRequest.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace ClemBot.Api.Services.Caching.GlobalStats.Models; 4 | 5 | public class GlobalGuildStatsRequest : IRequest 6 | { 7 | } 8 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Services/Caching/GlobalStats/Models/GlobalUserStatsRequest.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace ClemBot.Api.Services.Caching.GlobalStats.Models; 4 | 5 | public class GlobalUserStatsRequest : IRequest 6 | { 7 | } 8 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Services/Caching/Guilds/CheckGuildExistsHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using ClemBot.Api.Data.Contexts; 5 | using ClemBot.Api.Services.Caching.Guilds.Models; 6 | using LazyCache; 7 | using MediatR; 8 | using Microsoft.EntityFrameworkCore; 9 | 10 | namespace ClemBot.Api.Services.Caching.Guilds; 11 | 12 | public class CheckGuildExistsHandler : IRequestHandler 13 | { 14 | private readonly IAppCache _cache; 15 | 16 | private readonly ClemBotContext _context; 17 | 18 | public CheckGuildExistsHandler(IAppCache cache, ClemBotContext context) 19 | { 20 | _cache = cache; 21 | _context = context; 22 | } 23 | 24 | public async Task Handle(GuildExistsRequest request, CancellationToken cancellationToken) 25 | => await _cache.GetOrAddAsync(GetCacheKey(request.Id), 26 | () => _context.Guilds.AnyAsync(x => x.Id == request.Id), 27 | TimeSpan.FromHours(6)); 28 | 29 | private static string GetCacheKey(ulong id) 30 | => $"{nameof(GuildExistsRequest)}:{id}"; 31 | } 32 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Services/Caching/Guilds/Models/GuildExistsRequest.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace ClemBot.Api.Services.Caching.Guilds.Models; 4 | 5 | public class GuildExistsRequest : ICacheRequest, IRequest 6 | { 7 | public ulong Id { get; init; } 8 | } 9 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Services/Caching/ICacheRequest.cs: -------------------------------------------------------------------------------- 1 | namespace ClemBot.Api.Services.Caching; 2 | 3 | public interface ICacheRequest 4 | { 5 | ulong Id { get; init; } 6 | } 7 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Services/Caching/Messages/CheckMessageExistsHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using ClemBot.Api.Data.Contexts; 5 | using ClemBot.Api.Services.Caching.Messages.Models; 6 | using LazyCache; 7 | using MediatR; 8 | using Microsoft.EntityFrameworkCore; 9 | 10 | namespace ClemBot.Api.Services.Caching.Messages; 11 | 12 | public class CheckMessageExistsHandler : IRequestHandler 13 | { 14 | private readonly IAppCache _cache; 15 | 16 | private readonly ClemBotContext _context; 17 | 18 | public CheckMessageExistsHandler(IAppCache cache, ClemBotContext context) 19 | { 20 | _cache = cache; 21 | _context = context; 22 | } 23 | 24 | public async Task Handle(MessageExistsRequest request, CancellationToken cancellationToken) => 25 | await _cache.GetOrAddAsync(GetCacheKey(request.Id), 26 | () => _context.Messages.AnyAsync(x => x.Id == request.Id), 27 | TimeSpan.FromHours(3)); 28 | 29 | private static string GetCacheKey(ulong id) 30 | => $"{nameof(MessageExistsRequest)}:{id}"; 31 | } 32 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Services/Caching/Messages/Models/MessageExistsRequest.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace ClemBot.Api.Services.Caching.Messages.Models; 4 | 5 | public class MessageExistsRequest : ICacheRequest, IRequest 6 | { 7 | public ulong Id { get; init; } 8 | } 9 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Services/Caching/Users/CheckUserExists.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using ClemBot.Api.Data.Contexts; 5 | using ClemBot.Api.Services.Caching.Users.Models; 6 | using LazyCache; 7 | using MediatR; 8 | using Microsoft.EntityFrameworkCore; 9 | 10 | namespace ClemBot.Api.Services.Caching.Users; 11 | 12 | public class CheckUserExists : IRequestHandler 13 | { 14 | private readonly IAppCache _cache; 15 | 16 | private readonly ClemBotContext _context; 17 | 18 | public CheckUserExists(IAppCache cache, ClemBotContext context) 19 | { 20 | _cache = cache; 21 | _context = context; 22 | } 23 | 24 | public async Task Handle(UserExistsRequest request, CancellationToken cancellationToken) 25 | => await _cache.GetOrAddAsync(GetCacheKey(request.Id), 26 | () => _context.Users.AnyAsync(x => x.Id == request.Id), 27 | TimeSpan.FromHours(12)); 28 | 29 | private string GetCacheKey(ulong id) 30 | => $"{nameof(UserExistsRequest)}:{id}"; 31 | } 32 | -------------------------------------------------------------------------------- /ClemBot.Api/ClemBot.Api.Services/Caching/Users/Models/UserExistsRequest.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | 3 | namespace ClemBot.Api.Services.Caching.Users.Models; 4 | 5 | public class UserExistsRequest : ICacheRequest, IRequest 6 | { 7 | public ulong Id { get; init; } 8 | } 9 | -------------------------------------------------------------------------------- /ClemBot.Api/Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build-env 3 | WORKDIR /ClemBot.Api 4 | 5 | # Copy everything else and build 6 | COPY . ./ 7 | RUN dotnet publish . -c Release -o out 8 | 9 | # Build runtime image 10 | FROM mcr.microsoft.com/dotnet/aspnet:8.0 11 | WORKDIR /ClemBot.Api 12 | 13 | COPY --from=build-env /ClemBot.Api/out . 14 | ENTRYPOINT ["dotnet", "ClemBot.Api.Core.dll"] 15 | -------------------------------------------------------------------------------- /ClemBot.Bot/.dockerignore: -------------------------------------------------------------------------------- 1 | **/__pycache__ 2 | **/.classpath 3 | **/.dockerignore 4 | **/.env 5 | **/.git 6 | **/.gitignore 7 | **/.project 8 | **/.settings 9 | **/.toolstarget 10 | **/.vs 11 | **/.vscode 12 | **/*.*proj.user 13 | **/*.dbmdl 14 | **/*.jfm 15 | **/azds.yaml 16 | **/bin 17 | **/charts 18 | **/docker-compose* 19 | **/Dockerfile* 20 | **/node_modules 21 | **/npm-debug.log 22 | **/obj 23 | **/secrets.dev.yaml 24 | **/values.dev.yaml 25 | **/.venv 26 | README.md 27 | -------------------------------------------------------------------------------- /ClemBot.Bot/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "ClemBot", 9 | "type": "python", 10 | "request": "launch", 11 | "module": "bot", 12 | "console": "integratedTerminal" 13 | }, 14 | { 15 | "name": "Docker: Python - General", 16 | "type": "docker", 17 | "request": "launch", 18 | "preLaunchTask": "docker-run: debug", 19 | "python": { 20 | "pathMappings": [ 21 | { 22 | "localRoot": "${workspaceFolder}", 23 | "remoteRoot": "/app" 24 | } 25 | ], 26 | "projectType": "general" 27 | } 28 | }, 29 | { 30 | "name": "Python: Current File", 31 | "type": "python", 32 | "request": "launch", 33 | "program": "${file}", 34 | "console": "integratedTerminal" 35 | } 36 | ] 37 | } -------------------------------------------------------------------------------- /ClemBot.Bot/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "python.pythonPath": ".venv/bin/python" 3 | } 4 | -------------------------------------------------------------------------------- /ClemBot.Bot/.vscode/settings.json.default: -------------------------------------------------------------------------------- 1 | { 2 | "python.linting.pylintEnabled": false, 3 | "python.linting.enabled": true, 4 | "python.pythonPath": "venv/bin/python", 5 | "python.linting.banditEnabled": false, 6 | "python.linting.flake8Enabled": true, 7 | "python.linting.mypyEnabled": false, 8 | "python.linting.flake8Args": [ 9 | "--max-line-length=130", 10 | "--ignore=E402,F841,F401,E302,E251,E305,W291,W292,W293,W391,E265,E128", 11 | ], 12 | } -------------------------------------------------------------------------------- /ClemBot.Bot/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "docker-build", 6 | "label": "docker-build", 7 | "platform": "python", 8 | "dockerBuild": { 9 | "tag": "clembot:latest", 10 | "dockerfile": "${workspaceFolder}/Dockerfile", 11 | "context": "${workspaceFolder}", 12 | "pull": true 13 | } 14 | }, 15 | { 16 | "type": "docker-run", 17 | "label": "docker-run: debug", 18 | "dependsOn": [ 19 | "docker-build" 20 | ], 21 | "python": { 22 | "file": "src/__main__.py" 23 | } 24 | } 25 | ] 26 | } -------------------------------------------------------------------------------- /ClemBot.Bot/BotSecrets.json.template: -------------------------------------------------------------------------------- 1 | { 2 | "ClientToken": "", 3 | "ClientSecret": "", 4 | "BotToken": "", 5 | "ApiUrl": "", 6 | "ApiKey": "", 7 | "SiteUrl": "", 8 | "DocsUrl": "", 9 | "StartupLogChannelIds": [123456789], 10 | "ErrorLogChannelIds": [123456789], 11 | "BotOnly": false, 12 | "BotPrefix": "", 13 | "ReplUrl": "", 14 | "GithubSourceUrl": "", 15 | "AllowBotInputIds": [] 16 | } 17 | -------------------------------------------------------------------------------- /ClemBot.Bot/ClemBot.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "." 5 | } 6 | ], 7 | "settings": {} 8 | } -------------------------------------------------------------------------------- /ClemBot.Bot/Dockerfile: -------------------------------------------------------------------------------- 1 | # For more information, please refer to https://aka.ms/vscode-docker-python 2 | FROM python:3.10-slim-buster 3 | 4 | # Keeps Python from generating .pyc files in the container 5 | ENV PYTHONDONTWRITEBYTECODE 1 6 | 7 | # Turns off buffering for easier container logging 8 | ENV PYTHONUNBUFFERED 1 9 | 10 | RUN apt-get update && apt-get install -y git 11 | 12 | # Install poetry 13 | RUN python -m pip install poetry 14 | 15 | # Install dependencies with Poetry 16 | WORKDIR /ClemBot.Bot 17 | ADD pyproject.toml . 18 | ADD poetry.lock . 19 | 20 | RUN poetry config virtualenvs.in-project true 21 | RUN poetry install --no-dev --no-interaction 22 | 23 | ADD . /ClemBot.Bot 24 | 25 | # During debugging, this entry point will be overridden. For more information, please refer to https://aka.ms/vscode-docker-python-debug 26 | CMD ["poetry", "run", "python3", "-m", "bot"] 27 | -------------------------------------------------------------------------------- /ClemBot.Bot/bot/__init__.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | 4 | import seqlog 5 | from seqlog import ConsoleStructuredLogHandler, StructuredLogger, StructuredRootLogger 6 | 7 | if bool(os.environ.get("PROD")): 8 | 9 | # Production logging setup 10 | url = os.environ.get("SEQ_URL") 11 | key = os.environ.get("SEQ_BOT_KEY") 12 | 13 | if not key: 14 | raise Exception("SEQ_BOT_KEY not found but SEQ_URL was specified") 15 | 16 | seqlog.log_to_seq( 17 | # Initialize the seq logging url before the secrets are loaded 18 | # this is ok because seq logging only happens in prod 19 | server_url=url, 20 | api_key=key, 21 | level=logging.INFO, 22 | batch_size=5, 23 | auto_flush_timeout=10, # seconds 24 | override_root_logger=False, 25 | ) 26 | else: 27 | # Development logging setup 28 | logging.setLoggerClass(StructuredLogger) 29 | 30 | logging.root = StructuredRootLogger(logging.WARNING) 31 | logging.Logger.root = logging.root 32 | logging.Logger.manager = logging.Manager(logging.Logger.root) 33 | 34 | logging.basicConfig( 35 | format="%(asctime)s %(levelname)s %(module)s %(message)s", 36 | handlers=[ConsoleStructuredLogHandler()], 37 | level=logging.INFO, 38 | ) 39 | -------------------------------------------------------------------------------- /ClemBot.Bot/bot/api/__init__.py: -------------------------------------------------------------------------------- 1 | __all__: list[str] = [ 2 | "guild_route", 3 | "user_route", 4 | "base_route", 5 | "channel_route", 6 | "role_route", 7 | "message_route", 8 | "tag_route", 9 | "designated_channel_route", 10 | "welcome_message_route", 11 | "custom_prefix_route", 12 | "custom_tag_prefix_route", 13 | "moderation_route", 14 | "claim_route", 15 | "commands_route", 16 | "thread_route", 17 | "slots_score_route", 18 | "health_check_route", 19 | ] 20 | -------------------------------------------------------------------------------- /ClemBot.Bot/bot/api/base_route.py: -------------------------------------------------------------------------------- 1 | import abc 2 | 3 | from bot.api.api_client import ApiClient 4 | 5 | 6 | class BaseRoute(abc.ABC): 7 | def __init__(self, client: ApiClient): 8 | self._client: ApiClient = client 9 | -------------------------------------------------------------------------------- /ClemBot.Bot/bot/api/channel_route.py: -------------------------------------------------------------------------------- 1 | import typing as t 2 | 3 | from bot.api.api_client import ApiClient 4 | from bot.api.base_route import BaseRoute 5 | from bot.models.channel_models import Channel 6 | 7 | 8 | class ChannelRoute(BaseRoute): 9 | def __init__(self, api_client: ApiClient): 10 | super().__init__(api_client) 11 | 12 | async def create_channel( 13 | self, channel_id: int, name: str, guild_id: int, **kwargs: t.Any 14 | ) -> None: 15 | json = {"Id": channel_id, "Name": name, "GuildId": guild_id} 16 | await self._client.post("bot/channels", data=json, **kwargs) 17 | 18 | async def get_channel(self, channel_id: int) -> Channel: 19 | return Channel(**await self._client.get(f"bot/channels/{channel_id}")) 20 | 21 | async def edit_channel(self, channel_id: int, name: str, **kwargs: t.Any) -> None: 22 | json = { 23 | "Id": channel_id, 24 | "Name": name, 25 | } 26 | 27 | await self._client.patch("bot/channels", data=json, **kwargs) 28 | 29 | async def remove_channel(self, channel_id: int, **kwargs: t.Any) -> None: 30 | await self._client.delete(f"bot/channels/{channel_id}", **kwargs) 31 | 32 | async def get_guilds_channels(self, guild_id: int) -> list[int] | None: 33 | return t.cast(list[int] | None, await self._client.get(f"bot/guilds/{guild_id}/channels")) 34 | -------------------------------------------------------------------------------- /ClemBot.Bot/bot/api/claim_route.py: -------------------------------------------------------------------------------- 1 | import typing as t 2 | 3 | import discord 4 | 5 | from bot.api.api_client import ApiClient 6 | from bot.api.base_route import BaseRoute 7 | from bot.consts import Claims 8 | 9 | 10 | class ClaimRoute(BaseRoute): 11 | def __init__(self, api_client: ApiClient): 12 | super().__init__(api_client) 13 | 14 | async def add_claim_mapping(self, claim: Claims, role_id: int, **kwargs: t.Any) -> None: 15 | json = {"RoleId": role_id, "Claim": claim.name} 16 | 17 | await self._client.post("bot/claimmappings", data=json, **kwargs) 18 | 19 | async def remove_claim_mapping(self, claim: Claims, role_id: int, **kwargs: t.Any) -> None: 20 | json = {"RoleId": role_id, "Claim": claim.name} 21 | 22 | await self._client.delete("bot/claimmappings", data=json, **kwargs) 23 | 24 | async def get_claims_role(self, role_id: int) -> list[Claims]: 25 | return [Claims[c] for c in await self._client.get(f"bot/roles/{role_id}/claimmappings")] 26 | 27 | async def get_claims_user(self, user: discord.Member) -> list[Claims]: 28 | return [ 29 | Claims[c] for c in await self._client.get(f"bot/users/{user.id}/{user.guild.id}/claims") 30 | ] 31 | 32 | async def check_claim_role(self, claim: Claims, role: discord.Role) -> bool: 33 | return claim in await self.get_claims_role(role.id) 34 | 35 | async def check_claim_user(self, claim: Claims, user: discord.Member) -> bool: 36 | return claim in await self.get_claims_user(user) 37 | -------------------------------------------------------------------------------- /ClemBot.Bot/bot/api/custom_prefix_route.py: -------------------------------------------------------------------------------- 1 | import typing as t 2 | 3 | from bot.api.api_client import ApiClient 4 | from bot.api.base_route import BaseRoute 5 | 6 | 7 | class CustomPrefixRoute(BaseRoute): 8 | def __init__(self, api_client: ApiClient): 9 | super().__init__(api_client) 10 | 11 | async def set_custom_prefix(self, guild_id: int, prefix: str) -> None: 12 | json = {"GuildId": guild_id, "Prefix": prefix} 13 | await self._client.post("customprefixes/add", data=json) 14 | 15 | async def remove_custom_prefix(self, guild_id: int, prefix: str) -> None: 16 | json = {"GuildId": guild_id, "Prefix": prefix} 17 | await self._client.delete("bot/customprefixes/remove", data=json) 18 | 19 | async def get_custom_prefixes(self, guild_id: int, **kwargs: t.Any) -> list[str]: 20 | resp = await self._client.get(f"guilds/{guild_id}/customprefixes", **kwargs) 21 | return t.cast(list[str], resp["prefixes"]) 22 | -------------------------------------------------------------------------------- /ClemBot.Bot/bot/api/custom_tag_prefix_route.py: -------------------------------------------------------------------------------- 1 | import typing as t 2 | 3 | from bot.api.api_client import ApiClient 4 | from bot.api.base_route import BaseRoute 5 | 6 | 7 | class CustomTagPrefixRoute(BaseRoute): 8 | def __init__(self, api_client: ApiClient): 9 | super().__init__(api_client) 10 | 11 | async def set_custom_tag_prefix(self, guild_id: int, tagprefix: str) -> None: 12 | json = {"GuildId": guild_id, "tagPrefix": tagprefix} 13 | await self._client.post("tags/addcustomtagprefix", data=json) 14 | 15 | async def remove_custom_tag_prefix(self, guild_id: int, tagprefix: str) -> None: 16 | json = {"GuildId": guild_id, "tagPrefix": tagprefix} 17 | await self._client.delete("bot/tags/deletecustomtagprefix", data=json) 18 | 19 | async def get_custom_tag_prefixes(self, guild_id: int, **kwargs: t.Any) -> list[str]: 20 | resp = await self._client.get(f"guilds/{guild_id}/customtagprefixes", **kwargs) 21 | return t.cast(list[str], resp["tagPrefixes"]) 22 | -------------------------------------------------------------------------------- /ClemBot.Bot/bot/api/health_check_route.py: -------------------------------------------------------------------------------- 1 | from bot.api.api_client import ApiClient 2 | from bot.api.base_route import BaseRoute 3 | 4 | 5 | class HealthCheckRoute(BaseRoute): 6 | def __init__(self, api_client: ApiClient): 7 | super().__init__(api_client) 8 | 9 | async def ping(self) -> None: 10 | await self._client.get("HealthCheck/ping") 11 | -------------------------------------------------------------------------------- /ClemBot.Bot/bot/api/slots_score_route.py: -------------------------------------------------------------------------------- 1 | import typing as t 2 | 3 | from bot.api.api_client import ApiClient 4 | from bot.api.base_route import BaseRoute 5 | 6 | 7 | class SlotsScoreRoute(BaseRoute): 8 | def __init__(self, api_client: ApiClient): 9 | super().__init__(api_client) 10 | 11 | async def add_slot_score( 12 | self, score: int, guild_id: int, user_id: int, **kwargs: t.Any 13 | ) -> None: 14 | json = {"Score": score, "GuildId": guild_id, "UserId": user_id} 15 | 16 | await self._client.post("bot/slotscores", data=json, **kwargs) 17 | -------------------------------------------------------------------------------- /ClemBot.Bot/bot/api/thread_route.py: -------------------------------------------------------------------------------- 1 | import typing as t 2 | 3 | from bot.api.api_client import ApiClient 4 | from bot.api.base_route import BaseRoute 5 | from bot.models.thread_models import Thread 6 | 7 | 8 | class ThreadRoute(BaseRoute): 9 | def __init__(self, api_client: ApiClient): 10 | super().__init__(api_client) 11 | 12 | async def create_thread( 13 | self, thread_id: int, name: str, guild_id: int, parent_id: int, **kwargs: t.Any 14 | ) -> None: 15 | json = {"Id": thread_id, "Name": name, "GuildId": guild_id, "ParentId": parent_id} 16 | await self._client.post("bot/threads", data=json, **kwargs) 17 | 18 | async def get_thread(self, thread_id: int) -> Thread | None: 19 | resp = await self._client.get(f"bot/threads/{thread_id}") 20 | 21 | if not resp: 22 | return None 23 | 24 | return Thread(**resp) 25 | 26 | async def edit_thread(self, thread_id: int, name: str, **kwargs: t.Any) -> None: 27 | json = { 28 | "Id": thread_id, 29 | "Name": name, 30 | } 31 | 32 | await self._client.patch("bot/threads", data=json, **kwargs) 33 | 34 | async def remove_thread(self, thread_id: int, **kwargs: t.Any) -> None: 35 | await self._client.delete(f"bot/threads/{thread_id}", **kwargs) 36 | 37 | async def get_guilds_threads(self, guild_id: int) -> list[int] | None: 38 | return t.cast(list[int] | None, await self._client.get(f"bot/guilds/{guild_id}/threads")) 39 | -------------------------------------------------------------------------------- /ClemBot.Bot/bot/api/welcome_message_route.py: -------------------------------------------------------------------------------- 1 | import typing as t 2 | 3 | from bot.api.api_client import ApiClient 4 | from bot.api.base_route import BaseRoute 5 | 6 | 7 | class WelcomeMessageRoute(BaseRoute): 8 | def __init__(self, api_client: ApiClient): 9 | super().__init__(api_client) 10 | 11 | async def set_welcome_message( 12 | self, guild_id: int, message: str | None, **kwargs: t.Any 13 | ) -> None: 14 | json = {"Message": message} 15 | await self._client.post(f"guilds/{guild_id}/SetWelcomeMessage", data=json, **kwargs) 16 | 17 | async def get_welcome_message(self, guild_id: int) -> str: 18 | resp = await self._client.get(f"guilds/{guild_id}/GetWelcomeMessage") 19 | return t.cast(str, resp["message"]) 20 | 21 | async def delete_welcome_message(self, guild_id: int, **kwargs: t.Any) -> None: 22 | await self._client.delete(f"bot/guilds/{guild_id}/GetWelcomeMessage", **kwargs) 23 | -------------------------------------------------------------------------------- /ClemBot.Bot/bot/cogs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClemBotProject/ClemBot/5b169b1d45d581660fff4438aab920fb4101f64b/ClemBot.Bot/bot/cogs/__init__.py -------------------------------------------------------------------------------- /ClemBot.Bot/bot/cogs/grades_cog/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClemBotProject/ClemBot/5b169b1d45d581660fff4438aab920fb4101f64b/ClemBot.Bot/bot/cogs/grades_cog/__init__.py -------------------------------------------------------------------------------- /ClemBot.Bot/bot/cogs/moderation_cog/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClemBotProject/ClemBot/5b169b1d45d581660fff4438aab920fb4101f64b/ClemBot.Bot/bot/cogs/moderation_cog/__init__.py -------------------------------------------------------------------------------- /ClemBot.Bot/bot/cogs/random_cog/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClemBotProject/ClemBot/5b169b1d45d581660fff4438aab920fb4101f64b/ClemBot.Bot/bot/cogs/random_cog/__init__.py -------------------------------------------------------------------------------- /ClemBot.Bot/bot/cogs/random_cog/assets/phrases.txt: -------------------------------------------------------------------------------- 1 | Will you get lucky? 2 | You're feeling lucky I see 3 | You're on a roll 4 | Keep going hotshot 5 | Good fortune in your future 6 | Dang you're good 7 | Ohh come on, one more try! 8 | Absolutely Stupendous! 9 | This is the start of something great 10 | Right on! 11 | High score incoming! 12 | You got this! 13 | I am feeling great about this one! 14 | You know you want to try again 15 | Fortune favors the bold 16 | Riches await you 17 | Go buy a lottery ticket after this one! 18 | Truly incredible 19 | You rock! 20 | Here it comes! 21 | Don't stop here 22 | -------------------------------------------------------------------------------- /ClemBot.Bot/bot/custom_prefix.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | 4 | import bot.bot_secrets as bot_secrets 5 | from bot.clem_bot import ClemBot 6 | from bot.errors import PrefixRequestError 7 | from bot.utils.logging_utils import get_logger 8 | 9 | log = get_logger(__name__) 10 | 11 | 12 | class CustomPrefix: 13 | def __init__(self, *, default: str): 14 | log.info(f'Setting default prefix too: "{default}""') 15 | self.default = default 16 | 17 | async def get_prefix(self, bot: ClemBot, message: discord.Message) -> list[str]: 18 | 19 | prefixes = [] 20 | 21 | # Check if bot is in BotOnly mode, if it is we cant get custom prefixes 22 | # so we have to fall back to self.default 23 | if not bot_secrets.secrets.bot_only: 24 | # noinspection PyBroadException 25 | try: 26 | # Try to grab the prefixes from the db, raise an error on failure 27 | # and bailout, we cant respond to anything at the moment 28 | assert message.guild 29 | prefixes = await bot.custom_prefix_route.get_custom_prefixes( 30 | message.guild.id, raise_on_error=True 31 | ) 32 | except Exception as e: 33 | log.error("Custom prefix request failed with error: {error}", error=e) 34 | raise PrefixRequestError("Requesting custom prefix from the api failed") 35 | 36 | if len(prefixes) == 0: 37 | prefixes = [self.default] 38 | 39 | return commands.when_mentioned(bot, message) + prefixes 40 | -------------------------------------------------------------------------------- /ClemBot.Bot/bot/messaging/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClemBotProject/ClemBot/5b169b1d45d581660fff4438aab920fb4101f64b/ClemBot.Bot/bot/messaging/__init__.py -------------------------------------------------------------------------------- /ClemBot.Bot/bot/models/channel_models.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | from bot.models.clem_bot_model import ClemBotModel 4 | 5 | 6 | class Channel(ClemBotModel): 7 | id: int 8 | name: Optional[str] 9 | guild_id: int 10 | -------------------------------------------------------------------------------- /ClemBot.Bot/bot/models/claim_models.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClemBotProject/ClemBot/5b169b1d45d581660fff4438aab920fb4101f64b/ClemBot.Bot/bot/models/claim_models.py -------------------------------------------------------------------------------- /ClemBot.Bot/bot/models/clem_bot_model.py: -------------------------------------------------------------------------------- 1 | from humps import camel 2 | from pydantic import BaseModel 3 | 4 | 5 | class ClemBotModel(BaseModel): 6 | """Base model which adds support for camelCase""" 7 | 8 | class Config: 9 | alias_generator = camel.case 10 | allow_population_by_field_name = True 11 | use_enum_values = True 12 | -------------------------------------------------------------------------------- /ClemBot.Bot/bot/models/command_models.py: -------------------------------------------------------------------------------- 1 | from bot.models.clem_bot_model import ClemBotModel 2 | 3 | 4 | class BlackListCommandModel(ClemBotModel): 5 | channel_id: int 6 | silently_fail: bool 7 | 8 | 9 | class CommandModel(ClemBotModel): 10 | command_name: str 11 | guild_disabled: bool 12 | guild_id: int 13 | white_listed_channel_ids: list[int] 14 | black_listed_channel_ids: list[BlackListCommandModel] 15 | 16 | 17 | class CommandStatusModel(ClemBotModel): 18 | 19 | disabled: bool 20 | silently_fail: bool | None 21 | -------------------------------------------------------------------------------- /ClemBot.Bot/bot/models/guild_models.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | from bot.models.clem_bot_model import ClemBotModel 4 | 5 | 6 | class Guild(ClemBotModel): 7 | id: int 8 | name: Optional[str] 9 | welcome_message: Optional[str] 10 | 11 | 12 | class SlotScore(ClemBotModel): 13 | high_score: int 14 | user_id: int 15 | -------------------------------------------------------------------------------- /ClemBot.Bot/bot/models/message_models.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | 3 | from bot.models.clem_bot_model import ClemBotModel 4 | 5 | 6 | class SingleBatchMessage(ClemBotModel): 7 | id: int 8 | content: str 9 | guild: int 10 | author: int 11 | channel: int 12 | time: datetime 13 | 14 | 15 | class SingleBatchMessageEdit(ClemBotModel): 16 | id: int 17 | content: str 18 | time: datetime 19 | 20 | 21 | class Message(ClemBotModel): 22 | id: int 23 | content: str 24 | guild_id: int 25 | channel_id: int 26 | user_id: int 27 | -------------------------------------------------------------------------------- /ClemBot.Bot/bot/models/moderation_models.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | 3 | from bot.models.clem_bot_model import ClemBotModel 4 | 5 | 6 | class Infraction(ClemBotModel): 7 | id: int 8 | guild_id: int 9 | author_id: int 10 | subject_id: int 11 | type: str 12 | reason: str | None 13 | duration: datetime | None 14 | time: datetime 15 | active: int | None 16 | -------------------------------------------------------------------------------- /ClemBot.Bot/bot/models/reminder_models.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | 3 | from bot.models.clem_bot_model import ClemBotModel 4 | 5 | 6 | class Reminder(ClemBotModel): 7 | id: int 8 | link: str 9 | content: str | None 10 | time: datetime 11 | dispatched: bool 12 | user_id: int 13 | 14 | 15 | class ReminderReload(ClemBotModel): 16 | id: int 17 | time: datetime 18 | -------------------------------------------------------------------------------- /ClemBot.Bot/bot/models/role_models.py: -------------------------------------------------------------------------------- 1 | from bot.models.clem_bot_model import ClemBotModel 2 | 3 | 4 | class Role(ClemBotModel): 5 | id: int 6 | name: str 7 | is_assignable: bool 8 | 9 | 10 | class RoleFull(Role): 11 | id: int 12 | name: str 13 | guild_id: int 14 | admin: bool 15 | is_assignable: bool 16 | -------------------------------------------------------------------------------- /ClemBot.Bot/bot/models/tag_models.py: -------------------------------------------------------------------------------- 1 | from bot.models.clem_bot_model import ClemBotModel 2 | 3 | 4 | class Tag(ClemBotModel): 5 | name: str 6 | content: str 7 | creation_date: str 8 | guild_id: int 9 | user_id: int 10 | use_count: int = 0 11 | 12 | 13 | class TagDelete(ClemBotModel): 14 | id: int 15 | name: str | None 16 | content: str | None 17 | 18 | 19 | class TagInvoke(ClemBotModel): 20 | guildId: int 21 | name: str | None 22 | -------------------------------------------------------------------------------- /ClemBot.Bot/bot/models/thread_models.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | from bot.models.clem_bot_model import ClemBotModel 4 | 5 | 6 | class Thread(ClemBotModel): 7 | id: int 8 | name: Optional[str] 9 | guild_id: int 10 | -------------------------------------------------------------------------------- /ClemBot.Bot/bot/models/user_models.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | from bot.models.clem_bot_model import ClemBotModel 4 | 5 | 6 | class User(ClemBotModel): 7 | id: int 8 | name: Optional[str] 9 | guilds: list[int] 10 | 11 | 12 | class UserSlotScores(ClemBotModel): 13 | scores: list[int] 14 | -------------------------------------------------------------------------------- /ClemBot.Bot/bot/services/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClemBotProject/ClemBot/5b169b1d45d581660fff4438aab920fb4101f64b/ClemBot.Bot/bot/services/__init__.py -------------------------------------------------------------------------------- /ClemBot.Bot/bot/services/fuzzy_matching_service.py: -------------------------------------------------------------------------------- 1 | from bot.clem_bot import ClemBot 2 | from bot.services.base_service import BaseService 3 | from bot.utils.trigrams import BankSearchEntry, find_best_match, make_search_bank 4 | 5 | 6 | class FuzzyMatchingService(BaseService): 7 | def __init__(self, bot: ClemBot): 8 | super().__init__(bot) 9 | 10 | self.bot = bot 11 | 12 | self._cmd_name_bank = make_search_bank([cmd.qualified_name for cmd in bot.walk_commands()]) 13 | 14 | async def load_service(self) -> None: 15 | pass 16 | 17 | def fuzzy_find_command(self, cmd_name: str) -> BankSearchEntry: 18 | return find_best_match(self._cmd_name_bank, cmd_name) 19 | -------------------------------------------------------------------------------- /ClemBot.Bot/bot/services/welcome_message_service.py: -------------------------------------------------------------------------------- 1 | import discord 2 | 3 | from bot.clem_bot import ClemBot 4 | from bot.messaging.events import Events 5 | from bot.services.base_service import BaseService 6 | from bot.utils.logging_utils import get_logger 7 | 8 | log = get_logger(__name__) 9 | 10 | 11 | class WelcomeMessageService(BaseService): 12 | def __init__(self, *, bot: ClemBot): 13 | super().__init__(bot) 14 | 15 | @BaseService.listener(Events.on_user_join_initialized) 16 | async def user_joined(self, user: discord.Member) -> None: 17 | message = await self.bot.welcome_message_route.get_welcome_message(user.guild.id) 18 | 19 | if message and not user.bot: 20 | await user.send(message) 21 | 22 | async def load_service(self) -> None: 23 | pass 24 | -------------------------------------------------------------------------------- /ClemBot.Bot/bot/utils/log_serializers.py: -------------------------------------------------------------------------------- 1 | import typing as t 2 | 3 | import discord 4 | 5 | 6 | def log_guild(guild: discord.Guild) -> dict[str, t.Any]: 7 | return {"id": guild.id, "name": guild.name} 8 | 9 | 10 | def log_user(member: (discord.Member | discord.User | discord.ClientUser)) -> dict[str, t.Any]: 11 | return ( 12 | {"id": member.id, "name": member.name, "guild": log_guild(member.guild)} 13 | if isinstance(member, discord.Member) 14 | else { 15 | "id": member.id, 16 | "name": member.name, 17 | } 18 | ) 19 | 20 | 21 | def log_message(message: discord.Message) -> dict[str, t.Any]: 22 | return {"author": log_user(message.author), "content": message.content} 23 | 24 | 25 | def log_channel(channel: t.Any) -> dict[str, t.Any]: 26 | id = getattr(channel, "id", None) 27 | name = getattr(channel, "name", str(channel)) 28 | guild = getattr(channel, "guild", None) 29 | 30 | return {"id": id, "name": name, "guild": log_guild(guild) if guild else None} 31 | 32 | 33 | def log_role(role: discord.Role) -> dict[str, t.Any]: 34 | return {"id": role.id, "name": role.name, "guild": log_guild(role.guild)} 35 | -------------------------------------------------------------------------------- /ClemBot.Bot/bot/utils/logging_utils.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import typing as t 3 | 4 | import seqlog 5 | 6 | 7 | def get_logger(name: str) -> seqlog.StructuredLogger: 8 | """Provides a way to get seqlog StructuredLogger instance without mypy being angry""" 9 | 10 | return t.cast(seqlog.StructuredLogger, logging.getLogger(name)) 11 | -------------------------------------------------------------------------------- /ClemBot.Bot/stubs/humps/camel/__init__.pyi: -------------------------------------------------------------------------------- 1 | def case(string: str) -> str: ... 2 | -------------------------------------------------------------------------------- /ClemBot.Bot/stubs/markdownify/__init__.pyi: -------------------------------------------------------------------------------- 1 | import typing as t 2 | 3 | def markdownify(html: str, **options: t.Any) -> str: ... 4 | -------------------------------------------------------------------------------- /ClemBot.Bot/stubs/nltk/__init__.pyi: -------------------------------------------------------------------------------- 1 | # very very incomplete 2 | 3 | import typing 4 | 5 | T = typing.TypeVar("T") 6 | 7 | def trigrams( 8 | sequence: typing.Sequence[T], **kwargs: typing.Any 9 | ) -> typing.Generator[tuple[T, ...], None, None]: ... 10 | -------------------------------------------------------------------------------- /ClemBot.Bot/stubs/seqlog/__init__.pyi: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | 3 | from _typeshed import Incomplete 4 | from seqlog.structured_logging import ConsoleStructuredLogHandler as ConsoleStructuredLogHandler 5 | from seqlog.structured_logging import SeqLogHandler as SeqLogHandler 6 | from seqlog.structured_logging import StructuredLogger as StructuredLogger 7 | from seqlog.structured_logging import StructuredRootLogger as StructuredRootLogger 8 | from seqlog.structured_logging import set_callback_on_failure as set_callback_on_failure 9 | 10 | def configure_from_file( 11 | file_name: Any, override_root_logger: bool = ..., use_structured_logger: bool = ... 12 | ) -> None: ... 13 | def configure_from_dict( 14 | config: Any, override_root_logger: bool = ..., use_structured_logger: bool = ... 15 | ) -> None: ... 16 | def log_to_seq( 17 | server_url: Any, 18 | api_key: Incomplete | None = ..., 19 | level: Any = ..., 20 | batch_size: int = ..., 21 | auto_flush_timeout: Incomplete | None = ..., 22 | additional_handlers: Incomplete | None = ..., 23 | override_root_logger: bool = ..., 24 | json_encoder_class: Incomplete | None = ..., 25 | **kwargs: Any 26 | ) -> Any: ... 27 | def log_to_console(level: Any = ..., override_root_logger: bool = ..., **kwargs: Any) -> None: ... 28 | def get_global_log_properties() -> None: ... 29 | def set_global_log_properties(**properties: Any) -> None: ... 30 | def reset_global_log_properties() -> None: ... 31 | def clear_global_log_properties() -> None: ... 32 | -------------------------------------------------------------------------------- /ClemBot.Bot/stubs/seqlog/consumer.pyi: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | 3 | from _typeshed import Incomplete 4 | 5 | class QueueConsumer: 6 | is_running: bool 7 | state_lock: Incomplete 8 | consumer_thread: Incomplete 9 | flush_timer: Incomplete 10 | current_batch: Incomplete 11 | name: Incomplete 12 | queue: Incomplete 13 | callback: Incomplete 14 | batch_size: Incomplete 15 | auto_flush_timeout: Incomplete 16 | def __init__( 17 | self, 18 | name: Any, 19 | queue: Any, 20 | callback: Any, 21 | batch_size: Any, 22 | auto_flush_timeout: Incomplete | None = ..., 23 | ) -> None: ... 24 | @property 25 | def current_batch_size(self) -> Any: ... 26 | def flush(self) -> None: ... 27 | def start(self) -> None: ... 28 | def stop(self) -> None: ... 29 | -------------------------------------------------------------------------------- /ClemBot.Bot/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClemBotProject/ClemBot/5b169b1d45d581660fff4438aab920fb4101f64b/ClemBot.Bot/tests/__init__.py -------------------------------------------------------------------------------- /ClemBot.Bot/tests/bot/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClemBotProject/ClemBot/5b169b1d45d581660fff4438aab920fb4101f64b/ClemBot.Bot/tests/bot/__init__.py -------------------------------------------------------------------------------- /ClemBot.Bot/tests/bot/messaging/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClemBotProject/ClemBot/5b169b1d45d581660fff4438aab920fb4101f64b/ClemBot.Bot/tests/bot/messaging/__init__.py -------------------------------------------------------------------------------- /ClemBot.Bot/tests/bot/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClemBotProject/ClemBot/5b169b1d45d581660fff4438aab920fb4101f64b/ClemBot.Bot/tests/bot/utils/__init__.py -------------------------------------------------------------------------------- /ClemBot.Docs/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | -------------------------------------------------------------------------------- /ClemBot.Docs/README.md: -------------------------------------------------------------------------------- 1 | # Website 2 | 3 | This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator. 4 | 5 | ### Installation 6 | 7 | ``` 8 | $ yarn 9 | ``` 10 | 11 | ### Local Development 12 | 13 | ``` 14 | $ yarn start 15 | ``` 16 | 17 | This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. 18 | 19 | ### Build 20 | 21 | ``` 22 | $ yarn build 23 | ``` 24 | 25 | This command generates static content into the `build` directory and can be served using any static contents hosting service. 26 | 27 | ### Deployment 28 | 29 | Using SSH: 30 | 31 | ``` 32 | $ USE_SSH=true yarn deploy 33 | ``` 34 | 35 | Not using SSH: 36 | 37 | ``` 38 | $ GIT_USER= yarn deploy 39 | ``` 40 | 41 | If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. 42 | -------------------------------------------------------------------------------- /ClemBot.Docs/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 3 | }; 4 | -------------------------------------------------------------------------------- /ClemBot.Docs/docs/CustomPrefix.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 2 3 | --- 4 | 5 | # Custom Prefix 6 | 7 | ## Overview 8 | 9 | A command prefix is the character or phrase that notifies ClemBot that you wish to invoke a command. 10 | 11 | ### Command Example 12 | 13 | ```txt title="Discord Message" 14 | !about 15 | ``` 16 | 17 | `!` is the command prefix and `about` is the name of the command that you wish to invoke. 18 | 19 | :::tip 20 | ClemBot's mention serves as a universal prefix. 21 | It can always be used to invoke the bot's commands. 22 | 23 | Use `@ClemBot prefix` to find out what prefix ClemBot has in your server. 24 | ::: 25 | 26 | ## Dashboard 27 | 28 | Changes a server's prefix to a given value. 29 | If no value is provided, the current in-use prefix is shown. 30 | 31 | ## Commands 32 | 33 | ### Prefix 34 | 35 | #### Aliases 36 | 37 | * `prefixes` 38 | 39 | #### Required [Claims](./Claims.md) 40 | 41 | ``` 42 | custom_prefix_set 43 | ``` 44 | 45 | #### Format 46 | 47 | ```txt title="View the current prefix" 48 | !prefix 49 | ``` 50 | 51 | ```txt title="Change the current prefix" 52 | !prefix 53 | ``` 54 | 55 | #### Example 56 | 57 | ``` 58 | !prefix 59 | ``` 60 | 61 | ``` 62 | !prefix ? 63 | ``` 64 | 65 | ### Reset 66 | 67 | Resets the bot prefix to the default, `!`. 68 | 69 | #### Aliases 70 | 71 | - `revert` 72 | 73 | #### Required [Claims](./Claims.md) 74 | 75 | ``` 76 | custom_prefix_set 77 | ``` 78 | 79 | #### Format 80 | 81 | ``` 82 | !prefix reset 83 | ``` 84 | 85 | #### Example 86 | 87 | ``` 88 | !prefix reset 89 | ``` 90 | 91 | ``` 92 | !prefix revert 93 | ``` -------------------------------------------------------------------------------- /ClemBot.Docs/docs/Moderation/Ban.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 2 3 | --- 4 | 5 | # Ban 6 | 7 | ## Overview 8 | 9 | Bans remove an individual from a server permanently (until undone) and blocks their IP from rejoining. Optionally, users 10 | can specify the number of days of messages from which to purge/delete a user's messages. 11 | 12 | :::note 13 | The IP ban is done on Discord's side and can be easily circumvented by using a proxy. 14 | However, enabling phone verification in your server can make ban circumvention much more difficult. 15 | ::: 16 | 17 | To undo a ban on a user, navigate to your server/guild settings and under the `Moderation` group, click the `Bans` tab. 18 | From there, select the user you wish to un-ban and click the `Revoke Ban` button. 19 | 20 | ## Commands 21 | 22 | ### Ban 23 | 24 | #### Required [Claims](../Claims.md) 25 | 26 | * `moderation_ban` 27 | 28 | #### Format 29 | 30 | ``` 31 | !ban @User [DaysToPurge] 32 | ``` 33 | 34 | :::note 35 | If `DaysToPurge` is not specified, it defaults to `0`, with a maximum value of `7`. 36 | ::: 37 | 38 | #### Example 39 | 40 | ``` 41 | !ban @User Spamming and trolling! 42 | ``` 43 | 44 | ``` 45 | !ban @User 3 Spamming and trolling so bad we want to delete 3 days of messages! 46 | ``` -------------------------------------------------------------------------------- /ClemBot.Docs/docs/Moderation/Overview.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | --- 4 | 5 | # Overview 6 | 7 | ClemBot provides powerful moderation tools to help you run your server. 8 | Every moderation action is logged and categorized and can be searched through at any time. 9 | You have a complete history of every infraction ever committed in your server. 10 | To view them, run the commands below. 11 | 12 | ## Logging 13 | 14 | ClemBot will log all moderation actions into a given channel if configured. 15 | See [Designated Channels](../DesignatedChannels.md) for more information on how to enable moderation logging. 16 | 17 | ## Commands 18 | 19 | ### Infractions 20 | 21 | #### Required [Claims](../Claims.md) 22 | 23 | `moderation_infraction_view` 24 | or 25 | `moderation_infraction_view_self` 26 | 27 | :::info 28 | The `moderation_infraction_view_self` only gives a user permission to view their **OWN** infractions. 29 | ::: 30 | 31 | #### Format 32 | 33 | ```txt title="View your own infractions" 34 | !infractions 35 | ``` 36 | 37 | ```txt title="View a users infractions" 38 | !infractions 39 | ``` 40 | 41 | #### Example 42 | 43 | ``` 44 | !infractions 45 | 46 | !infractions @SomeUser 47 | ``` 48 | 49 | ### Delete 50 | 51 | Deletes a given infraction 52 | 53 | #### Format 54 | 55 | ``` 56 | !infractions delete 57 | ``` 58 | 59 | :::tip 60 | You can find the infraction ID by running the `!infractions` command on the user you wish to remove the infraction from. 61 | ::: 62 | 63 | #### Example 64 | 65 | ``` 66 | !infractions delete 12 67 | ``` -------------------------------------------------------------------------------- /ClemBot.Docs/docs/Moderation/Warning.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 3 3 | --- 4 | 5 | # Warn 6 | 7 | ## Overview 8 | 9 | Warnings are a way to create a formal record of wrongdoing and serve as a way to keep track of long-standing or historic 10 | bad behavior. 11 | You can view the reasons and the dates for any warning given. 12 | 13 | :::info 14 | You can find the infraction ID by running the `!infractions` command on the user you warned. 15 | ::: 16 | 17 | ## Commands 18 | 19 | ### Warn 20 | 21 | #### Required [Claims](../Claims.md) 22 | 23 | * `moderation_warn` 24 | 25 | #### Format 26 | 27 | ``` 28 | !warn @User 29 | ``` 30 | 31 | #### Example 32 | 33 | ``` 34 | !warn @User Spamming and trolling! 35 | ``` -------------------------------------------------------------------------------- /ClemBot.Docs/docs/WelcomeMessage.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 7 3 | --- 4 | 5 | # Welcome Message 6 | 7 | ## Overview 8 | 9 | Welcome messages are a way to define a message that ClemBot will dm to all members that join your server. You can use 10 | this to notify users of any rules or setup they may need to know, or just say hello! 11 | 12 | ## Dashboard 13 | 14 | A guilds welcome message can be viewed and set from the guild tab on the dashboard. 15 | 16 | ## Commands 17 | 18 | ### Welcome 19 | 20 | View the current welcome message 21 | 22 | #### Required [Claims](./Claims.md) 23 | 24 | * `welcome_message_view` 25 | 26 | #### Example 27 | 28 | ```txt 29 | !welcome 30 | ``` 31 | 32 | ### Set 33 | 34 | Sets a servers welcome message 35 | 36 | #### Required [Claims](./Claims.md) 37 | 38 | * `welcome_message_modify` 39 | 40 | #### Format 41 | 42 | ``` 43 | !welcome set 44 | ``` 45 | 46 | #### Example 47 | 48 | ```txt 49 | !welcome set welcome to our amazing server 50 | ``` 51 | 52 | ### Delete 53 | 54 | Deletes a servers welcome message 55 | 56 | #### Required [Claims](./Claims.md) 57 | 58 | * `welcome_message_modify` 59 | 60 | #### Example 61 | 62 | ```txt 63 | !welcome delete 64 | ``` -------------------------------------------------------------------------------- /ClemBot.Docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "clem-bot-docs", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "docusaurus": "docusaurus", 7 | "start": "docusaurus start", 8 | "build": "docusaurus build", 9 | "swizzle": "docusaurus swizzle", 10 | "deploy": "docusaurus deploy", 11 | "clear": "docusaurus clear", 12 | "serve": "docusaurus serve", 13 | "write-translations": "docusaurus write-translations", 14 | "write-heading-ids": "docusaurus write-heading-ids", 15 | "typecheck": "tsc", 16 | "v": "node -v" 17 | }, 18 | "dependencies": { 19 | "@docusaurus/core": "2.4.1", 20 | "@docusaurus/preset-classic": "2.4.1", 21 | "@mdx-js/react": "^1.6.21", 22 | "clsx": "^1.1.1", 23 | "docusaurus-plugin-sass": "^0.2.5", 24 | "node": "16.14.0", 25 | "prism-react-renderer": "^1.2.1", 26 | "react": "^17.0.1", 27 | "react-dom": "^17.0.1", 28 | "sass": "^1.49.0" 29 | }, 30 | "devDependencies": { 31 | "@docusaurus/module-type-aliases": "2.4.1", 32 | "@tsconfig/docusaurus": "^2.0.0", 33 | "typescript": "^4.5.2" 34 | }, 35 | "browserslist": { 36 | "production": [ 37 | ">0.5%", 38 | "not dead", 39 | "not op_mini all" 40 | ], 41 | "development": [ 42 | "last 1 chrome version", 43 | "last 1 firefox version", 44 | "last 1 safari version" 45 | ] 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /ClemBot.Docs/sidebars.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Creating a sidebar enables you to: 3 | - create an ordered group of docs 4 | - render a sidebar for each doc of that group 5 | - provide next/previous navigation 6 | 7 | The sidebars can be generated from the filesystem, or explicitly defined here. 8 | 9 | Create as many sidebars as you want. 10 | */ 11 | 12 | // @ts-check 13 | 14 | /** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ 15 | const sidebars = { 16 | // By default, Docusaurus generates a sidebar from the docs folder structure 17 | tutorialSidebar: [{type: 'autogenerated', dirName: '.'}], 18 | 19 | // But you can create a sidebar manually 20 | /* 21 | tutorialSidebar: [ 22 | { 23 | type: 'category', 24 | label: 'Tutorial', 25 | items: ['hello'], 26 | }, 27 | ], 28 | */ 29 | }; 30 | 31 | module.exports = sidebars; 32 | -------------------------------------------------------------------------------- /ClemBot.Docs/src/components/temp.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClemBotProject/ClemBot/5b169b1d45d581660fff4438aab920fb4101f64b/ClemBot.Docs/src/components/temp.txt -------------------------------------------------------------------------------- /ClemBot.Docs/src/pages/index.module.css: -------------------------------------------------------------------------------- 1 | /** 2 | * CSS files with the .module.css suffix will be treated as CSS modules 3 | * and scoped locally. 4 | */ 5 | 6 | .heroBanner { 7 | padding: 4rem 0; 8 | text-align: center; 9 | position: relative; 10 | overflow: hidden; 11 | } 12 | 13 | @media screen and (max-width: 966px) { 14 | .heroBanner { 15 | padding: 2rem; 16 | } 17 | } 18 | 19 | .buttons { 20 | display: flex; 21 | align-items: center; 22 | justify-content: center; 23 | } 24 | -------------------------------------------------------------------------------- /ClemBot.Docs/src/pages/markdown-page.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Markdown page example 3 | --- 4 | 5 | # Markdown page example 6 | 7 | You don't need React to write simple standalone pages. 8 | -------------------------------------------------------------------------------- /ClemBot.Docs/static/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClemBotProject/ClemBot/5b169b1d45d581660fff4438aab920fb4101f64b/ClemBot.Docs/static/.nojekyll -------------------------------------------------------------------------------- /ClemBot.Docs/static/img/ClemBotLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClemBotProject/ClemBot/5b169b1d45d581660fff4438aab920fb4101f64b/ClemBot.Docs/static/img/ClemBotLogo.png -------------------------------------------------------------------------------- /ClemBot.Docs/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClemBotProject/ClemBot/5b169b1d45d581660fff4438aab920fb4101f64b/ClemBot.Docs/static/img/favicon.ico -------------------------------------------------------------------------------- /ClemBot.Docs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // This file is not used in compilation. It is here just for a nice editor experience. 3 | "extends": "@tsconfig/docusaurus/tsconfig.json", 4 | "compilerOptions": { 5 | "baseUrl": "." 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ClemBot.Site/.dockerignore: -------------------------------------------------------------------------------- 1 | .nuxt 2 | node_modules 3 | npm-debug 4 | -------------------------------------------------------------------------------- /ClemBot.Site/.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /ClemBot.Site/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | browser: true, 5 | node: true, 6 | }, 7 | extends: [ 8 | '@nuxtjs/eslint-config-typescript', 9 | 'plugin:prettier/recommended', 10 | 'plugin:nuxt/recommended', 11 | ], 12 | plugins: [], 13 | // add your custom rules here 14 | rules: {}, 15 | } 16 | -------------------------------------------------------------------------------- /ClemBot.Site/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true 4 | } 5 | -------------------------------------------------------------------------------- /ClemBot.Site/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "pwa-chrome", 9 | "request": "launch", 10 | "name": "Launch Chrome against localhost", 11 | "url": "http://localhost:3000", 12 | "webRoot": "${workspaceFolder}" 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /ClemBot.Site/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:14-alpine 2 | 3 | # Create app directory 4 | WORKDIR /usr/src/clembot.site 5 | 6 | ENV PROD=1 7 | 8 | ARG DISCORD_CLIENT_ID_ARG 9 | ENV DISCORD_CLIENT_ID ${DISCORD_CLIENT_ID_ARG} 10 | 11 | ARG DISCORD_CLIENT_SECRET_ARG 12 | ENV DISCORD_CLIENT_SECRET ${DISCORD_CLIENT_SECRET_ARG} 13 | 14 | ARG OAUTH_PERMISSIONS_ARG 15 | ENV OAUTH_PERMISSIONS ${OAUTH_PERMISSIONS_ARG} 16 | 17 | COPY package*.json ./ 18 | 19 | COPY . . 20 | 21 | RUN npm install && npm run build 22 | 23 | CMD ["npm", "start"] 24 | -------------------------------------------------------------------------------- /ClemBot.Site/README.md: -------------------------------------------------------------------------------- 1 | # nuxt-ts 2 | 3 | ## Build Setup 4 | 5 | ```bash 6 | # install dependencies 7 | $ npm install 8 | 9 | # serve with hot reload at localhost:3000 10 | $ npm run dev 11 | 12 | # build for production and launch server 13 | $ npm run build 14 | $ npm run start 15 | 16 | # generate static project 17 | $ npm run generate 18 | ``` 19 | 20 | For detailed explanation on how things work, check out [Nuxt.js docs](https://nuxtjs.org). 21 | -------------------------------------------------------------------------------- /ClemBot.Site/assets/README.md: -------------------------------------------------------------------------------- 1 | # ASSETS 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your un-compiled assets such as LESS, SASS, or JavaScript. 6 | 7 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#webpacked). 8 | -------------------------------------------------------------------------------- /ClemBot.Site/assets/buefy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClemBotProject/ClemBot/5b169b1d45d581660fff4438aab920fb4101f64b/ClemBot.Site/assets/buefy.png -------------------------------------------------------------------------------- /ClemBot.Site/components/DiscordLogo.vue: -------------------------------------------------------------------------------- 1 | 22 | -------------------------------------------------------------------------------- /ClemBot.Site/components/FeatureCard.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 37 | 38 | 50 | -------------------------------------------------------------------------------- /ClemBot.Site/components/Logo.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 36 | -------------------------------------------------------------------------------- /ClemBot.Site/components/UserDisplay.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | -------------------------------------------------------------------------------- /ClemBot.Site/components/support/SponsorButton.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 36 | -------------------------------------------------------------------------------- /ClemBot.Site/layouts/README.md: -------------------------------------------------------------------------------- 1 | # LAYOUTS 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your Application Layouts. 6 | 7 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/views#layouts). 8 | -------------------------------------------------------------------------------- /ClemBot.Site/middleware/DashboardAuthCheck.ts: -------------------------------------------------------------------------------- 1 | import { Middleware } from '@nuxt/types' 2 | 3 | const DashboardAuthCheck: Middleware = async ({ app: { $auth }, redirect }) => { 4 | if (!$auth.loggedIn) { 5 | redirect('/') 6 | } 7 | } 8 | 9 | export default DashboardAuthCheck -------------------------------------------------------------------------------- /ClemBot.Site/middleware/HomeAuthCheck.ts: -------------------------------------------------------------------------------- 1 | import { Middleware } from '@nuxt/types' 2 | 3 | const HomeAuthCheck: Middleware = async ({ app: { $auth, $axios } }) => { 4 | if (!$auth.loggedIn) { 5 | return 6 | } 7 | 8 | if ($auth.loggedIn && $auth.strategy.name === 'local') { 9 | 10 | } 11 | 12 | if ($auth.loggedIn && $auth.strategy.name === 'discord') { 13 | await $auth.loginWith('local', { 14 | // @ts-ignore 15 | data: { bearer: $auth.strategy.token.get().split(' ')[1] }, 16 | }) 17 | } 18 | } 19 | 20 | export default HomeAuthCheck 21 | -------------------------------------------------------------------------------- /ClemBot.Site/middleware/README.md: -------------------------------------------------------------------------------- 1 | # MIDDLEWARE 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your application middleware. 6 | Middleware let you define custom functions that can be run before rendering either a page or a group of pages. 7 | 8 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing#middleware). 9 | -------------------------------------------------------------------------------- /ClemBot.Site/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nuxt-ts", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "nuxt", 7 | "build": "nuxt build", 8 | "start": "nuxt start", 9 | "generate": "nuxt generate", 10 | "lint:js": "eslint --ext \".js,.vue\" --ignore-path .gitignore .", 11 | "lint": "npm run lint:js" 12 | }, 13 | "dependencies": { 14 | "@nuxtjs/auth-next": "5.0.0-1624817847.21691f1", 15 | "@nuxtjs/axios": "^5.13.6", 16 | "core-js": "^3.9.1", 17 | "discord-oauth2": "^2.7.1", 18 | "fibers": "^5.0.1", 19 | "node-sass": "npm:sass@^1.45.0", 20 | "nuxt": "^2.15.3", 21 | "nuxt-buefy": "^0.4.4", 22 | "sass": "^1.32.0", 23 | "vuex-module-decorators": "^1.0.1" 24 | }, 25 | "devDependencies": { 26 | "@creativebulma/bulma-divider": "^1.1.0", 27 | "@creativebulma/bulma-tooltip": "^1.2.0", 28 | "@nuxt/types": "^2.15.3", 29 | "@nuxt/typescript-build": "^2.1.0", 30 | "@nuxtjs/dotenv": "^1.4.1", 31 | "@nuxtjs/eslint-config-typescript": "^6.0.0", 32 | "@nuxtjs/eslint-module": "^3.0.2", 33 | "babel-eslint": "^10.1.0", 34 | "eslint": "^7.22.0", 35 | "eslint-config-prettier": "^8.1.0", 36 | "eslint-plugin-nuxt": "^2.0.0", 37 | "eslint-plugin-prettier": "^3.3.1", 38 | "eslint-plugin-vue": "^7.7.0", 39 | "prettier": "^2.2.1", 40 | "sass-loader": "^10.2.0" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ClemBot.Site/pages/README.md: -------------------------------------------------------------------------------- 1 | # PAGES 2 | 3 | This directory contains your Application Views and Routes. 4 | The framework reads all the `*.vue` files inside this directory and creates the router of your application. 5 | 6 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing). 7 | -------------------------------------------------------------------------------- /ClemBot.Site/pages/dashboard/_id/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 13 | -------------------------------------------------------------------------------- /ClemBot.Site/pages/login.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /ClemBot.Site/pages/status.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /ClemBot.Site/pages/support.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 26 | -------------------------------------------------------------------------------- /ClemBot.Site/plugins/Api.ts: -------------------------------------------------------------------------------- 1 | import { Context, Plugin } from '@nuxt/types' 2 | import { Inject } from '@nuxt/types/app' 3 | import ApiClient from '~/services/api/ApiClient' 4 | 5 | declare module '@nuxt/types' { 6 | interface Context { 7 | $api: ApiClient 8 | } 9 | } 10 | 11 | declare module 'vue/types/vue' { 12 | interface Vue { 13 | $api: ApiClient 14 | } 15 | } 16 | 17 | const api: Plugin = (ctx: Context, inject: Inject) => { 18 | ctx.$api = new ApiClient(ctx.app.$axios) 19 | inject('api', ctx.$api) 20 | } 21 | 22 | export default api 23 | -------------------------------------------------------------------------------- /ClemBot.Site/plugins/Axios.js: -------------------------------------------------------------------------------- 1 | import https from 'https'; 2 | 3 | export default function ({ $axios }) { 4 | $axios.defaults.httpsAgent = new https.Agent({ rejectUnauthorized: false }); 5 | } -------------------------------------------------------------------------------- /ClemBot.Site/plugins/README.md: -------------------------------------------------------------------------------- 1 | # PLUGINS 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains Javascript plugins that you want to run before mounting the root Vue.js application. 6 | 7 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/plugins). 8 | -------------------------------------------------------------------------------- /ClemBot.Site/services/Claims.ts: -------------------------------------------------------------------------------- 1 | export enum BotAuthClaims 2 | { 3 | designatedChannelView = "designated_channel_view", 4 | designatedChannelModify = "designated_channel_modify", 5 | customPrefixSet = "custom_prefix_set", 6 | welcomeMessageView = "welcome_message_view", 7 | welcomeMessageModify = "welcome_message_modify", 8 | tagAdd = "tag_add", 9 | tagDelete = "tag_delete", 10 | assignableRolesAdd = "assignable_roles_add", 11 | assignableRolesDelete = "assignable_roles_delete", 12 | deleteMessage = "delete_message", 13 | emoteAdd = "emote_add", 14 | claimsView = "claims_view", 15 | claimsModify = "claims_modify", 16 | manageClassAdd = "manage_class_add", 17 | moderationWarn = "moderation_warn", 18 | moderationBan = "moderation_ban", 19 | moderationMute = "moderation_mute", 20 | moderationPurge = "moderation_purge", 21 | moderationInfractionView = "moderation_infraction_view", 22 | moderationInfractionViewSelf = "moderation_infraction_view_self", 23 | dashboardView = "dashboard_view", 24 | dashboardEdit = "dashboard_edit", 25 | guildSettingsView = "guild_settings_view", 26 | guildSettingsEdit = "guild_settings_edit" 27 | } -------------------------------------------------------------------------------- /ClemBot.Site/services/GuildSettings.ts: -------------------------------------------------------------------------------- 1 | export enum GuildSettings { 2 | allowEmbedLinks = 'allow_embed_links' 3 | } -------------------------------------------------------------------------------- /ClemBot.Site/services/Models/Guild.ts: -------------------------------------------------------------------------------- 1 | export interface Guild { 2 | id: string 3 | name: string 4 | icon: string 5 | owner: boolean 6 | permissions: number 7 | features: string[] 8 | claims: string[] 9 | isAdded: boolean 10 | isHovered: boolean 11 | } -------------------------------------------------------------------------------- /ClemBot.Site/services/Utilities.ts: -------------------------------------------------------------------------------- 1 | export function chunkArray( 2 | value: Array, 3 | size: number 4 | ): Array> { 5 | const chunks: Array> = [] 6 | 7 | let currentChunk: Array = [] 8 | 9 | for (const item of value) { 10 | if (currentChunk.length >= size) { 11 | chunks.push(currentChunk) 12 | currentChunk = [] 13 | } 14 | 15 | currentChunk.push(item) 16 | } 17 | 18 | chunks.push(currentChunk) 19 | return chunks 20 | } 21 | 22 | export function titleCase(str: string) { 23 | return str.replace( 24 | /\w\S*/g, 25 | function(txt: string) { 26 | return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); 27 | } 28 | ); 29 | } -------------------------------------------------------------------------------- /ClemBot.Site/services/api/ApiClient.ts: -------------------------------------------------------------------------------- 1 | import { NuxtAxiosInstance } from '@nuxtjs/axios' 2 | import Public from './routes/Public' 3 | import CustomPrefix from './routes/CustomPrefix' 4 | import WelcomeMessage from './routes/WelcomeMessage' 5 | import Tags from './routes/Tags' 6 | import GuildSettings from './routes/GuildSetting' 7 | 8 | export default class ApiClient { 9 | $axios: NuxtAxiosInstance 10 | public: Public 11 | customPrefix: CustomPrefix 12 | welcomeMessage: WelcomeMessage 13 | tags: Tags 14 | guildSettings: GuildSettings 15 | 16 | constructor(axios: NuxtAxiosInstance) { 17 | this.$axios = axios 18 | this.public = new Public(axios) 19 | this.customPrefix = new CustomPrefix(axios) 20 | this.welcomeMessage = new WelcomeMessage(axios) 21 | this.tags = new Tags(axios) 22 | this.guildSettings = new GuildSettings(axios) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ClemBot.Site/services/api/routes/CustomPrefix.ts: -------------------------------------------------------------------------------- 1 | import { NuxtAxiosInstance } from '@nuxtjs/axios' 2 | 3 | interface Prefix { 4 | prefixes: Array 5 | } 6 | 7 | export default class CustomPrefix { 8 | $axios: NuxtAxiosInstance 9 | 10 | constructor(axios: NuxtAxiosInstance) { 11 | this.$axios = axios 12 | } 13 | 14 | async getCustomPrefix(id: string): Promise { 15 | const prefixes = await this.$axios.$get( 16 | `guilds/${id}/customprefixes` 17 | ) 18 | return prefixes.prefixes[0] 19 | } 20 | 21 | async setCustomPrefix(id: string, prefix: string): Promise { 22 | await this.$axios.$post('customprefixes/add', { 23 | guildId: id, 24 | prefix: prefix, 25 | }) 26 | return true 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /ClemBot.Site/services/api/routes/GuildSetting.ts: -------------------------------------------------------------------------------- 1 | import { NuxtAxiosInstance } from '@nuxtjs/axios' 2 | import { GuildSettings } from '~/services/GuildSettings' 3 | 4 | interface Setting { 5 | setting: GuildSettings 6 | } 7 | 8 | export default class GuildSetting { 9 | $axios: NuxtAxiosInstance 10 | 11 | constructor(axios: NuxtAxiosInstance) { 12 | this.$axios = axios 13 | } 14 | 15 | async getCanEmbedLink(id: string): Promise { 16 | const setting = await this.$axios.$get( 17 | `guildsettings/${id}/${GuildSettings.allowEmbedLinks}` 18 | ) 19 | 20 | return setting.value 21 | } 22 | 23 | async setCanEmbedLink(id: string, val: boolean): Promise { 24 | let resp = await this.$axios.$post(`guildsettings/${id}/${GuildSettings.allowEmbedLinks}`, `"${val}"`, {headers: { 25 | // Overwrite Axios's automatically set Content-Type 26 | 'Content-Type': 'application/json' 27 | }}) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /ClemBot.Site/services/api/routes/Public.ts: -------------------------------------------------------------------------------- 1 | import { NuxtAxiosInstance } from '@nuxtjs/axios' 2 | 3 | interface GlobalStats { 4 | guilds: string 5 | users: string 6 | commands: string 7 | } 8 | 9 | export default class Public { 10 | $axios: NuxtAxiosInstance 11 | 12 | constructor(axios: NuxtAxiosInstance) { 13 | this.$axios = axios 14 | } 15 | 16 | async getGlobalStats(): Promise { 17 | let stats; 18 | try { 19 | stats = await this.$axios.$get('public/globalstats') 20 | } 21 | catch(e) { 22 | console.log(e) 23 | } 24 | 25 | return stats ?? null 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /ClemBot.Site/services/api/routes/Tags.ts: -------------------------------------------------------------------------------- 1 | import { NuxtAxiosInstance } from '@nuxtjs/axios' 2 | 3 | export interface Tag { 4 | name: string 5 | content: string 6 | creationDate: string 7 | userId: string 8 | userName: string 9 | useCount: string 10 | } 11 | 12 | interface Model { 13 | tags: Array 14 | } 15 | 16 | export default class Tags{ 17 | $axios: NuxtAxiosInstance 18 | 19 | constructor(axios: NuxtAxiosInstance) { 20 | this.$axios = axios 21 | } 22 | 23 | async getGuildTags(id: string): Promise> { 24 | let resp = await this.$axios.$get(`guilds/${id}/tags`) 25 | return resp.tags 26 | } 27 | 28 | async createTag(name: string, content: string, guildId: string, userId: string){ 29 | let resp = await this.$axios.$post('tags', { 30 | name, 31 | content, 32 | guildId, 33 | userId 34 | }) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /ClemBot.Site/services/api/routes/WelcomeMessage.ts: -------------------------------------------------------------------------------- 1 | import { github } from '@nuxtjs/auth-next' 2 | import { NuxtAxiosInstance } from '@nuxtjs/axios' 3 | 4 | interface WelcomeMessage{ 5 | message: string 6 | } 7 | 8 | export default class CustomPrefix { 9 | $axios: NuxtAxiosInstance 10 | 11 | constructor(axios: NuxtAxiosInstance) { 12 | this.$axios = axios 13 | } 14 | 15 | async getWelcomeMessage(id: string): Promise { 16 | let foo = await this.$axios.$get(`guilds/${id}/getWelcomeMessage`) 17 | return foo.message 18 | } 19 | 20 | async setWelcomeMessage(id: string, message: string): Promise { 21 | await this.$axios.$post(`guilds/${id}/SetWelcomeMessage`, { 22 | Message: message, 23 | }) 24 | return true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ClemBot.Site/static/ClemBotLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClemBotProject/ClemBot/5b169b1d45d581660fff4438aab920fb4101f64b/ClemBot.Site/static/ClemBotLogo.png -------------------------------------------------------------------------------- /ClemBot.Site/static/FeatureImages/AssignRoles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClemBotProject/ClemBot/5b169b1d45d581660fff4438aab920fb4101f64b/ClemBot.Site/static/FeatureImages/AssignRoles.png -------------------------------------------------------------------------------- /ClemBot.Site/static/FeatureImages/CustomPrefix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClemBotProject/ClemBot/5b169b1d45d581660fff4438aab920fb4101f64b/ClemBot.Site/static/FeatureImages/CustomPrefix.png -------------------------------------------------------------------------------- /ClemBot.Site/static/FeatureImages/MemberBan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClemBotProject/ClemBot/5b169b1d45d581660fff4438aab920fb4101f64b/ClemBot.Site/static/FeatureImages/MemberBan.png -------------------------------------------------------------------------------- /ClemBot.Site/static/FeatureImages/MemberMute.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClemBotProject/ClemBot/5b169b1d45d581660fff4438aab920fb4101f64b/ClemBot.Site/static/FeatureImages/MemberMute.png -------------------------------------------------------------------------------- /ClemBot.Site/static/FeatureImages/MessageEdit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClemBotProject/ClemBot/5b169b1d45d581660fff4438aab920fb4101f64b/ClemBot.Site/static/FeatureImages/MessageEdit.png -------------------------------------------------------------------------------- /ClemBot.Site/static/FeatureImages/SlotMachineInvoke.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClemBotProject/ClemBot/5b169b1d45d581660fff4438aab920fb4101f64b/ClemBot.Site/static/FeatureImages/SlotMachineInvoke.png -------------------------------------------------------------------------------- /ClemBot.Site/static/FeatureImages/TagInvoke.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClemBotProject/ClemBot/5b169b1d45d581660fff4438aab920fb4101f64b/ClemBot.Site/static/FeatureImages/TagInvoke.png -------------------------------------------------------------------------------- /ClemBot.Site/static/FeatureImages/UserJoinEmbed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClemBotProject/ClemBot/5b169b1d45d581660fff4438aab920fb4101f64b/ClemBot.Site/static/FeatureImages/UserJoinEmbed.png -------------------------------------------------------------------------------- /ClemBot.Site/static/README.md: -------------------------------------------------------------------------------- 1 | # STATIC 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your static files. 6 | Each file inside this directory is mapped to `/`. 7 | Thus you'd want to delete this README.md before deploying to production. 8 | 9 | Example: `/static/robots.txt` is mapped as `/robots.txt`. 10 | 11 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#static). 12 | -------------------------------------------------------------------------------- /ClemBot.Site/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClemBotProject/ClemBot/5b169b1d45d581660fff4438aab920fb4101f64b/ClemBot.Site/static/favicon.ico -------------------------------------------------------------------------------- /ClemBot.Site/store/README.md: -------------------------------------------------------------------------------- 1 | # STORE 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your Vuex Store files. 6 | Vuex Store option is implemented in the Nuxt.js framework. 7 | 8 | Creating a file in this directory automatically activates the option in the framework. 9 | 10 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/vuex-store). 11 | -------------------------------------------------------------------------------- /ClemBot.Site/store/index.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClemBotProject/ClemBot/5b169b1d45d581660fff4438aab920fb4101f64b/ClemBot.Site/store/index.ts -------------------------------------------------------------------------------- /ClemBot.Site/tempt.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ClemBotProject/ClemBot/5b169b1d45d581660fff4438aab920fb4101f64b/ClemBot.Site/tempt.txt -------------------------------------------------------------------------------- /ClemBot.Site/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2018", 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "lib": [ 7 | "ESNext", 8 | "ESNext.AsyncIterable", 9 | "DOM" 10 | ], 11 | "esModuleInterop": true, 12 | "allowJs": true, 13 | "sourceMap": true, 14 | "strict": true, 15 | "noEmit": true, 16 | "experimentalDecorators": true, 17 | "baseUrl": ".", 18 | "paths": { 19 | "~/*": [ 20 | "./*" 21 | ], 22 | "@/*": [ 23 | "./*" 24 | ] 25 | }, 26 | "types": [ 27 | "@nuxt/types", 28 | "@nuxtjs/axios", 29 | "@types/node", 30 | "@nuxtjs/auth-next", 31 | ] 32 | }, 33 | "exclude": [ 34 | "node_modules", 35 | ".nuxt", 36 | "dist" 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2024 Jay Cox 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | --------------------------------------------------------------------------------