├── .github
├── FUNDING.yml
└── workflows
│ └── build-and-test.yml
├── .gitignore
├── CB.sln
├── LICENSE
├── PRIVACYPOLICY.md
├── README.md
├── TERMSOFUSE.md
├── docker-compose.yml
└── src
├── CB.Accessors
├── CB.Accessors.csproj
├── Contracts
│ ├── IAllowConfigurationAccessor.cs
│ ├── IChannelAccessor.cs
│ ├── IChannelConfigurationAccessor.cs
│ ├── IClipEmbedAccessor.cs
│ ├── ICreatorAccessor.cs
│ ├── ICreatorChannelAccessor.cs
│ ├── IDLiveAccessor.cs
│ ├── IDiscordLiveConfigurationAccessor.cs
│ ├── IDropdownPayloadAccessor.cs
│ ├── IFilterAccessor.cs
│ ├── IFunAccessor.cs
│ ├── IGuildAccessor.cs
│ ├── IGuildConfigurationAccessor.cs
│ ├── ILiveEmbedAccessor.cs
│ ├── IMessageConfigurationAccessor.cs
│ ├── IPicartoAccessor.cs
│ ├── IPiczelAccessor.cs
│ ├── IRoleConfigurationAccessor.cs
│ ├── ITrovoAccessor.cs
│ ├── ITwitchAccessor.cs
│ ├── IUserAccessor.cs
│ ├── IVodEmbedAccessor.cs
│ └── IYouTubeAccessor.cs
└── Implementations
│ ├── AllowConfigurationAccessor.cs
│ ├── ChannelAccessor.cs
│ ├── ChannelConfigurationAccessor.cs
│ ├── ClipEmbedAccessor.cs
│ ├── CreatorAccessor.cs
│ ├── CreatorChannelAccessor.cs
│ ├── DLiveAccessor.cs
│ ├── DiscordLiveConfigurationAccessor.cs
│ ├── DropdownPayloadAccessor.cs
│ ├── FilterAccessor.cs
│ ├── FunAccessor.cs
│ ├── GuildAccessor.cs
│ ├── GuildConfigurationAccessor.cs
│ ├── LiveEmbedAccessor.cs
│ ├── MessageConfigurationAccessor.cs
│ ├── PicartoAccessor.cs
│ ├── PiczelAccessor.cs
│ ├── RoleConfigurationAccessor.cs
│ ├── TrovoAccessor.cs
│ ├── TwitchAccessor.cs
│ ├── UserAccessor.cs
│ ├── VodEmbedAccessor.cs
│ └── YouTubeAccessor.cs
├── CB.Antenna
├── CB.Antenna.csproj
├── Dockerfile
├── Program.cs
├── Properties
│ └── launchSettings.json
├── Worker.cs
├── appsettings.Development.json
└── appsettings.json
├── CB.Api
├── CB.Api.csproj
├── CB.Api.http
├── Controllers
│ └── GuildController.cs
├── Dockerfile
├── Program.cs
├── Properties
│ └── launchSettings.json
├── appsettings.Development.json
└── appsettings.json
├── CB.Bot
├── CB.Bot.csproj
├── Commands
│ ├── Application
│ │ ├── BaseSlashCommands.cs
│ │ ├── ChannelSlashCommands.cs
│ │ ├── ConfigurationSlashCommands.cs
│ │ ├── CustomizationCommands.cs
│ │ ├── DiscordLiveSlashCommands.cs
│ │ ├── DiscoverySlashCommands.cs
│ │ ├── FilterSlashCommands.cs
│ │ ├── FunSlashCommands.cs
│ │ ├── ListSlashCommands.cs
│ │ ├── MessageSlashCommands.cs
│ │ ├── RoleSlashCommands.cs
│ │ └── UtilityApplicationCommands.cs
│ └── Text
│ │ └── UtilityTextCommands.cs
├── Dockerfile
├── Program.cs
├── Properties
│ └── launchSettings.json
├── Services
│ ├── DiscordBotService.cs
│ ├── GuildInteractionService.cs
│ └── MessageInteractionService.cs
├── appsettings.Development.json
└── appsettings.json
├── CB.Data
├── CB.Data.csproj
├── CbContext.cs
├── Entities
│ ├── AllowConfiguration.cs
│ ├── Channel.cs
│ ├── ChannelConfiguration.cs
│ ├── ClipEmbed.cs
│ ├── Creator.cs
│ ├── CreatorChannel.cs
│ ├── DiscordLiveConfiguration.cs
│ ├── DropdownPayload.cs
│ ├── Filter.cs
│ ├── FilterType.cs
│ ├── FunStuff.cs
│ ├── Game.cs
│ ├── GameChannel.cs
│ ├── Guild.cs
│ ├── GuildConfiguration.cs
│ ├── LiveEmbed.cs
│ ├── MessageConfiguration.cs
│ ├── Platform.cs
│ ├── RoleConfiguration.cs
│ ├── Team.cs
│ ├── TeamChannel.cs
│ ├── User.cs
│ └── VodEmbed.cs
└── Migrations
│ ├── 20250710194734_InitialCreate.Designer.cs
│ ├── 20250710194734_InitialCreate.cs
│ ├── 20250710201805_AddGuilds.Designer.cs
│ ├── 20250710201805_AddGuilds.cs
│ ├── 20250710203427_AddCreators.Designer.cs
│ ├── 20250710203427_AddCreators.cs
│ ├── 20250710220537_AddConfigsAndChannel.Designer.cs
│ ├── 20250710220537_AddConfigsAndChannel.cs
│ ├── 20250710235930_PendingMigration.Designer.cs
│ ├── 20250710235930_PendingMigration.cs
│ ├── 20250711023200_7102025-931p.Designer.cs
│ ├── 20250711023200_7102025-931p.cs
│ ├── 20250715022843_7142025-928p.Designer.cs
│ ├── 20250715022843_7142025-928p.cs
│ ├── 20250715022909_7142025-929p.Designer.cs
│ ├── 20250715022909_7142025-929p.cs
│ ├── 20250716021538_7152025-906p.Designer.cs
│ ├── 20250716021538_7152025-906p.cs
│ ├── 20250717022001_7162025-919p.Designer.cs
│ ├── 20250717022001_7162025-919p.cs
│ ├── 20250717225501_7172025-554p.Designer.cs
│ ├── 20250717225501_7172025-554p.cs
│ ├── 20250717225700_7172025-556p.Designer.cs
│ ├── 20250717225700_7172025-556p.cs
│ ├── 20250717225843_7172025-558p.Designer.cs
│ ├── 20250717225843_7172025-558p.cs
│ ├── 20250718013457_7172025-834p.Designer.cs
│ ├── 20250718013457_7172025-834p.cs
│ ├── 20250718014125_7172025-841p.Designer.cs
│ ├── 20250718014125_7172025-841p.cs
│ ├── 20250718015544_7172025-855p.Designer.cs
│ ├── 20250718015544_7172025-855p.cs
│ ├── 20250724033914_7232025-1036p.Designer.cs
│ ├── 20250724033914_7232025-1036p.cs
│ └── CbContextModelSnapshot.cs
├── CB.Engines
├── CB.Engines.csproj
├── Contracts
│ └── ICreatorEngine.cs
└── Implementations
│ └── CreatorEngine.cs
├── CB.Shared
├── CB.Shared.csproj
├── Constants.cs
├── Dtos
│ ├── AllowConfigurationDto.cs
│ ├── CbProfile.cs
│ ├── ChannelConfigurationDto.cs
│ ├── ChannelConfigurationSummaryDto.cs
│ ├── ChannelDto.cs
│ ├── ClipEmbedDto.cs
│ ├── CreatorChannelDto.cs
│ ├── CreatorDto.cs
│ ├── DiscordLiveConfigurationDto.cs
│ ├── DropdownPayloadDto.cs
│ ├── FilterDto.cs
│ ├── FilterTypeDto.cs
│ ├── GameChannelDto.cs
│ ├── GameDto.cs
│ ├── GuildConfigurationDto.cs
│ ├── GuildConfigurationSummaryDto.cs
│ ├── GuildDto.cs
│ ├── LiveEmbedDto.cs
│ ├── MessageConfigurationDto.cs
│ ├── PlatformDto.cs
│ ├── RoleConfigurationDto.cs
│ ├── TeamChannelDto.cs
│ ├── TeamDto.cs
│ ├── UserDto.cs
│ └── VodEmbedDto.cs
├── Enums
│ ├── ChannelType.cs
│ ├── ChannelTypeTwitch.cs
│ ├── ChannelTypeYouTube.cs
│ ├── ConfiguredChannelType.cs
│ ├── DropdownType.cs
│ ├── FilterType.cs
│ ├── MessageType.cs
│ └── Platform.cs
├── Models
│ ├── Bot
│ │ ├── GenericDropdownPayload.cs
│ │ ├── ListViewModel.cs
│ │ └── YouTubeCreatorDropdownPayload.cs
│ ├── DLive
│ │ └── DLiveUser.cs
│ ├── Picarto
│ │ ├── Channel.cs
│ │ ├── ChannelDetails.cs
│ │ ├── ChatSettings.cs
│ │ ├── Language.cs
│ │ ├── PicartoChannel.cs
│ │ ├── PicartoUser.cs
│ │ └── Thumbnails.cs
│ ├── Piczel
│ │ └── PiczelChannel.cs
│ ├── Trovo
│ │ ├── TrovoChannel.cs
│ │ └── TrovoUser.cs
│ ├── Twitch
│ │ ├── TwitchClientCredentials.cs
│ │ ├── TwitchClipsResponse.cs
│ │ ├── TwitchGameChannel.cs
│ │ ├── TwitchGameSearchResponse.cs
│ │ ├── TwitchStreamResponse.cs
│ │ ├── TwitchTeam.cs
│ │ └── TwitchUserResponse.cs
│ └── YouTube
│ │ ├── LiveChatStatusResponse.cs
│ │ ├── PageInfo.cs
│ │ ├── YouTubeChannelByIdResponse.cs
│ │ ├── YouTubeChannelSearchList.cs
│ │ ├── YouTubeChannelStatistics.cs
│ │ ├── YouTubePlaylist.cs
│ │ ├── YouTubeSearchListChannel.cs
│ │ └── YouTubeVideoListResponse.cs
├── Responses
│ ├── ChannelValidityResponse.cs
│ ├── GameValidityResponse.cs
│ ├── TeamValidityResponse.cs
│ └── ValidityResponse.cs
└── Utilities
│ ├── ApiUtilities.cs
│ └── StringUtilities.cs
└── tests
├── CB.Tests.Accessors
├── CB.Tests.Accessors.csproj
└── GuildAccessorTests.cs
└── CB.Tests.Api
├── CB.Tests.Api.csproj
└── GuildsControllerTests.cs
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: MattTheDev
4 | patreon: CouchBot
5 | ko_fi: MattTheDev
6 | custom: "https://paypal.me/dawgeth"
7 |
--------------------------------------------------------------------------------
/.github/workflows/build-and-test.yml:
--------------------------------------------------------------------------------
1 | # This workflow will build a .NET project
2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net
3 |
4 | name: .NET
5 |
6 | on:
7 | push:
8 | branches: [ "main" ]
9 | pull_request:
10 | branches: [ "main" ]
11 |
12 | jobs:
13 | build:
14 |
15 | runs-on: ubuntu-latest
16 |
17 | steps:
18 | - uses: actions/checkout@v4
19 | - name: Setup .NET
20 | uses: actions/setup-dotnet@v4
21 | with:
22 | dotnet-version: 8.0.x
23 | - name: Restore dependencies
24 | run: dotnet restore
25 | - name: Build
26 | run: dotnet build --no-restore
27 | - name: Test
28 | run: dotnet test --no-build --verbosity normal
29 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## user-generated files
3 |
4 | # Build results
5 | [Bb]in/
6 | [Oo]bj/
7 | [Ll]og/
8 | TestResults/
9 | out/
10 | generated/
11 |
12 | # User-specific files
13 | *.user
14 | *.userosscache
15 | *.suo
16 | *.sln.docstates
17 | *.VC.db
18 | *.vscode/
19 | .vscode/
20 |
21 | # Rider
22 | .idea/
23 |
24 | # VS for Mac
25 | *.userprefs
26 |
27 | # Auto-generated files
28 | *.pidb
29 | *.svd
30 |
31 | # OS junk
32 | .DS_Store
33 | Thumbs.db
34 | ehthumbs.db
35 | Desktop.ini
36 |
37 | # ASP.NET Scaffolding
38 | ScaffoldingReadMe.txt
39 |
40 | # dotCover
41 | *.dotCover
42 |
43 | # NCrunch
44 | _NCrunch_*
45 | .*crunch*.local.xml
46 |
47 | # VS Code launch configs
48 | .vscode/launch.json
49 | .vscode/tasks.json
50 |
51 | # docker-compose override
52 | docker-compose.override.yml
53 |
54 | # dotenv / secrets
55 | .env
56 | *.env
57 |
58 | # local db
59 | *.mdf
60 | *.ldf
61 | *.sqlite
62 | *.db
63 | *.db-shm
64 | *.db-wal
65 |
66 | # Entity Framework
67 | Migrations/*Snapshot.cs
68 |
69 | # NuGet
70 | *.nupkg
71 | .nuget/
72 | packages/
73 | project.lock.json
74 | project.fragment.lock.json
75 | artifacts/
76 |
77 | # Global.json SDK resolver caches
78 | global.json.sdk.*
79 |
80 | # Visual Studio Code settings
81 | .vscode/*
82 |
83 | # ReSharper
84 | _ReSharper*/
85 | *.[Rr]e[Ss]harper
86 | *.DotSettings.user
87 |
88 | # dotnet watch
89 | dotnet-watch.json
90 |
91 | # Logs
92 | *.log
93 |
94 | # Tye
95 | .tye/
96 |
97 | # Coverage reports
98 | coverage/
99 | coverage.*.xml
100 | *.lcov
101 |
102 | # Reports
103 | reports/
104 |
105 | # Publish artifacts
106 | publish/
107 | dist/
108 |
109 | # Binaries
110 | *.dll
111 | *.exe
112 | *.app
113 | *.appx
114 | *.apk
115 | *.so
116 | *.dylib
117 | *.a
118 | *.lib
119 | *.pdb
120 |
121 | # Docker files
122 | docker-compose.override.yml
123 | docker-compose.*.yml
124 |
125 | # Terraform / infra junk
126 | .terraform/
127 | *.tfstate
128 | *.tfstate.backup
129 |
130 | # Local certs
131 | *.pfx
132 | *.cer
133 | *.key
134 |
135 | # IDE settings backups
136 | *.bak
137 | *.orig
138 |
139 | # Temporary files
140 | ~$*
141 | *.tmp
142 |
143 | # .vs folder
144 | .vs/
145 |
146 |
--------------------------------------------------------------------------------
/PRIVACYPOLICY.md:
--------------------------------------------------------------------------------
1 | # Privacy Policy of CouchBot
2 | *Last Updated: 10/23/2022*
3 |
4 | This document contains the privacy policy for CouchBot.
5 |
6 | **Owner Contact Information**
7 |
8 | Matthew Smith - me@mattthedev.codes
9 |
10 | **Types of Data Collected**
11 |
12 | * Discord Data
13 | * User, Channel, and Guild Ids
14 | * Platform Data
15 | * Platform Channel Id, Channel Name, and meta data required to announce new content.
16 | * Analytics for website
17 | * Google analytics is used for tracking website visits and geographic demographics of visitors.
18 |
19 | **Storage of Data**
20 |
21 | * Discord and Platform Data
22 | * CouchBot Database and data backups.
23 | * Analytics data
24 | * Handled by Google
25 |
26 | **Retention of Data**
27 |
28 | * Discord Channel and Guild Data
29 | * Stored for the duration of the user of bot. Upon removal, the bot cleans up this data - not archiving it - hard delete.
30 | * Platform Data
31 | * Platform Channel Ids often are used across various servers. This data is held to ensure proper announcement of creators across multiple guilds.
32 |
33 | **Collection Methods**
34 |
35 | * **Commands** - The following data may be collected and temporarily stored when intentionally provided by a User via usage of a function of a Bot, hence this data is not collected automatically. Should a User provide data in this manner, they forego any rights pertaining to the content of the data provided.
36 | * Server specific configuration settings
37 | * Data and content for the task called
38 | * **Automatically Collected** - When users join/leave a server, their Discord User Id and Username are collected for potential linking for CouchBot's Discovery service. This service allows anyone to link their Discord and Platform accounts for easy announcing.
39 | * **via Stripe** - On subscription creation, a customer ID is generated per user. This is linked to a users Discord User record in the CouchBot database.
40 |
41 | **Why is this data collected?**
42 |
43 | For bot functionality, betterment of the bot, and to facilitate tracking of payments to ensure the bot is allowed into servers where he's paid for.
44 |
45 | **Who is the data shared with?**
46 |
47 | Absolutely nobody. There is only 1 developer. I am him.
48 |
49 | **Requesting Your Data**
50 |
51 | No problem! Email me at me@mattthedev.codes OR DM me via Discord and the data will be provided within 30 days.
52 |
53 | **Agreement**
54 |
55 | By using the CouchBot service, you are in agreement with this document. If you wish to remove your data from any of the systems - don't hesitate to reach out!
56 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CouchBot
6 |
7 |
8 |
9 | Streaming Announcements, Role Hoisting, Cross-Platform.
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | Overview
31 | •
32 | Platforms Supported
33 | •
34 | Documentation
35 |
36 |
37 | # Overview
38 |
39 | Looking for CouchBot Documentation?
40 |
41 | [https://docs.couch.bot](https://docs.couch.bot)
42 |
43 |
44 | # Welcome to the Couch!
45 |
46 | Looking to engage your communities, let your Twitch, and YouTube viewers know when you've got new content, or know when your favorite creators have gone live? Look no further. CouchBot has been running for over 4 years, has gone through a few rewrites, and we're one of the best at doing what we do.
47 |
48 | CouchBot is monetized to ensure people that use the bot are willing to invest in its day to day, its growth, and its future.
49 |
50 | ## Want the Bot?!
51 |
52 | TBD
53 |
54 | #### How do I get my server id?
55 | Discord Resource: https://dis.gd/findmyid
56 |
57 | GIF Example:
58 |
59 | 
60 |
--------------------------------------------------------------------------------
/TERMSOFUSE.md:
--------------------------------------------------------------------------------
1 | # Terms of Use for CouchBot
2 | *Last Updated: 10/23/2022*
3 |
4 | This document contains the terms of use for CouchBot.
5 |
6 | **Owner Contact Information**
7 |
8 | Matthew Smith - me@mattthedev.codes
9 |
10 | These things can get super stuffy and formal - and while I should probably provide something long and drawn out ... I want to make sure the main bullet points are hit.
11 |
12 | 1. The bot can be removed from servers when the service is abused. Refunds will not be provided.
13 | 2. I, Matt Smith / CouchBot, am not responsible for the content delivered by CouchBot. If you have any concerns, please reach out to me at the email above.
14 |
15 | If you have any questions, comments, or feedback - don't hesitate to reach out.
16 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | services:
2 | db:
3 | image: postgres:16
4 | environment:
5 | POSTGRES_USER: myuser
6 | POSTGRES_PASSWORD: mypassword
7 | POSTGRES_DB: myappdb
8 | ports:
9 | - "5432:5432"
10 | volumes:
11 | - pgdata:/var/lib/postgresql/data
12 |
13 | api:
14 | build:
15 | context: .
16 | dockerfile: src/CB.Api/Dockerfile
17 | depends_on:
18 | - db
19 | environment:
20 | - ConnectionStrings__DefaultConnection=Host=db;Database=myappdb;Username=myuser;Password=mypassword
21 | - ASPNETCORE_ENVIRONMENT=Development
22 | ports:
23 | - "5000:8080"
24 |
25 | bot:
26 | build:
27 | context: .
28 | dockerfile: src/CB.Bot/Dockerfile
29 | depends_on:
30 | - db
31 | environment:
32 | - ConnectionStrings__DefaultConnection=Host=db;Database=myappdb;Username=myuser;Password=mypassword
33 | - ASPNETCORE_ENVIRONMENT=Development
34 | - BOT_TOKEN=BOT_TOKEN_HERE
35 |
36 | antenna:
37 | build:
38 | context: .
39 | dockerfile: src/CB.Antenna/Dockerfile
40 | depends_on:
41 | - db
42 | environment:
43 | - ConnectionStrings__DefaultConnection=Host=db;Database=myappdb;Username=myuser;Password=mypassword
44 | - ASPNETCORE_ENVIRONMENT=Development
45 |
46 | volumes:
47 | pgdata:
48 |
--------------------------------------------------------------------------------
/src/CB.Accessors/CB.Accessors.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/CB.Accessors/Contracts/IAllowConfigurationAccessor.cs:
--------------------------------------------------------------------------------
1 | using CB.Data.Entities;
2 | using CB.Shared.Dtos;
3 |
4 | namespace CB.Accessors.Contracts;
5 |
6 | public interface IAllowConfigurationAccessor
7 | {
8 | Task> GetAllAsync();
9 |
10 | Task GetByIdAsync(string id);
11 |
12 | Task CreateAsync(AllowConfiguration entity);
13 |
14 | Task UpdateAsync(AllowConfigurationDto entity);
15 |
16 | Task DeleteAsync(string id);
17 | }
--------------------------------------------------------------------------------
/src/CB.Accessors/Contracts/IChannelAccessor.cs:
--------------------------------------------------------------------------------
1 | using CB.Data.Entities;
2 | using CB.Shared.Dtos;
3 |
4 | namespace CB.Accessors.Contracts;
5 |
6 | public interface IChannelAccessor
7 | {
8 | Task> GetAllAsync();
9 |
10 | Task GetByIdAsync(string id);
11 |
12 | Task GetChannelConfigurationSummaryByIdAsync(string id);
13 |
14 | Task CreateAsync(Channel entity);
15 |
16 | Task UpdateAsync(ChannelDto entity);
17 |
18 | Task DeleteAsync(string id);
19 | }
--------------------------------------------------------------------------------
/src/CB.Accessors/Contracts/IChannelConfigurationAccessor.cs:
--------------------------------------------------------------------------------
1 | using CB.Data.Entities;
2 | using CB.Shared.Dtos;
3 |
4 | namespace CB.Accessors.Contracts;
5 |
6 | public interface IChannelConfigurationAccessor
7 | {
8 | Task> GetAllAsync();
9 |
10 | Task GetByIdAsync(string id);
11 |
12 | Task CreateAsync(ChannelConfiguration entity);
13 |
14 | Task UpdateAsync(string id,
15 | ChannelConfigurationDto dto);
16 |
17 | Task DeleteAsync(string id);
18 | }
--------------------------------------------------------------------------------
/src/CB.Accessors/Contracts/IClipEmbedAccessor.cs:
--------------------------------------------------------------------------------
1 | using CB.Shared.Dtos;
2 |
3 | namespace CB.Accessors.Contracts;
4 |
5 | public interface IClipEmbedAccessor
6 | {
7 | Task GetByIdAsync(string guildId);
8 |
9 | Task UpdateAsync(ClipEmbedDto entity);
10 | }
--------------------------------------------------------------------------------
/src/CB.Accessors/Contracts/ICreatorAccessor.cs:
--------------------------------------------------------------------------------
1 | using CB.Data.Entities;
2 | using CB.Shared.Dtos;
3 | using CB.Shared.Enums;
4 | using Platform = CB.Shared.Enums.Platform;
5 |
6 | namespace CB.Accessors.Contracts;
7 |
8 | public interface ICreatorAccessor
9 | {
10 | Task> GetAllAsync();
11 |
12 | Task GetByIdAsync(long id);
13 |
14 | Task GetByChannelIdAndPlatformAsync(string channelId, Platform platform);
15 |
16 | Task CreateAsync(Creator entity);
17 |
18 | Task UpdateAsync(string id,
19 | Creator entity);
20 |
21 | Task DeleteAsync(string id);
22 | }
--------------------------------------------------------------------------------
/src/CB.Accessors/Contracts/ICreatorChannelAccessor.cs:
--------------------------------------------------------------------------------
1 | using CB.Data.Entities;
2 | using CB.Shared.Dtos;
3 |
4 | // ReSharper disable UnusedMember.Global
5 |
6 | namespace CB.Accessors.Contracts;
7 |
8 | public interface ICreatorChannelAccessor
9 | {
10 | Task> GetAllAsync();
11 |
12 | Task GetAsync(long creatorId,
13 | string channelId,
14 | int channelTypeId);
15 |
16 | Task CreateAsync(CreatorChannel entity);
17 |
18 | Task UpdateAsync(string id,
19 | CreatorChannelDto entity);
20 |
21 | Task DeleteAsync(long creatorId,
22 | string channelId,
23 | int channelTypeId);
24 | }
--------------------------------------------------------------------------------
/src/CB.Accessors/Contracts/IDLiveAccessor.cs:
--------------------------------------------------------------------------------
1 | using CB.Shared.Models.DLive;
2 |
3 | namespace CB.Accessors.Contracts;
4 |
5 | public interface IDLiveAccessor
6 | {
7 | Task GetUserByDisplayNameAsync(string displayName);
8 |
9 | Task GetUserByUsernameAsync(string username);
10 | }
--------------------------------------------------------------------------------
/src/CB.Accessors/Contracts/IDiscordLiveConfigurationAccessor.cs:
--------------------------------------------------------------------------------
1 | using CB.Data.Entities;
2 | using CB.Shared.Dtos;
3 |
4 | namespace CB.Accessors.Contracts;
5 |
6 | public interface IDiscordLiveConfigurationAccessor
7 | {
8 | Task CreateAsync(DiscordLiveConfiguration entity);
9 | Task GetByIdAsync(string guildId);
10 | Task UpdateAsync(DiscordLiveConfigurationDto updated);
11 | }
--------------------------------------------------------------------------------
/src/CB.Accessors/Contracts/IDropdownPayloadAccessor.cs:
--------------------------------------------------------------------------------
1 | using CB.Data.Entities;
2 | using CB.Shared.Dtos;
3 |
4 | namespace CB.Accessors.Contracts;
5 |
6 | public interface IDropdownPayloadAccessor
7 | {
8 | Task> GetAllAsync();
9 |
10 | Task GetByIdAsync(int id);
11 |
12 | Task CreateAsync(DropdownPayload entity);
13 |
14 | //Task UpdateAsync(string id,
15 | // DropdownPayload entity);
16 |
17 | Task DeleteAsync(string id);
18 | }
--------------------------------------------------------------------------------
/src/CB.Accessors/Contracts/IFilterAccessor.cs:
--------------------------------------------------------------------------------
1 | using CB.Data.Entities;
2 | using CB.Shared.Dtos;
3 |
4 | namespace CB.Accessors.Contracts;
5 |
6 | public interface IFilterAccessor
7 | {
8 | Task CreateAsync(Filter entity);
9 |
10 | Task> GetAllAsync();
11 |
12 | Task> GetAllAsync(string guildId);
13 |
14 | Task DeleteAsync(FilterDto filter);
15 | }
--------------------------------------------------------------------------------
/src/CB.Accessors/Contracts/IFunAccessor.cs:
--------------------------------------------------------------------------------
1 | namespace CB.Accessors.Contracts;
2 |
3 | public interface IFunAccessor
4 | {
5 | Task IncrementHaiBai();
6 | }
--------------------------------------------------------------------------------
/src/CB.Accessors/Contracts/IGuildAccessor.cs:
--------------------------------------------------------------------------------
1 | using CB.Data.Entities;
2 | using CB.Shared.Dtos;
3 |
4 | namespace CB.Accessors.Contracts;
5 |
6 | public interface IGuildAccessor
7 | {
8 | Task> GetAllAsync();
9 |
10 | Task GetByIdAsync(string id);
11 |
12 | Task GetConfigurationSummaryByIdAsync(string id);
13 |
14 | Task CreateAsync(Guild entity);
15 |
16 | Task UpdateAsync(string id,
17 | Guild entity);
18 |
19 | Task DeleteAsync(string id);
20 | }
--------------------------------------------------------------------------------
/src/CB.Accessors/Contracts/IGuildConfigurationAccessor.cs:
--------------------------------------------------------------------------------
1 | using CB.Data.Entities;
2 | using CB.Shared.Dtos;
3 |
4 | namespace CB.Accessors.Contracts;
5 |
6 | public interface IGuildConfigurationAccessor
7 | {
8 | Task> GetAllAsync();
9 |
10 | Task GetByIdAsync(string id);
11 |
12 | Task CreateAsync(GuildConfiguration entity);
13 |
14 | Task UpdateAsync(string id,
15 | GuildConfigurationDto entity);
16 |
17 | Task DeleteAsync(string id);
18 | }
--------------------------------------------------------------------------------
/src/CB.Accessors/Contracts/ILiveEmbedAccessor.cs:
--------------------------------------------------------------------------------
1 | using CB.Shared.Dtos;
2 |
3 | namespace CB.Accessors.Contracts;
4 |
5 | public interface ILiveEmbedAccessor
6 | {
7 | Task GetByIdAsync(string guildId);
8 |
9 | Task UpdateAsync(LiveEmbedDto entity);
10 | }
--------------------------------------------------------------------------------
/src/CB.Accessors/Contracts/IMessageConfigurationAccessor.cs:
--------------------------------------------------------------------------------
1 | using CB.Shared.Dtos;
2 |
3 | namespace CB.Accessors.Contracts;
4 |
5 | public interface IMessageConfigurationAccessor
6 | {
7 | Task UpdateAsync(MessageConfigurationDto updated);
8 | }
--------------------------------------------------------------------------------
/src/CB.Accessors/Contracts/IPicartoAccessor.cs:
--------------------------------------------------------------------------------
1 | using CB.Shared.Models.Picarto;
2 |
3 | namespace CB.Accessors.Contracts;
4 |
5 | public interface IPicartoAccessor
6 | {
7 | Task RetrieveAsync(string accessToken);
8 |
9 | Task GetChannelByNameAsync(string name);
10 | }
--------------------------------------------------------------------------------
/src/CB.Accessors/Contracts/IPiczelAccessor.cs:
--------------------------------------------------------------------------------
1 | using CB.Shared.Models.Piczel;
2 |
3 | namespace CB.Accessors.Contracts;
4 |
5 | public interface IPiczelAccessor
6 | {
7 | Task GetChannelByNameAsync(string name);
8 | }
--------------------------------------------------------------------------------
/src/CB.Accessors/Contracts/IRoleConfigurationAccessor.cs:
--------------------------------------------------------------------------------
1 | using CB.Data.Entities;
2 | using CB.Shared.Dtos;
3 |
4 | namespace CB.Accessors.Contracts;
5 |
6 | public interface IRoleConfigurationAccessor
7 | {
8 | Task> GetAllAsync();
9 |
10 | Task GetByIdAsync(string id);
11 |
12 | Task CreateAsync(RoleConfiguration entity);
13 |
14 | Task UpdateAsync(RoleConfigurationDto entity);
15 |
16 | Task DeleteAsync(string id);
17 | }
--------------------------------------------------------------------------------
/src/CB.Accessors/Contracts/ITrovoAccessor.cs:
--------------------------------------------------------------------------------
1 | using CB.Shared.Models.Trovo;
2 |
3 | namespace CB.Accessors.Contracts;
4 |
5 | public interface ITrovoAccessor
6 | {
7 | Task GetUserByNameAsync(string name);
8 |
9 | Task GetChannelByIdAsync(int id);
10 | }
--------------------------------------------------------------------------------
/src/CB.Accessors/Contracts/ITwitchAccessor.cs:
--------------------------------------------------------------------------------
1 | using CB.Shared.Models.Twitch;
2 | using CouchBot.Domain.Models.Twitch;
3 |
4 | namespace CB.Accessors.Contracts;
5 |
6 | public interface ITwitchAccessor
7 | {
8 | Task GetStreamsAsync(string twitchIdList);
9 |
10 | Task GetUsersAsync(string twitchIdList);
11 |
12 | Task GetTwitchTeamByNameAsync(string name);
13 |
14 | Task GetStreamsByGameNameAsync(string gameName);
15 |
16 | Task SearchForGameByNameAsync(string gameName);
17 |
18 | Task GetClipsAsync(string channelId);
19 | }
--------------------------------------------------------------------------------
/src/CB.Accessors/Contracts/IUserAccessor.cs:
--------------------------------------------------------------------------------
1 | using CB.Data.Entities;
2 | using CB.Shared.Dtos;
3 |
4 | namespace CB.Accessors.Contracts;
5 |
6 | public interface IUserAccessor
7 | {
8 | Task> GetAllAsync();
9 |
10 | Task GetByIdAsync(string id);
11 |
12 | Task CreateAsync(User entity);
13 |
14 | Task UpdateAsync(string id,
15 | User entity);
16 |
17 | Task DeleteAsync(string id);
18 | }
--------------------------------------------------------------------------------
/src/CB.Accessors/Contracts/IVodEmbedAccessor.cs:
--------------------------------------------------------------------------------
1 | using CB.Shared.Dtos;
2 |
3 | namespace CB.Accessors.Contracts;
4 |
5 | public interface IVodEmbedAccessor
6 | {
7 | Task GetByIdAsync(string guildId);
8 |
9 | Task UpdateAsync(VodEmbedDto entity);
10 | }
--------------------------------------------------------------------------------
/src/CB.Accessors/Contracts/IYouTubeAccessor.cs:
--------------------------------------------------------------------------------
1 | using CB.Shared.Models.YouTube;
2 |
3 | namespace CB.Accessors.Contracts;
4 |
5 | public interface IYouTubeAccessor
6 | {
7 | Task GetVideoByIdAsync(string id);
8 |
9 | Task GetChannelStatisticsByIdAsync(string id);
10 |
11 | Task GetPlaylistItemsByPlaylistIdAsync(string playlistId);
12 |
13 | Task GetYouTubeChannelByQueryAsync(string name);
14 |
15 | Task GetYouTubeChannelByIdAsync(string channelId);
16 |
17 | Task GetLiveChannelsAsync(string channelIds);
18 |
19 | Task GetVideoDetailsAsync(string videoId);
20 |
21 | Task> GetYouTubeChannelsByIdsAsync(string channelIds);
22 |
23 | Task GetYouTubeChannelByUsernameAsync(string username);
24 |
25 | Task IsYouTubeShortAsync(string videoId);
26 | }
--------------------------------------------------------------------------------
/src/CB.Accessors/Implementations/AllowConfigurationAccessor.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using AutoMapper.QueryableExtensions;
3 | using CB.Accessors.Contracts;
4 | using CB.Data;
5 | using CB.Data.Entities;
6 | using CB.Shared.Dtos;
7 | using Microsoft.EntityFrameworkCore;
8 |
9 | namespace CB.Accessors.Implementations;
10 |
11 | public class AllowConfigurationAccessor(CbContext context,
12 | IMapper mapper)
13 | : IAllowConfigurationAccessor
14 | {
15 | public Task> GetAllAsync() => context
16 | .AllowConfigurations
17 | .AsNoTracking()
18 | .ProjectTo(mapper.ConfigurationProvider)
19 | .ToListAsync();
20 |
21 | public Task GetByIdAsync(string id) => context.AllowConfigurations
22 | .AsNoTracking()
23 | .Where(g => g.GuildId == id)
24 | .ProjectTo(mapper.ConfigurationProvider)
25 | .FirstOrDefaultAsync();
26 |
27 | public async Task CreateAsync(AllowConfiguration entity)
28 | {
29 | context.AllowConfigurations.Add(entity);
30 | await context
31 | .SaveChangesAsync()
32 | .ConfigureAwait(false);
33 |
34 | return mapper.Map(entity);
35 | }
36 |
37 | public async Task UpdateAsync(AllowConfigurationDto updated)
38 | {
39 | var allowConfiguration = await context
40 | .AllowConfigurations
41 | .FirstOrDefaultAsync( x => x.GuildId == updated.GuildId)
42 | .ConfigureAwait(false);
43 |
44 | if (allowConfiguration == null)
45 | {
46 | return null;
47 | }
48 |
49 | allowConfiguration.AllowCrosspost = updated.AllowCrosspost;
50 | allowConfiguration.AllowDiscordLive = updated.AllowDiscordLive;
51 | allowConfiguration.AllowFfa = updated.AllowFfa;
52 | allowConfiguration.AllowGoodbyes = updated.AllowGoodbyes;
53 | allowConfiguration.AllowGreetings = updated.AllowGreetings;
54 | allowConfiguration.AllowLive = updated.AllowLive;
55 | allowConfiguration.AllowLiveDiscovery = updated.AllowLiveDiscovery;
56 | allowConfiguration.AllowPublished = updated.AllowPublished;
57 | allowConfiguration.AllowStreamVod = updated.AllowStreamVod;
58 | allowConfiguration.AllowThumbnails = updated.AllowThumbnails;
59 |
60 | await context
61 | .SaveChangesAsync()
62 | .ConfigureAwait(false);
63 |
64 | return mapper.Map(allowConfiguration);
65 | }
66 |
67 | public async Task DeleteAsync(string id)
68 | {
69 | var allowConfiguration = await context
70 | .AllowConfigurations
71 | .FindAsync(id)
72 | .ConfigureAwait(false);
73 |
74 | if (allowConfiguration == null)
75 | {
76 | return false;
77 | }
78 |
79 | context.AllowConfigurations.Remove(allowConfiguration);
80 | await context
81 | .SaveChangesAsync()
82 | .ConfigureAwait(false);
83 |
84 | return true;
85 | }
86 | }
--------------------------------------------------------------------------------
/src/CB.Accessors/Implementations/ChannelAccessor.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using AutoMapper.QueryableExtensions;
3 | using CB.Accessors.Contracts;
4 | using CB.Data;
5 | using CB.Data.Entities;
6 | using CB.Shared.Dtos;
7 | using Microsoft.EntityFrameworkCore;
8 |
9 | namespace CB.Accessors.Implementations;
10 |
11 | public class ChannelAccessor(CbContext context,
12 | IMapper mapper)
13 | : IChannelAccessor
14 | {
15 | public Task> GetAllAsync() => context
16 | .Channels
17 | .AsNoTracking()
18 | .ProjectTo(mapper.ConfigurationProvider)
19 | .ToListAsync();
20 |
21 | public Task GetByIdAsync(string id) => context.Channels
22 | .AsNoTracking()
23 | .Where(g => g.Id == id)
24 | .ProjectTo(mapper.ConfigurationProvider)
25 | .FirstOrDefaultAsync();
26 |
27 | public Task GetChannelConfigurationSummaryByIdAsync(string id) => context.Channels
28 | .AsNoTracking()
29 | .Where(g => g.Id == id)
30 | .ProjectTo(mapper.ConfigurationProvider)
31 | .FirstOrDefaultAsync();
32 |
33 | public async Task CreateAsync(Channel entity)
34 | {
35 | entity.CreatedDate = DateTime.UtcNow;
36 | entity.ModifiedDate = DateTime.UtcNow;
37 |
38 | context.Channels.Add(entity);
39 | await context
40 | .SaveChangesAsync()
41 | .ConfigureAwait(false);
42 |
43 | return mapper.Map(entity);
44 | }
45 |
46 | public async Task UpdateAsync(ChannelDto updated)
47 | {
48 | var channel = await context
49 | .Channels
50 | .FirstOrDefaultAsync(x => x.Id == updated.Id)
51 | .ConfigureAwait(false);
52 |
53 | if (channel == null)
54 | {
55 | return null;
56 | }
57 |
58 | channel.DisplayName = updated.DisplayName;
59 | channel.ModifiedDate = DateTime.UtcNow;
60 |
61 | await context
62 | .SaveChangesAsync()
63 | .ConfigureAwait(false);
64 |
65 | return mapper.Map(channel);
66 | }
67 |
68 | public async Task DeleteAsync(string id)
69 | {
70 | var channel = await context
71 | .Channels
72 | .FindAsync(id)
73 | .ConfigureAwait(false);
74 |
75 | if (channel == null)
76 | {
77 | return false;
78 | }
79 |
80 | context.Channels.Remove(channel);
81 | await context
82 | .SaveChangesAsync()
83 | .ConfigureAwait(false);
84 |
85 | return true;
86 | }
87 | }
--------------------------------------------------------------------------------
/src/CB.Accessors/Implementations/ChannelConfigurationAccessor.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using AutoMapper.QueryableExtensions;
3 | using CB.Accessors.Contracts;
4 | using CB.Data;
5 | using CB.Data.Entities;
6 | using CB.Shared.Dtos;
7 | using Microsoft.EntityFrameworkCore;
8 |
9 | namespace CB.Accessors.Implementations;
10 |
11 | public class ChannelConfigurationAccessor(CbContext context,
12 | IMapper mapper)
13 | : IChannelConfigurationAccessor
14 | {
15 | public Task> GetAllAsync() => context
16 | .ChannelConfigurations
17 | .AsNoTracking()
18 | .ProjectTo(mapper.ConfigurationProvider)
19 | .ToListAsync();
20 |
21 | public Task GetByIdAsync(string id) => context.ChannelConfigurations
22 | .AsNoTracking()
23 | .Where(g => g.GuildId == id)
24 | .ProjectTo(mapper.ConfigurationProvider)
25 | .FirstOrDefaultAsync();
26 |
27 | public async Task CreateAsync(ChannelConfiguration entity)
28 | {
29 | context.ChannelConfigurations.Add(entity);
30 | await context
31 | .SaveChangesAsync()
32 | .ConfigureAwait(false);
33 |
34 | return mapper.Map(entity);
35 | }
36 |
37 | public async Task UpdateAsync(string id,
38 | ChannelConfigurationDto dto)
39 | {
40 | var channelConfiguration = await context
41 | .ChannelConfigurations
42 | .FindAsync(id)
43 | .ConfigureAwait(false);
44 |
45 | if (channelConfiguration == null)
46 | {
47 | return null;
48 | }
49 |
50 | channelConfiguration.GreetingChannelId = dto.GreetingChannelId;
51 | channelConfiguration.GoodbyeChannelId = dto.GoodbyeChannelId;
52 | channelConfiguration.LiveChannelId = dto.LiveChannelId;
53 | channelConfiguration.DiscordLiveChannelId = dto.DiscordLiveChannelId;
54 |
55 | await context
56 | .SaveChangesAsync()
57 | .ConfigureAwait(false);
58 |
59 | return mapper.Map(channelConfiguration);
60 | }
61 |
62 | public async Task DeleteAsync(string id)
63 | {
64 | var channelConfiguration = await context
65 | .ChannelConfigurations
66 | .FindAsync(id)
67 | .ConfigureAwait(false);
68 |
69 | if (channelConfiguration == null)
70 | {
71 | return false;
72 | }
73 |
74 | context.ChannelConfigurations.Remove(channelConfiguration);
75 | await context
76 | .SaveChangesAsync()
77 | .ConfigureAwait(false);
78 |
79 | return true;
80 | }
81 | }
--------------------------------------------------------------------------------
/src/CB.Accessors/Implementations/ClipEmbedAccessor.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using CB.Accessors.Contracts;
3 | using CB.Data;
4 | using CB.Shared.Dtos;
5 | using Microsoft.EntityFrameworkCore;
6 |
7 | namespace CB.Accessors.Implementations;
8 |
9 | public class ClipEmbedAccessor(CbContext context,
10 | IMapper mapper)
11 | : IClipEmbedAccessor
12 | {
13 | public async Task GetByIdAsync(string guildId)
14 | {
15 | var entity = await context
16 | .ClipEmbeds
17 | .FirstOrDefaultAsync(x => x.GuildId == guildId);
18 |
19 | if (entity == null)
20 | {
21 | entity = new()
22 | {
23 | GuildId = guildId,
24 | Header = "New Clip @ %CHANNEL%",
25 | Description = "%DESCRIPTION%",
26 | Footer = "Powered by 100% Couch • Created by %CHANNEL%",
27 | WatchButton = "Watch this Clip!",
28 | MoreButton = "And More!",
29 | };
30 |
31 | await context
32 | .ClipEmbeds
33 | .AddAsync(entity);
34 | await context
35 | .SaveChangesAsync();
36 | }
37 |
38 | return mapper.Map(entity);
39 | }
40 |
41 | public async Task UpdateAsync(ClipEmbedDto entity)
42 | {
43 | var embed = await context
44 | .ClipEmbeds
45 | .FirstOrDefaultAsync(x => x.GuildId == entity.GuildId)
46 | .ConfigureAwait(false);
47 |
48 | if (embed == null)
49 | {
50 | return null;
51 | }
52 |
53 | embed.Description = entity.Description;
54 | embed.Footer = entity.Description;
55 | embed.Header = entity.Description;
56 | embed.MoreButton = entity.Description;
57 | embed.WatchButton = entity.WatchButton;
58 |
59 | await context
60 | .SaveChangesAsync()
61 | .ConfigureAwait(false);
62 |
63 | return mapper.Map(embed);
64 | }
65 | }
--------------------------------------------------------------------------------
/src/CB.Accessors/Implementations/CreatorAccessor.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using AutoMapper.QueryableExtensions;
3 | using CB.Accessors.Contracts;
4 | using CB.Data;
5 | using CB.Data.Entities;
6 | using CB.Shared.Dtos;
7 | using CB.Shared.Enums;
8 | using Microsoft.EntityFrameworkCore;
9 | using Platform = CB.Shared.Enums.Platform;
10 |
11 | namespace CB.Accessors.Implementations;
12 |
13 | public class CreatorAccessor(CbContext context,
14 | IMapper mapper)
15 | : ICreatorAccessor
16 | {
17 | public Task> GetAllAsync() => context
18 | .Creators
19 | .AsNoTracking()
20 | .ProjectTo(mapper.ConfigurationProvider)
21 | .ToListAsync();
22 |
23 | public Task GetByIdAsync(long id) => context.Creators
24 | .AsNoTracking()
25 | .Where(g => g.Id == id)
26 | .ProjectTo(mapper.ConfigurationProvider)
27 | .FirstOrDefaultAsync();
28 |
29 | public Task GetByChannelIdAndPlatformAsync(string channelId, Platform platform) => context.Creators
30 | .AsNoTracking()
31 | .Where(g => g.ChannelId == channelId &&
32 | g.PlatformId == (int)platform)
33 | .ProjectTo(mapper.ConfigurationProvider)
34 | .FirstOrDefaultAsync();
35 |
36 | public async Task CreateAsync(Creator entity)
37 | {
38 | entity.CreatedDate = DateTime.UtcNow;
39 | entity.ModifiedDate = DateTime.UtcNow;
40 |
41 | context.Creators.Add(entity);
42 | await context
43 | .SaveChangesAsync()
44 | .ConfigureAwait(false);
45 |
46 | return mapper.Map(entity);
47 | }
48 |
49 | public async Task UpdateAsync(string id,
50 | Creator updated)
51 | {
52 | var creator = await context
53 | .Creators
54 | .FindAsync(id)
55 | .ConfigureAwait(false);
56 |
57 | if (creator == null)
58 | {
59 | return null;
60 | }
61 |
62 | creator.DisplayName = updated.DisplayName;
63 | creator.ModifiedDate = DateTime.UtcNow;
64 |
65 | await context
66 | .SaveChangesAsync()
67 | .ConfigureAwait(false);
68 |
69 | return mapper.Map(creator);
70 | }
71 |
72 | public async Task DeleteAsync(string id)
73 | {
74 | var creator = await context
75 | .Creators
76 | .FindAsync(id)
77 | .ConfigureAwait(false);
78 |
79 | if (creator == null)
80 | {
81 | return false;
82 | }
83 |
84 | context.Creators.Remove(creator);
85 | await context
86 | .SaveChangesAsync()
87 | .ConfigureAwait(false);
88 |
89 | return true;
90 | }
91 | }
--------------------------------------------------------------------------------
/src/CB.Accessors/Implementations/CreatorChannelAccessor.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using AutoMapper.QueryableExtensions;
3 | using CB.Accessors.Contracts;
4 | using CB.Data;
5 | using CB.Data.Entities;
6 | using CB.Shared.Dtos;
7 | using Microsoft.EntityFrameworkCore;
8 |
9 | namespace CB.Accessors.Implementations;
10 |
11 | public class CreatorChannelAccessor(CbContext context,
12 | IMapper mapper)
13 | : ICreatorChannelAccessor
14 | {
15 | public Task> GetAllAsync() => context
16 | .CreatorChannels
17 | .AsNoTracking()
18 | .ProjectTo(mapper.ConfigurationProvider)
19 | .ToListAsync();
20 |
21 | public Task GetAsync(long creatorId,
22 | string channelId,
23 | int channelTypeId) => context.CreatorChannels
24 | .AsNoTracking()
25 | .Where(g => g.CreatorId == creatorId &&
26 | g.ChannelId == channelId &&
27 | g.ChannelTypeId == channelTypeId)
28 | .ProjectTo(mapper.ConfigurationProvider)
29 | .FirstOrDefaultAsync();
30 |
31 | public async Task CreateAsync(CreatorChannel entity)
32 | {
33 | context.CreatorChannels.Add(entity);
34 | await context
35 | .SaveChangesAsync()
36 | .ConfigureAwait(false);
37 |
38 | return mapper.Map(entity);
39 | }
40 |
41 | public async Task UpdateAsync(string id,
42 | CreatorChannelDto updated)
43 | {
44 | var creatorChannel = await context
45 | .CreatorChannels
46 | .FindAsync(id)
47 | .ConfigureAwait(false);
48 |
49 | if (creatorChannel == null)
50 | {
51 | return null;
52 | }
53 |
54 | creatorChannel.CustomMessage = updated.CustomMessage;
55 | creatorChannel.ChannelTypeId = updated.ChannelTypeId;
56 |
57 | await context
58 | .SaveChangesAsync()
59 | .ConfigureAwait(false);
60 |
61 | return mapper.Map(creatorChannel);
62 | }
63 |
64 | public async Task DeleteAsync(long creatorId,
65 | string channelId,
66 | int channelTypeId)
67 | {
68 | var creatorChannel = await context
69 | .CreatorChannels
70 | .FirstOrDefaultAsync(x => x.CreatorId == creatorId &&
71 | x.ChannelId == channelId &&
72 | x.ChannelTypeId == channelTypeId)
73 | .ConfigureAwait(false);
74 |
75 | if (creatorChannel == null)
76 | {
77 | return false;
78 | }
79 |
80 | context.CreatorChannels.Remove(creatorChannel);
81 | await context
82 | .SaveChangesAsync()
83 | .ConfigureAwait(false);
84 |
85 | return true;
86 | }
87 | }
--------------------------------------------------------------------------------
/src/CB.Accessors/Implementations/DLiveAccessor.cs:
--------------------------------------------------------------------------------
1 | using CB.Accessors.Contracts;
2 | using CB.Shared.Models.DLive;
3 | using CB.Shared.Utilities;
4 | using Newtonsoft.Json;
5 |
6 | namespace CB.Accessors.Implementations;
7 |
8 | public class DLiveAccessor : IDLiveAccessor
9 | {
10 | private const string DLiveUrl = "https://graphigo.prd.dlive.tv/";
11 | private const string GetUserByDisplayNameQuery =
12 | "query {" +
13 | "userByDisplayName(displayname:\"%DISPLAYNAME%\") {" +
14 | " id" +
15 | " username" +
16 | " displayname" +
17 | "}" +
18 | "}";
19 |
20 | private const string GetUserByUsernameQuery =
21 | "query {" +
22 | "user(username:\"%USERNAME%\") { " +
23 | "displayname " +
24 | "avatar " +
25 | "followers {" +
26 | "totalCount " +
27 | "} " +
28 | "livestream { " +
29 | "thumbnailUrl " +
30 | "title " +
31 | "watchingCount " +
32 | "category " +
33 | "{ " +
34 | "title " +
35 | "} " +
36 | "} " +
37 | "} " +
38 | "}";
39 |
40 | public async Task GetUserByDisplayNameAsync(string displayName)
41 | {
42 | var query = GetUserByDisplayNameQuery.Replace("%DISPLAYNAME%", displayName);
43 |
44 | var dliveQuery = new DLiveQuery
45 | {
46 | Query = query,
47 | OperationName = null,
48 | Variables = null
49 | };
50 |
51 | return await ApiUtilities
52 | .PostApiHelper(DLiveUrl,
53 | payloadString: JsonConvert.SerializeObject(dliveQuery))
54 | .ConfigureAwait(false);
55 | }
56 |
57 | public async Task GetUserByUsernameAsync(string username)
58 | {
59 | var query = GetUserByUsernameQuery.Replace("%USERNAME%", username);
60 |
61 | var dliveQuery = new DLiveQuery
62 | {
63 | Query = query,
64 | OperationName = null,
65 | Variables = null
66 | };
67 |
68 | return await ApiUtilities
69 | .PostApiHelper(DLiveUrl,
70 | payloadString: JsonConvert.SerializeObject(dliveQuery))
71 | .ConfigureAwait(false);
72 | }
73 | }
74 |
75 | public class DLiveQuery
76 | {
77 | public string Query { get; set; }
78 |
79 | public object Variables { get; set; }
80 |
81 | public object OperationName { get; set; }
82 | }
--------------------------------------------------------------------------------
/src/CB.Accessors/Implementations/DiscordLiveConfigurationAccessor.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using AutoMapper.QueryableExtensions;
3 | using CB.Accessors.Contracts;
4 | using CB.Data;
5 | using CB.Data.Entities;
6 | using CB.Shared.Dtos;
7 | using Microsoft.EntityFrameworkCore;
8 |
9 | namespace CB.Accessors.Implementations;
10 |
11 | public class DiscordLiveConfigurationAccessor(CbContext context,
12 | IMapper mapper)
13 | : IDiscordLiveConfigurationAccessor
14 | {
15 | public async Task CreateAsync(DiscordLiveConfiguration entity)
16 | {
17 | context.DiscordLiveConfigurations.Add(entity);
18 | await context
19 | .SaveChangesAsync()
20 | .ConfigureAwait(false);
21 |
22 | return mapper.Map(entity);
23 | }
24 |
25 | public Task GetByIdAsync(string guildId) =>
26 | context.DiscordLiveConfigurations
27 | .AsNoTracking()
28 | .Where(g => g.GuildId == guildId)
29 | .ProjectTo(mapper.ConfigurationProvider)
30 | .FirstOrDefaultAsync();
31 |
32 |
33 | public async Task UpdateAsync(DiscordLiveConfigurationDto updated)
34 | {
35 | var entity = await context
36 | .DiscordLiveConfigurations
37 | .FirstOrDefaultAsync(x => x.GuildId == updated.GuildId)
38 | .ConfigureAwait(false);
39 |
40 | if (entity == null)
41 | {
42 | return null;
43 | }
44 |
45 | entity.Description = updated.Description;
46 | entity.Footer = updated.Footer;
47 | entity.Header = updated.Header;
48 | entity.MentionRoleId = updated.MentionRoleId;
49 | entity.Message = updated.Message;
50 |
51 | await context
52 | .SaveChangesAsync()
53 | .ConfigureAwait(false);
54 |
55 | return mapper.Map(entity);
56 | }
57 | }
--------------------------------------------------------------------------------
/src/CB.Accessors/Implementations/DropdownPayloadAccessor.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using AutoMapper.QueryableExtensions;
3 | using CB.Accessors.Contracts;
4 | using CB.Data;
5 | using CB.Data.Entities;
6 | using CB.Shared.Dtos;
7 | using Microsoft.EntityFrameworkCore;
8 |
9 | namespace CB.Accessors.Implementations;
10 |
11 | public class DropdownPayloadAccessor(CbContext context,
12 | IMapper mapper)
13 | : IDropdownPayloadAccessor
14 | {
15 | public Task> GetAllAsync() => context
16 | .DropdownPayloads
17 | .AsNoTracking()
18 | .ProjectTo(mapper.ConfigurationProvider)
19 | .ToListAsync();
20 |
21 | public Task GetByIdAsync(int id) => context.DropdownPayloads
22 | .AsNoTracking()
23 | .Where(g => g.Id == id)
24 | .ProjectTo(mapper.ConfigurationProvider)
25 | .FirstOrDefaultAsync();
26 |
27 | public async Task CreateAsync(DropdownPayload entity)
28 | {
29 | context.DropdownPayloads.Add(entity);
30 | await context
31 | .SaveChangesAsync()
32 | .ConfigureAwait(false);
33 |
34 | return mapper.Map(entity);
35 | }
36 |
37 | public async Task DeleteAsync(string id)
38 | {
39 | var dropdownPayload = await context
40 | .DropdownPayloads
41 | .FindAsync(id)
42 | .ConfigureAwait(false);
43 |
44 | if (dropdownPayload == null)
45 | {
46 | return false;
47 | }
48 |
49 | context.DropdownPayloads.Remove(dropdownPayload);
50 | await context
51 | .SaveChangesAsync()
52 | .ConfigureAwait(false);
53 |
54 | return true;
55 | }
56 | }
--------------------------------------------------------------------------------
/src/CB.Accessors/Implementations/FilterAccessor.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using AutoMapper.QueryableExtensions;
3 | using CB.Accessors.Contracts;
4 | using CB.Data;
5 | using CB.Data.Entities;
6 | using CB.Shared.Dtos;
7 | using Microsoft.EntityFrameworkCore;
8 |
9 | namespace CB.Accessors.Implementations;
10 |
11 | public class FilterAccessor(CbContext context,
12 | IMapper mapper) : IFilterAccessor
13 | {
14 | public async Task CreateAsync(Filter entity)
15 | {
16 | context.Filters.Add(entity);
17 | await context
18 | .SaveChangesAsync()
19 | .ConfigureAwait(false);
20 |
21 | return mapper.Map(entity);
22 | }
23 |
24 | public Task> GetAllAsync() =>
25 | context
26 | .Filters
27 | .AsNoTracking()
28 | .ProjectTo(mapper.ConfigurationProvider)
29 | .ToListAsync();
30 |
31 | public Task> GetAllAsync(string guildId) =>
32 | context
33 | .Filters
34 | .AsNoTracking()
35 | .Where(x => x.GuildId == guildId)
36 | .ProjectTo(mapper.ConfigurationProvider)
37 | .ToListAsync();
38 |
39 | public async Task DeleteAsync(FilterDto filter)
40 | {
41 | var entity = await context
42 | .Filters
43 | .FirstOrDefaultAsync(x => x.Id == filter.Id)
44 | .ConfigureAwait(false);
45 |
46 | if (entity == null)
47 | {
48 | return false;
49 | }
50 |
51 | context.Filters.Remove(entity);
52 | await context
53 | .SaveChangesAsync()
54 | .ConfigureAwait(false);
55 |
56 | return true;
57 | }
58 | }
--------------------------------------------------------------------------------
/src/CB.Accessors/Implementations/FunAccessor.cs:
--------------------------------------------------------------------------------
1 | using CB.Accessors.Contracts;
2 | using CB.Data;
3 | using CB.Data.Entities;
4 | using Microsoft.EntityFrameworkCore;
5 |
6 | namespace CB.Accessors.Implementations;
7 |
8 | public class FunAccessor(CbContext context) : IFunAccessor
9 | {
10 | public async Task IncrementHaiBai()
11 | {
12 | var result = await context
13 | .FunStuff
14 | .FromSqlRaw("UPDATE \"FunStuff\" SET \"HaiBaiCount\" = \"HaiBaiCount\" + 1 RETURNING \"HaiBaiCount\"")
15 | .ToListAsync();
16 |
17 | return result.First().HaiBaiCount;
18 | }
19 | }
--------------------------------------------------------------------------------
/src/CB.Accessors/Implementations/GuildAccessor.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using AutoMapper.QueryableExtensions;
3 | using CB.Accessors.Contracts;
4 | using CB.Data;
5 | using CB.Data.Entities;
6 | using CB.Shared.Dtos;
7 | using Microsoft.EntityFrameworkCore;
8 |
9 | namespace CB.Accessors.Implementations;
10 |
11 | public class GuildAccessor(CbContext context,
12 | IMapper mapper)
13 | : IGuildAccessor
14 | {
15 | public Task> GetAllAsync() => context
16 | .Guilds
17 | .AsNoTracking()
18 | .ProjectTo(mapper.ConfigurationProvider)
19 | .ToListAsync();
20 |
21 | public Task GetByIdAsync(string id) => context.Guilds
22 | .AsNoTracking()
23 | .Where(g => g.Id == id)
24 | .ProjectTo(mapper.ConfigurationProvider)
25 | .FirstOrDefaultAsync();
26 |
27 | public Task GetConfigurationSummaryByIdAsync(string id) => context.Guilds
28 | .AsNoTracking()
29 | .Where(g => g.Id == id)
30 | .ProjectTo(mapper.ConfigurationProvider)
31 | .FirstOrDefaultAsync();
32 |
33 | public async Task CreateAsync(Guild entity)
34 | {
35 | entity.CreatedDate = DateTime.UtcNow;
36 | entity.ModifiedDate = DateTime.UtcNow;
37 |
38 | context.Guilds.Add(entity);
39 | await context
40 | .SaveChangesAsync()
41 | .ConfigureAwait(false);
42 |
43 | return mapper.Map(entity);
44 | }
45 |
46 | public async Task UpdateAsync(string id,
47 | Guild updated)
48 | {
49 | var guild = await context
50 | .Guilds
51 | .FindAsync(id)
52 | .ConfigureAwait(false);
53 |
54 | if (guild == null)
55 | {
56 | return null;
57 | }
58 |
59 | guild.DisplayName = updated.DisplayName;
60 | guild.OwnerId = updated.OwnerId;
61 | guild.ModifiedDate = DateTime.UtcNow;
62 |
63 | await context
64 | .SaveChangesAsync()
65 | .ConfigureAwait(false);
66 |
67 | return mapper.Map(guild);
68 | }
69 |
70 | public async Task DeleteAsync(string id)
71 | {
72 | var guild = await context
73 | .Guilds
74 | .FindAsync(id)
75 | .ConfigureAwait(false);
76 |
77 | if (guild == null)
78 | {
79 | return false;
80 | }
81 |
82 | context.Guilds.Remove(guild);
83 | await context
84 | .SaveChangesAsync()
85 | .ConfigureAwait(false);
86 |
87 | return true;
88 | }
89 | }
--------------------------------------------------------------------------------
/src/CB.Accessors/Implementations/GuildConfigurationAccessor.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using AutoMapper.QueryableExtensions;
3 | using CB.Accessors.Contracts;
4 | using CB.Data;
5 | using CB.Data.Entities;
6 | using CB.Shared.Dtos;
7 | using Microsoft.EntityFrameworkCore;
8 |
9 | namespace CB.Accessors.Implementations;
10 |
11 | public class GuildConfigurationAccessor(CbContext context,
12 | IMapper mapper)
13 | : IGuildConfigurationAccessor
14 | {
15 | public Task> GetAllAsync() => context
16 | .GuildConfigurations
17 | .AsNoTracking()
18 | .ProjectTo(mapper.ConfigurationProvider)
19 | .ToListAsync();
20 |
21 | public Task GetByIdAsync(string id) => context.GuildConfigurations
22 | .AsNoTracking()
23 | .Where(g => g.GuildId == id)
24 | .ProjectTo(mapper.ConfigurationProvider)
25 | .FirstOrDefaultAsync();
26 |
27 | public async Task CreateAsync(GuildConfiguration entity)
28 | {
29 | context.GuildConfigurations.Add(entity);
30 | await context
31 | .SaveChangesAsync()
32 | .ConfigureAwait(false);
33 |
34 | return mapper.Map(entity);
35 | }
36 |
37 | public async Task UpdateAsync(string id,
38 | GuildConfigurationDto updated)
39 | {
40 | var guildConfiguration = await context
41 | .GuildConfigurations
42 | .FindAsync(id)
43 | .ConfigureAwait(false);
44 |
45 | if (guildConfiguration == null)
46 | {
47 | return null;
48 | }
49 |
50 | guildConfiguration.DeleteOffline = updated.DeleteOffline;
51 | guildConfiguration.TextAnnouncements = updated.TextAnnouncements;
52 |
53 | await context
54 | .SaveChangesAsync()
55 | .ConfigureAwait(false);
56 |
57 | return mapper.Map(guildConfiguration);
58 | }
59 |
60 | public async Task DeleteAsync(string id)
61 | {
62 | var guildConfiguration = await context
63 | .GuildConfigurations
64 | .FindAsync(id)
65 | .ConfigureAwait(false);
66 |
67 | if (guildConfiguration == null)
68 | {
69 | return false;
70 | }
71 |
72 | context.GuildConfigurations.Remove(guildConfiguration);
73 | await context
74 | .SaveChangesAsync()
75 | .ConfigureAwait(false);
76 |
77 | return true;
78 | }
79 | }
--------------------------------------------------------------------------------
/src/CB.Accessors/Implementations/LiveEmbedAccessor.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using CB.Accessors.Contracts;
3 | using CB.Data;
4 | using CB.Shared.Dtos;
5 | using Microsoft.EntityFrameworkCore;
6 |
7 | namespace CB.Accessors.Implementations;
8 |
9 | public class LiveEmbedAccessor(CbContext context,
10 | IMapper mapper)
11 | : ILiveEmbedAccessor
12 | {
13 | public async Task GetByIdAsync(string guildId)
14 | {
15 | var entity = await context
16 | .LiveEmbeds
17 | .FirstOrDefaultAsync(x => x.GuildId == guildId);
18 |
19 | if (entity == null)
20 | {
21 | entity = new()
22 | {
23 | GuildId = guildId,
24 | Header = "%CHANNEL% is now streaming!",
25 | DescriptionLabel = "Stream Description",
26 | Description = "%CHANNEL% is currently playing %GAME%",
27 | LastStreamed = "Last Streamed",
28 | AverageStream = "Average Stream",
29 | StreamDescription = "%DESCRIPTION%",
30 | FooterStopped = "Powered by 100% Couch • Stopped streaming",
31 | FooterStart = "Powered by 100% Couch • Started streaming",
32 | ChannelButton = "Creator Channel",
33 | };
34 |
35 | await context
36 | .LiveEmbeds
37 | .AddAsync(entity);
38 | await context
39 | .SaveChangesAsync();
40 | }
41 |
42 | return mapper.Map(entity);
43 | }
44 |
45 | public async Task UpdateAsync(LiveEmbedDto entity)
46 | {
47 | var embed = await context
48 | .LiveEmbeds
49 | .FirstOrDefaultAsync(x => x.GuildId == entity.GuildId)
50 | .ConfigureAwait(false);
51 |
52 | if (embed == null)
53 | {
54 | return null;
55 | }
56 |
57 | embed.AverageStream = entity.AverageStream;
58 | embed.ChannelButton = entity.ChannelButton;
59 | embed.Description = entity.Description;
60 | embed.DescriptionLabel = entity.DescriptionLabel;
61 | embed.FooterStart = entity.FooterStart;
62 | embed.FooterStopped = entity.FooterStopped;
63 | embed.Header = entity.Header;
64 | embed.LastStreamed = entity.LastStreamed;
65 | embed.StreamDescription = entity.StreamDescription;
66 |
67 | await context
68 | .SaveChangesAsync()
69 | .ConfigureAwait(false);
70 |
71 | return mapper.Map(embed);
72 | }
73 | }
--------------------------------------------------------------------------------
/src/CB.Accessors/Implementations/MessageConfigurationAccessor.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using CB.Accessors.Contracts;
3 | using CB.Data;
4 | using CB.Shared.Dtos;
5 | using Microsoft.EntityFrameworkCore;
6 |
7 | namespace CB.Accessors.Implementations;
8 |
9 | public class MessageConfigurationAccessor(CbContext context,
10 | IMapper mapper)
11 | : IMessageConfigurationAccessor
12 | {
13 | public async Task UpdateAsync(MessageConfigurationDto updated)
14 | {
15 | var messageConfiguration = await context
16 | .MessageConfigurations
17 | .FirstOrDefaultAsync(x => x.GuildId == updated.GuildId)
18 | .ConfigureAwait(false);
19 |
20 | if (messageConfiguration == null)
21 | {
22 | return null;
23 | }
24 |
25 | messageConfiguration.GoodbyeMessage = updated.GoodbyeMessage;
26 | messageConfiguration.GreetingMessage = updated.GreetingMessage;
27 | messageConfiguration.LiveMessage = updated.LiveMessage;
28 | messageConfiguration.PublishedMessage = updated.PublishedMessage;
29 | messageConfiguration.StreamOfflineMessage = updated.StreamOfflineMessage;
30 |
31 | await context
32 | .SaveChangesAsync()
33 | .ConfigureAwait(false);
34 |
35 | return mapper.Map(messageConfiguration);
36 | }
37 | }
--------------------------------------------------------------------------------
/src/CB.Accessors/Implementations/PicartoAccessor.cs:
--------------------------------------------------------------------------------
1 | using CB.Accessors.Contracts;
2 | using CB.Shared.Models.Picarto;
3 | using CB.Shared.Utilities;
4 |
5 | namespace CB.Accessors.Implementations;
6 |
7 | public class PicartoAccessor : IPicartoAccessor
8 | {
9 | public async Task RetrieveAsync(string accessToken)
10 | {
11 | return await ApiUtilities
12 | .ApiHelper("https://api.picarto.tv/api/v1/user",
13 | [("Authorization", $"Bearer {accessToken}"),
14 | ("Content-Type", "application/json"),
15 | ("User-Agent", "PostmanRuntime/7.26.8")])
16 | .ConfigureAwait(false);
17 | }
18 |
19 | public async Task GetChannelByNameAsync(string name)
20 | {
21 | return await ApiUtilities.ApiHelper($"https://ptvintern.picarto.tv/api/channel/detail/{name}",
22 | [("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0 - CouchBot - me@mattthedev.codes"),
23 | ("Accept", "*/*")])
24 | .ConfigureAwait(false);
25 | }
26 | }
--------------------------------------------------------------------------------
/src/CB.Accessors/Implementations/PiczelAccessor.cs:
--------------------------------------------------------------------------------
1 | using CB.Accessors.Contracts;
2 | using CB.Shared.Models.Piczel;
3 | using CB.Shared.Utilities;
4 |
5 | namespace CB.Accessors.Implementations;
6 |
7 | public class PiczelAccessor : IPiczelAccessor
8 | {
9 | public async Task GetChannelByNameAsync(string channelName)
10 | {
11 | return await ApiUtilities.ApiHelper($"https://piczel.tv/api/streams/{channelName}");
12 | }
13 | }
--------------------------------------------------------------------------------
/src/CB.Accessors/Implementations/RoleConfigurationAccessor.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using AutoMapper.QueryableExtensions;
3 | using CB.Accessors.Contracts;
4 | using CB.Data;
5 | using CB.Data.Entities;
6 | using CB.Shared.Dtos;
7 | using Microsoft.EntityFrameworkCore;
8 |
9 | namespace CB.Accessors.Implementations;
10 |
11 | public class RoleConfigurationAccessor(CbContext context,
12 | IMapper mapper)
13 | : IRoleConfigurationAccessor
14 | {
15 | public Task> GetAllAsync() => context
16 | .RoleConfigurations
17 | .AsNoTracking()
18 | .ProjectTo(mapper.ConfigurationProvider)
19 | .ToListAsync();
20 |
21 | public Task GetByIdAsync(string id) => context.RoleConfigurations
22 | .AsNoTracking()
23 | .Where(g => g.GuildId == id)
24 | .ProjectTo(mapper.ConfigurationProvider)
25 | .FirstOrDefaultAsync();
26 |
27 | public async Task CreateAsync(RoleConfiguration entity)
28 | {
29 | context.RoleConfigurations.Add(entity);
30 | await context
31 | .SaveChangesAsync()
32 | .ConfigureAwait(false);
33 |
34 | return mapper.Map(entity);
35 | }
36 |
37 | public async Task UpdateAsync(RoleConfigurationDto updated)
38 | {
39 | var roleConfiguration = await context
40 | .RoleConfigurations
41 | .FirstOrDefaultAsync(x => x.GuildId == updated.GuildId)
42 | .ConfigureAwait(false);
43 |
44 | if (roleConfiguration == null)
45 | {
46 | return null;
47 | }
48 |
49 | roleConfiguration.DiscoveryRoleId = updated.DiscoveryRoleId;
50 | roleConfiguration.JoinRoleId = updated.JoinRoleId;
51 | roleConfiguration.LiveDiscoveryRoleId = updated.LiveDiscoveryRoleId;
52 |
53 | await context
54 | .SaveChangesAsync()
55 | .ConfigureAwait(false);
56 |
57 | return mapper.Map(roleConfiguration);
58 | }
59 |
60 | public async Task DeleteAsync(string id)
61 | {
62 | var roleConfiguration = await context
63 | .RoleConfigurations
64 | .FindAsync(id)
65 | .ConfigureAwait(false);
66 |
67 | if (roleConfiguration == null)
68 | {
69 | return false;
70 | }
71 |
72 | context.RoleConfigurations.Remove(roleConfiguration);
73 | await context
74 | .SaveChangesAsync()
75 | .ConfigureAwait(false);
76 |
77 | return true;
78 | }
79 | }
--------------------------------------------------------------------------------
/src/CB.Accessors/Implementations/TrovoAccessor.cs:
--------------------------------------------------------------------------------
1 | using CB.Accessors.Contracts;
2 | using CB.Shared.Models.Trovo;
3 | using CB.Shared.Utilities;
4 | using Newtonsoft.Json;
5 |
6 | namespace CB.Accessors.Implementations;
7 |
8 | public class TrovoAccessor : ITrovoAccessor
9 | {
10 | private readonly string _trovoClientId;
11 |
12 | public TrovoAccessor()
13 | {
14 | _trovoClientId = Environment.GetEnvironmentVariable("TrovoClientId");
15 | if (string.IsNullOrEmpty(_trovoClientId))
16 | {
17 | throw new InvalidOperationException("TrovoClientId Configuration Missing.");
18 | }
19 | }
20 |
21 | public async Task GetUserByNameAsync(string name)
22 | {
23 | var query = new TrovoUserQuery
24 | {
25 | User = [name]
26 | };
27 |
28 | return await ApiUtilities
29 | .PostApiHelper(
30 | "https://open-api.trovo.live/openplatform/getusers",
31 | [("Client-Id", _trovoClientId)],
32 | JsonConvert.SerializeObject(query))
33 | .ConfigureAwait(false);
34 | }
35 |
36 | public async Task GetChannelByIdAsync(int id)
37 | {
38 | var query = new TrovoQuery
39 | {
40 | ChannelId = id
41 | };
42 |
43 | return await ApiUtilities
44 | .PostApiHelper("https://open-api.trovo.live/openplatform/channels/id",
45 | [("Client-Id", _trovoClientId)],
46 | JsonConvert.SerializeObject(query))
47 | .ConfigureAwait(false);
48 | }
49 | }
50 |
51 | public class TrovoQuery
52 | {
53 | [JsonProperty("channel_id")]
54 | public int ChannelId { get; set; }
55 | }
56 |
57 | public class TrovoUserQuery
58 | {
59 | [JsonProperty("user")]
60 | public List User { get; set; }
61 | }
--------------------------------------------------------------------------------
/src/CB.Accessors/Implementations/UserAccessor.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using AutoMapper.QueryableExtensions;
3 | using CB.Accessors.Contracts;
4 | using CB.Data;
5 | using CB.Data.Entities;
6 | using CB.Shared.Dtos;
7 | using Microsoft.EntityFrameworkCore;
8 |
9 | namespace CB.Accessors.Implementations;
10 |
11 | public class UserAccessor(CbContext context,
12 | IMapper mapper)
13 | : IUserAccessor
14 | {
15 | public Task> GetAllAsync() => context
16 | .Users
17 | .AsNoTracking()
18 | .ProjectTo(mapper.ConfigurationProvider)
19 | .ToListAsync();
20 |
21 | public Task GetByIdAsync(string id) => context.Users
22 | .AsNoTracking()
23 | .Where(g => g.Id == id)
24 | .ProjectTo(mapper.ConfigurationProvider)
25 | .FirstOrDefaultAsync();
26 |
27 | public async Task CreateAsync(User entity)
28 | {
29 | entity.CreatedDate = DateTime.UtcNow;
30 | entity.ModifiedDate = DateTime.UtcNow;
31 |
32 | context.Users.Add(entity);
33 | await context
34 | .SaveChangesAsync()
35 | .ConfigureAwait(false);
36 |
37 | return mapper.Map(entity);
38 | }
39 |
40 | public async Task UpdateAsync(string id,
41 | User updated)
42 | {
43 | var user = await context
44 | .Users
45 | .FindAsync(id)
46 | .ConfigureAwait(false);
47 |
48 | if (user == null)
49 | {
50 | return null;
51 | }
52 |
53 | user.DisplayName = updated.DisplayName;
54 | user.Id = updated.Id;
55 | user.ModifiedDate = DateTime.UtcNow;
56 |
57 | await context
58 | .SaveChangesAsync()
59 | .ConfigureAwait(false);
60 |
61 | return mapper.Map(user);
62 | }
63 |
64 | public async Task DeleteAsync(string id)
65 | {
66 | var user = await context.Users.FindAsync(id);
67 | if (user == null)
68 | {
69 | return false;
70 | }
71 |
72 | context.Users.Remove(user);
73 | await context
74 | .SaveChangesAsync()
75 | .ConfigureAwait(false);
76 |
77 | return true;
78 | }
79 | }
--------------------------------------------------------------------------------
/src/CB.Accessors/Implementations/VodEmbedAccessor.cs:
--------------------------------------------------------------------------------
1 | using AutoMapper;
2 | using CB.Accessors.Contracts;
3 | using CB.Data;
4 | using CB.Shared.Dtos;
5 | using Microsoft.EntityFrameworkCore;
6 |
7 | namespace CB.Accessors.Implementations;
8 |
9 | public class VodEmbedAccessor(CbContext context,
10 | IMapper mapper)
11 | : IVodEmbedAccessor
12 | {
13 | public async Task GetByIdAsync(string guildId)
14 | {
15 | var entity = await context
16 | .VodEmbeds
17 | .FirstOrDefaultAsync(x => x.GuildId == guildId);
18 |
19 | if (entity == null)
20 | {
21 | entity = new()
22 | {
23 | GuildId = guildId,
24 | Header = "%CHANNEL% has published a new video!",
25 | Description = "%DESCRIPTION%",
26 | DescriptionLabel = "Video Description",
27 | Footer = "Powered by 100% Couch",
28 | ChannelButton = "Creator Channel!",
29 | };
30 |
31 | await context
32 | .VodEmbeds
33 | .AddAsync(entity);
34 | await context
35 | .SaveChangesAsync();
36 | }
37 |
38 | return mapper.Map(entity);
39 | }
40 |
41 | public async Task UpdateAsync(VodEmbedDto entity)
42 | {
43 | var embed = await context
44 | .VodEmbeds
45 | .FirstOrDefaultAsync(x => x.GuildId == entity.GuildId)
46 | .ConfigureAwait(false);
47 |
48 | if (embed == null)
49 | {
50 | return null;
51 | }
52 |
53 | embed.Header = entity.Header;
54 | embed.ChannelButton = entity.ChannelButton;
55 | embed.Description = entity.Description;
56 | embed.DescriptionLabel = entity.DescriptionLabel;
57 | embed.Footer = entity.Footer;
58 |
59 | await context
60 | .SaveChangesAsync()
61 | .ConfigureAwait(false);
62 |
63 | return mapper.Map(embed);
64 | }
65 | }
--------------------------------------------------------------------------------
/src/CB.Antenna/CB.Antenna.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 | dotnet-CB.Antenna-8011c496-f6b9-40f4-9d32-b5230132b68a
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/CB.Antenna/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
2 | WORKDIR /app
3 | EXPOSE 80
4 |
5 | FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
6 | WORKDIR /src
7 | COPY . .
8 | RUN dotnet restore "src/CB.Antenna/CB.Antenna.csproj"
9 | RUN dotnet publish "src/CB.Antenna/CB.Antenna.csproj" -c Release -o /app/publish
10 |
11 | FROM base AS final
12 | WORKDIR /app
13 | COPY --from=build /app/publish .
14 | ENTRYPOINT ["dotnet", "CB.Antenna.dll"]
15 |
--------------------------------------------------------------------------------
/src/CB.Antenna/Program.cs:
--------------------------------------------------------------------------------
1 | using CB.Antenna;
2 |
3 | var builder = Host.CreateApplicationBuilder(args);
4 | builder.Services.AddHostedService();
5 |
6 | var host = builder.Build();
7 | host.Run();
8 |
--------------------------------------------------------------------------------
/src/CB.Antenna/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/launchsettings.json",
3 | "profiles": {
4 | "CB.Antenna": {
5 | "commandName": "Project",
6 | "dotnetRunMessages": true,
7 | "environmentVariables": {
8 | "DOTNET_ENVIRONMENT": "Development"
9 | }
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/CB.Antenna/Worker.cs:
--------------------------------------------------------------------------------
1 | namespace CB.Antenna;
2 |
3 | public class Worker(ILogger logger) : BackgroundService
4 | {
5 | protected override async Task ExecuteAsync(CancellationToken stoppingToken)
6 | {
7 | while (!stoppingToken.IsCancellationRequested)
8 | {
9 | if (logger.IsEnabled(LogLevel.Information))
10 | {
11 | logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
12 | }
13 | await Task.Delay(1000, stoppingToken);
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/src/CB.Antenna/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft.Hosting.Lifetime": "Information"
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/CB.Antenna/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft.Hosting.Lifetime": "Information"
6 | }
7 | },
8 | "ConnectionStrings": {
9 | "DefaultConnection": "Host=db;Database=myappdb;Username=myuser;Password=mypassword"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/CB.Api/CB.Api.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 |
7 |
8 |
9 |
10 |
11 | all
12 | runtime; build; native; contentfiles; analyzers; buildtransitive
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/CB.Api/CB.Api.http:
--------------------------------------------------------------------------------
1 | @CB.Api_HostAddress = http://localhost:5210
2 |
3 | GET {{CB.Api_HostAddress}}/weatherforecast/
4 | Accept: application/json
5 |
6 | ###
7 |
--------------------------------------------------------------------------------
/src/CB.Api/Controllers/GuildController.cs:
--------------------------------------------------------------------------------
1 | using CB.Accessors.Contracts;
2 | using CB.Data.Entities;
3 | using CB.Shared.Dtos;
4 | using Microsoft.AspNetCore.Mvc;
5 |
6 | namespace CB.Api.Controllers;
7 |
8 | [ApiController]
9 | [Route("api/[controller]")]
10 | public class GuildsController(IGuildAccessor guildAccessor) : ControllerBase
11 | {
12 | // GET api/guilds
13 | [HttpGet]
14 | public async Task>> GetAll()
15 | {
16 | var guilds = await guildAccessor
17 | .GetAllAsync()
18 | .ConfigureAwait(false);
19 | return Ok(guilds);
20 | }
21 |
22 | // GET api/guilds/{id}
23 | [HttpGet("{id}")]
24 | public async Task> GetById(string id)
25 | {
26 | var guild = await guildAccessor
27 | .GetByIdAsync(id)
28 | .ConfigureAwait(false);
29 | if (guild == null)
30 | {
31 | return NotFound();
32 | }
33 |
34 | return Ok(guild);
35 | }
36 |
37 | // POST api/guilds
38 | [HttpPost]
39 | public async Task> Create([FromBody] Guild newGuild)
40 | {
41 | var created = await guildAccessor.CreateAsync(newGuild);
42 |
43 | // Return 201 Created with location header for the new resource
44 | return CreatedAtAction(nameof(GetById), new { id = created.Id }, created);
45 | }
46 |
47 | // PUT api/guilds/{id}
48 | [HttpPut("{id}")]
49 | public async Task> Update(string id, [FromBody] Guild updatedGuild)
50 | {
51 | var updated = await guildAccessor.UpdateAsync(id, updatedGuild);
52 | if (updated == null)
53 | {
54 | return NotFound();
55 | }
56 |
57 | return Ok(updated);
58 | }
59 |
60 | // DELETE api/guilds/{id}
61 | [HttpDelete("{id}")]
62 | public async Task Delete(string id)
63 | {
64 | var deleted = await guildAccessor.DeleteAsync(id);
65 | if (!deleted)
66 | {
67 | return NotFound();
68 | }
69 |
70 | return NoContent();
71 | }
72 | }
--------------------------------------------------------------------------------
/src/CB.Api/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
2 | WORKDIR /app
3 | EXPOSE 80
4 |
5 | FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
6 | WORKDIR /src
7 | COPY . .
8 | RUN dotnet restore "src/CB.Api/CB.Api.csproj"
9 | RUN dotnet publish "src/CB.Api/CB.Api.csproj" -c Release -o /app/publish
10 |
11 | FROM base AS final
12 | WORKDIR /app
13 | COPY --from=build /app/publish .
14 | ENTRYPOINT ["dotnet", "CB.Api.dll"]
15 |
--------------------------------------------------------------------------------
/src/CB.Api/Program.cs:
--------------------------------------------------------------------------------
1 | using CB.Data;
2 | using Microsoft.EntityFrameworkCore;
3 |
4 | var builder = WebApplication.CreateBuilder(args);
5 |
6 | builder.Services.AddDbContext(options =>
7 | options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")));
8 |
9 | builder.Services.AddControllers();
10 | // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
11 | builder.Services.AddEndpointsApiExplorer();
12 | builder.Services.AddSwaggerGen();
13 |
14 | var app = builder.Build();
15 |
16 | using (var scope = app.Services.CreateScope())
17 | {
18 | var db = scope.ServiceProvider.GetRequiredService();
19 | db.Database.Migrate();
20 | }
21 |
22 | // Configure the HTTP request pipeline.
23 | if (app.Environment.IsDevelopment())
24 | {
25 | app.UseSwagger();
26 | app.UseSwaggerUI();
27 | }
28 |
29 | app.UseHttpsRedirection();
30 |
31 | app.UseAuthorization();
32 |
33 | app.MapControllers();
34 |
35 | app.Run();
36 |
--------------------------------------------------------------------------------
/src/CB.Api/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/launchsettings.json",
3 | "iisSettings": {
4 | "windowsAuthentication": false,
5 | "anonymousAuthentication": true,
6 | "iisExpress": {
7 | "applicationUrl": "http://localhost:34225",
8 | "sslPort": 44387
9 | }
10 | },
11 | "profiles": {
12 | "http": {
13 | "commandName": "Project",
14 | "dotnetRunMessages": true,
15 | "launchBrowser": true,
16 | "launchUrl": "swagger",
17 | "applicationUrl": "http://localhost:5210",
18 | "environmentVariables": {
19 | "ASPNETCORE_ENVIRONMENT": "Development"
20 | }
21 | },
22 | "https": {
23 | "commandName": "Project",
24 | "dotnetRunMessages": true,
25 | "launchBrowser": true,
26 | "launchUrl": "swagger",
27 | "applicationUrl": "https://localhost:7274;http://localhost:5210",
28 | "environmentVariables": {
29 | "ASPNETCORE_ENVIRONMENT": "Development"
30 | }
31 | },
32 | "IIS Express": {
33 | "commandName": "IISExpress",
34 | "launchBrowser": true,
35 | "launchUrl": "swagger",
36 | "environmentVariables": {
37 | "ASPNETCORE_ENVIRONMENT": "Development"
38 | }
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/CB.Api/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft.AspNetCore": "Warning"
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/CB.Api/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft.AspNetCore": "Warning"
6 | }
7 | },
8 | "AllowedHosts": "*",
9 | "ConnectionStrings": {
10 | "DefaultConnection": "Host=db;Database=myappdb;Username=myuser;Password=mypassword"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/CB.Bot/CB.Bot.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 | dotnet-CB.Bot-e32dd526-c97c-446a-816b-f4b92c67f026
7 |
8 |
9 |
10 |
11 |
12 |
13 | all
14 | runtime; build; native; contentfiles; analyzers; buildtransitive
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/src/CB.Bot/Commands/Application/BaseSlashCommands.cs:
--------------------------------------------------------------------------------
1 | using Discord;
2 | using Discord.Interactions;
3 | using Discord.WebSocket;
4 |
5 | namespace CB.Bot.Commands.Application;
6 |
7 | ///
8 | /// Base class to provide easier access to helper functions for our slash command implementations.
9 | ///
10 | public class BaseSlashCommands() : InteractionModuleBase
11 | {
12 | public IGuildUser GuildUser => (IGuildUser)Context.User;
13 | public IGuildChannel GuildChannel => (IGuildChannel)Context.Channel;
14 | public SocketInteraction SocketInteraction => (SocketInteraction)Context.Interaction;
15 | public SocketSlashCommand SocketSlashCommand => (SocketSlashCommand)SocketInteraction;
16 |
17 | ///
18 | /// Validate if the user executing the slash command is an approved admin, or not.
19 | ///
20 | /// Is user admin? true or false
21 | public async Task IsUserAdmin(bool sendResponse = true)
22 | {
23 | // TODO MS - We need to add ApprovedAdmin code.
24 | // This'll do for now.
25 | var isAdmin = GuildUser.GuildPermissions.ManageGuild;
26 |
27 | if (!isAdmin && sendResponse)
28 | {
29 | await SocketInteraction.FollowupAsync("Sorry, you have to be an admin to use that command.",
30 | ephemeral: true)
31 | .ConfigureAwait(false);
32 | }
33 |
34 | return isAdmin;
35 | }
36 | }
--------------------------------------------------------------------------------
/src/CB.Bot/Commands/Application/ChannelSlashCommands.cs:
--------------------------------------------------------------------------------
1 | using CB.Accessors.Contracts;
2 | using CB.Shared.Enums;
3 | using Discord;
4 | using Discord.Interactions;
5 | // ReSharper disable UnusedMember.Local
6 | // ReSharper disable UnusedMember.Global
7 |
8 | namespace CB.Bot.Commands.Application;
9 |
10 | [Group("channel", "Channel configuration")]
11 | public class ChannelSlashCommands(IGuildAccessor guildAccessor,
12 | IChannelAccessor channelAccessor,
13 | IChannelConfigurationAccessor channelConfigurationAccessor) : BaseSlashCommands
14 | {
15 | [SlashCommand(
16 | "live",
17 | "Configure server 'Channel' settings",
18 | false,
19 | RunMode.Async)]
20 | private async Task LiveChannelConfigurationAsync(IGuildChannel channel)
21 | {
22 | await ConfigureChannelAsync(ConfiguredChannelType.Live,
23 | channel);
24 | }
25 |
26 | [SlashCommand(
27 | "greetings",
28 | "Configure server 'Channel' settings",
29 | false,
30 | RunMode.Async)]
31 | private async Task GreetingChannelConfigurationAsync(IGuildChannel channel)
32 | {
33 | await ConfigureChannelAsync(ConfiguredChannelType.Greetings,
34 | channel);
35 | }
36 |
37 | [SlashCommand(
38 | "goodbyes",
39 | "Configure server 'Channel' settings",
40 | false,
41 | RunMode.Async)]
42 | private async Task GoodbyeChannelConfigurationAsync(IGuildChannel channel)
43 | {
44 | await ConfigureChannelAsync(ConfiguredChannelType.Goodbyes,
45 | channel);
46 | }
47 |
48 | [SlashCommand(
49 | "discordlive",
50 | "Configure server 'Discord Live Channel' setting",
51 | false,
52 | RunMode.Async)]
53 | private async Task DiscordLiveChannelConfigurationAsync(IGuildChannel channel)
54 | {
55 | await ConfigureChannelAsync(ConfiguredChannelType.DiscordLive,
56 | channel);
57 | }
58 |
59 | private async Task ConfigureChannelAsync(ConfiguredChannelType configuredChannelType,
60 | IGuildChannel discordChannel)
61 | {
62 | await SocketInteraction
63 | .DeferAsync(true)
64 | .ConfigureAwait(false);
65 |
66 | if (!await IsUserAdmin())
67 | {
68 | return;
69 | }
70 |
71 | var guild = await guildAccessor
72 | .GetConfigurationSummaryByIdAsync(Context.Guild.Id.ToString())
73 | .ConfigureAwait(false);
74 |
75 | if (guild == null)
76 | {
77 | await FollowupAsync("There was an issue retrieving your guild. Contact support.", ephemeral: true)
78 | .ConfigureAwait(false);
79 | return;
80 | }
81 |
82 | var existingChannel = await channelAccessor.GetChannelConfigurationSummaryByIdAsync(discordChannel.Id.ToString()).ConfigureAwait(false)
83 | ?? await channelAccessor.CreateAsync(new()
84 | {
85 | CreatedDate = DateTime.UtcNow,
86 | ModifiedDate = DateTime.UtcNow,
87 | DisplayName = discordChannel.Name,
88 | GuildId = guild.Id,
89 | Id = discordChannel.Id.ToString()
90 | }).ConfigureAwait(false);
91 |
92 | switch (configuredChannelType)
93 | {
94 | case ConfiguredChannelType.Greetings:
95 | guild.ChannelConfiguration.GreetingChannelId = existingChannel.Id;
96 | break;
97 | case ConfiguredChannelType.Goodbyes:
98 | guild.ChannelConfiguration.GoodbyeChannelId = existingChannel.Id;
99 | break;
100 | case ConfiguredChannelType.Live:
101 | guild.ChannelConfiguration.LiveChannelId = existingChannel.Id;
102 | break;
103 | case ConfiguredChannelType.DiscordLive:
104 | guild.ChannelConfiguration.DiscordLiveChannelId = existingChannel.Id;
105 | break;
106 | }
107 |
108 | await channelConfigurationAccessor.UpdateAsync(guild.ChannelConfiguration.GuildId,
109 | guild.ChannelConfiguration)
110 | .ConfigureAwait(false);
111 |
112 | await FollowupAsync($"Your '{configuredChannelType}' channel is now #{existingChannel.DisplayName}",
113 | ephemeral: true)
114 | .ConfigureAwait(false);
115 | }
116 | }
--------------------------------------------------------------------------------
/src/CB.Bot/Commands/Application/DiscordLiveSlashCommands.cs:
--------------------------------------------------------------------------------
1 | using CB.Accessors.Contracts;
2 | using CB.Data.Entities;
3 | using Discord;
4 | using Discord.Interactions;
5 |
6 | // ReSharper disable UnusedMember.Global
7 | // ReSharper disable UnusedMember.Local
8 |
9 | namespace CB.Bot.Commands.Application;
10 |
11 | public class DiscordLiveSlashCommands(IDiscordLiveConfigurationAccessor discordLiveConfigurationAccessor)
12 | : BaseSlashCommands
13 | {
14 | [SlashCommand("discordlive",
15 | "Configure the Discord Live notification.",
16 | false,
17 | RunMode.Async)]
18 | private async Task DiscordLiveAsync(string description,
19 | string footer,
20 | string header,
21 | string message,
22 | IRole mentionRole = null)
23 | {
24 | await SocketInteraction.DeferAsync(true);
25 |
26 | if (!await IsUserAdmin())
27 | {
28 | return;
29 | }
30 |
31 | var config = await discordLiveConfigurationAccessor.GetByIdAsync(GuildUser.GuildId.ToString()) ?? await discordLiveConfigurationAccessor.CreateAsync(new DiscordLiveConfiguration
32 | {
33 | GuildId = GuildUser.GuildId.ToString()
34 | });
35 |
36 | config.Description = description;
37 | config.Footer = footer;
38 | config.Header = header;
39 | config.Message = message;
40 | config.MentionRoleId = mentionRole?.Id.ToString();
41 |
42 | await discordLiveConfigurationAccessor.UpdateAsync(config);
43 | await SocketInteraction.FollowupAsync("Your configuration for your Discord Live announcements has been set!");
44 | }
45 | }
--------------------------------------------------------------------------------
/src/CB.Bot/Commands/Application/DiscoverySlashCommands.cs:
--------------------------------------------------------------------------------
1 | using CB.Accessors.Contracts;
2 | using Discord;
3 | using Discord.Interactions;
4 |
5 | // ReSharper disable UnusedMember.Local
6 | // ReSharper disable UnusedMember.Global
7 |
8 | namespace CB.Bot.Commands.Application;
9 |
10 | [Group("discovery", "Discovery commands")]
11 | public class DiscoverySlashCommands(IGuildAccessor guildAccessor,
12 | IRoleConfigurationAccessor roleConfigurationAccessor,
13 | IAllowConfigurationAccessor allowConfigurationAccessor) : BaseSlashCommands
14 | {
15 | [SlashCommand(
16 | "enable",
17 | "Configure server 'Discovery' setting",
18 | false,
19 | RunMode.Async)]
20 | private async Task DiscoveryEnableConfigurationAsync(IRole role = null)
21 | {
22 | await DoStuff(true, role);
23 | }
24 |
25 | [SlashCommand(
26 | "disable",
27 | "Configure server 'Discovery' setting",
28 | false,
29 | RunMode.Async)]
30 | private async Task DiscoveryDisableConfigurationAsync()
31 | {
32 | await DoStuff(false);
33 | }
34 |
35 | private async Task DoStuff(bool isEnabled,
36 | IRole role = null)
37 | {
38 | await SocketInteraction.DeferAsync(true);
39 |
40 | if (!await IsUserAdmin())
41 | {
42 | return;
43 | }
44 |
45 | var guildChannel = (IGuildChannel)SocketInteraction.Channel;
46 | var guild = await guildAccessor.GetConfigurationSummaryByIdAsync(guildChannel.Guild.Id.ToString());
47 | if (guild == null)
48 | {
49 | await SocketInteraction.FollowupAsync(
50 | "Sorry, unable to configure Discovery for this guild. Contact support.", ephemeral: true);
51 | return;
52 | }
53 |
54 | string message;
55 | if (!isEnabled)
56 | {
57 | guild.RoleConfiguration.DiscoveryRoleId = null;
58 | guild.AllowConfiguration.AllowLiveDiscovery = false;
59 | message = "Discovery has been set to `Disabled`.";
60 | }
61 | else
62 | {
63 | if (role != null)
64 | {
65 | guild.RoleConfiguration.DiscoveryRoleId = role.Id.ToString();
66 | guild.AllowConfiguration.AllowLiveDiscovery = true;
67 |
68 | message = $"Discovery has been set to role {role.Name}.";
69 | }
70 | else
71 | {
72 | guild.RoleConfiguration.DiscoveryRoleId = null;
73 | guild.AllowConfiguration.AllowLiveDiscovery = true;
74 |
75 | message = "Discovery has been set to .. literally everyone!";
76 | }
77 | }
78 |
79 | await roleConfigurationAccessor.UpdateAsync(guild.RoleConfiguration);
80 | await allowConfigurationAccessor.UpdateAsync(guild.AllowConfiguration);
81 |
82 | await SocketInteraction.FollowupAsync(message, ephemeral: true);
83 | }
84 | }
--------------------------------------------------------------------------------
/src/CB.Bot/Commands/Application/FilterSlashCommands.cs:
--------------------------------------------------------------------------------
1 | using CB.Accessors.Contracts;
2 | using CB.Data.Entities;
3 | using Discord.Interactions;
4 | using FilterType = CB.Shared.Enums.FilterType;
5 | using Platform = CB.Shared.Enums.Platform;
6 |
7 | // ReSharper disable UnusedMember.Global
8 | // ReSharper disable UnusedMember.Local
9 |
10 | namespace CB.Bot.Commands.Application;
11 |
12 | public class FilterSlashCommands(IFilterAccessor filterAccessor)
13 | : BaseSlashCommands
14 | {
15 | [SlashCommand(
16 | "filter",
17 | "Manage your filters.",
18 | false,
19 | RunMode.Async)]
20 | private async Task ToggleFilter(string filterText,
21 | FilterType filterType,
22 | Platform platform)
23 | {
24 | await SocketInteraction.DeferAsync(true);
25 |
26 | if (!await IsUserAdmin())
27 | {
28 | return;
29 | }
30 |
31 | var t = (int)platform;
32 | filterText = filterText.TrimStart('"').TrimEnd('"');
33 | var existingFilters = await filterAccessor.GetAllAsync(SocketInteraction.GuildId.ToString());
34 | var existingFilter = existingFilters.FirstOrDefault(
35 | x => x.PlatformId == (int)platform &&
36 | x.FilterTypeId == (int)filterType &&
37 | x.Text.Equals(filterText, StringComparison.OrdinalIgnoreCase));
38 |
39 | if (existingFilter == null)
40 | {
41 | await filterAccessor.CreateAsync(new Filter
42 | {
43 | Text = filterText,
44 | PlatformId = (int)platform,
45 | FilterTypeId = (int)filterType,
46 | GuildId = SocketInteraction.GuildId.ToString()
47 | });
48 |
49 | await SocketInteraction.FollowupAsync($"{filterType} filter has been created.");
50 | }
51 | else
52 | {
53 | await filterAccessor.DeleteAsync(existingFilter);
54 | await SocketInteraction.FollowupAsync($"{filterType} filter has been removed.");
55 | }
56 | }
57 | }
--------------------------------------------------------------------------------
/src/CB.Bot/Commands/Application/FunSlashCommands.cs:
--------------------------------------------------------------------------------
1 | using System.Security.Cryptography;
2 | using CB.Accessors.Contracts;
3 | using Discord.Interactions;
4 |
5 | namespace CB.Bot.Commands.Application;
6 |
7 | public class FunSlashCommands(IFunAccessor funAccessor) : InteractionModuleBase
8 | {
9 | [SlashCommand("roll", "Roll a D20")]
10 | public async Task RollAsync()
11 | {
12 | var rollResult = RandomNumberGenerator.GetInt32(1, 21);
13 | await RespondAsync($"You rolled a {rollResult}!");
14 | }
15 |
16 | [SlashCommand("haibai", "They comes .. they goes ..")]
17 | public async Task HaiBaiAsync()
18 | {
19 | var haiBaiCount = await funAccessor.IncrementHaiBai();
20 |
21 | await RespondAsync(
22 | $"_ _" +
23 | $"[`HaiBai Count: {haiBaiCount}`](https://cdn.discordapp.com/attachments/313015271566868480/502228420881678336/couch.gif?ex=6882a2aa&is=6881512a&hm=8541f1b54df685b3ee50627db3c2c1fae730e371d3e4fd7d78eadb40a1d1102f&)");
24 | }
25 | }
--------------------------------------------------------------------------------
/src/CB.Bot/Commands/Application/UtilityApplicationCommands.cs:
--------------------------------------------------------------------------------
1 | using Discord.Interactions;
2 | // ReSharper disable UnusedMember.Local
3 | // ReSharper disable UnusedMember.Global
4 |
5 | namespace CB.Bot.Commands.Application;
6 |
7 | public class UtilityApplicationCommands : InteractionModuleBase
8 | {
9 | [SlashCommand("ping",
10 | "Test bot response.",
11 | true,
12 | RunMode.Async)]
13 | private async Task PingAsync()
14 | {
15 | await RespondAsync("Pong!")
16 | .ConfigureAwait(false);
17 | }
18 | }
--------------------------------------------------------------------------------
/src/CB.Bot/Commands/Text/UtilityTextCommands.cs:
--------------------------------------------------------------------------------
1 | using Discord.Commands;
2 |
3 | namespace CB.Bot.Commands.Text;
4 |
5 | public class UtilityTextCommands : ModuleBase
6 | {
7 | [Command("ping",
8 | true,
9 | "Test bot response.",
10 | RunMode = RunMode.Async)]
11 | private async Task PingAsync()
12 | {
13 | await ReplyAsync("Pong!")
14 | .ConfigureAwait(false);
15 | }
16 | }
--------------------------------------------------------------------------------
/src/CB.Bot/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
2 | WORKDIR /app
3 | EXPOSE 80
4 |
5 | FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
6 | WORKDIR /src
7 | COPY . .
8 | RUN dotnet restore "src/CB.Bot/CB.Bot.csproj"
9 | RUN dotnet publish "src/CB.Bot/CB.Bot.csproj" -c Release -o /app/publish
10 |
11 | FROM base AS final
12 | WORKDIR /app
13 | COPY --from=build /app/publish .
14 | ENTRYPOINT ["dotnet", "CB.Bot.dll"]
15 |
--------------------------------------------------------------------------------
/src/CB.Bot/Program.cs:
--------------------------------------------------------------------------------
1 | using CB.Accessors.Contracts;
2 | using CB.Accessors.Implementations;
3 | using CB.Bot.Services;
4 | using CB.Data;
5 | using CB.Engines.Contracts;
6 | using CB.Engines.Implementations;
7 | using CB.Shared.Dtos;
8 | using Discord;
9 | using Discord.Commands;
10 | using Discord.Interactions;
11 | using Discord.WebSocket;
12 | using Microsoft.EntityFrameworkCore;
13 |
14 | AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
15 |
16 | var builder = Host.CreateApplicationBuilder(args);
17 | var socketConfig = new DiscordSocketConfig
18 | {
19 | LogLevel = LogSeverity.Verbose,
20 | GatewayIntents =
21 | GatewayIntents.GuildVoiceStates |
22 | GatewayIntents.GuildScheduledEvents |
23 | GatewayIntents.DirectMessages |
24 | GatewayIntents.GuildIntegrations |
25 | GatewayIntents.GuildMessageReactions |
26 | GatewayIntents.Guilds |
27 | GatewayIntents.GuildMessages,
28 | UseInteractionSnowflakeDate = false,
29 | AlwaysDownloadUsers = true,
30 | };
31 |
32 | var client = new DiscordSocketClient(socketConfig);
33 | builder.Services.AddSingleton(client);
34 | builder.Services.AddSingleton();
35 | builder.Services.AddSingleton(new InteractionService(client));
36 | builder.Services.AddSingleton();
37 | builder.Services.AddSingleton();
38 | builder.Services.AddScoped();
39 | builder.Services.AddScoped();
40 | builder.Services.AddScoped();
41 | builder.Services.AddScoped();
42 | builder.Services.AddScoped();
43 | builder.Services.AddScoped();
44 | builder.Services.AddScoped();
45 | builder.Services.AddScoped();
46 | builder.Services.AddScoped();
47 | builder.Services.AddScoped();
48 | builder.Services.AddScoped();
49 | builder.Services.AddScoped();
50 | builder.Services.AddScoped