├── TwitchBot ├── TwitchBotDb │ ├── SqlScripts │ │ ├── Tables │ │ │ └── InsertDefaults.sql │ │ ├── UserTableTypes │ │ │ └── dbo.Usernames.sql │ │ └── StoredProcedures │ │ │ ├── dbo.GetPartyUpRequestList.sql │ │ │ ├── dbo.UpdateCreateBalance.sql │ │ │ ├── dbo.CreateNewBossesWithDefaultStats.sql │ │ │ └── dbo.CreateDefaultRanks.sql │ ├── DTO │ │ ├── BalanceResult.cs │ │ └── PartyUpRequestResult.cs │ ├── Models │ │ ├── Rank.cs │ │ ├── SongRequestIgnore.cs │ │ ├── Bank.cs │ │ ├── SongRequest.cs │ │ ├── RankFollower.cs │ │ ├── Whitelist.cs │ │ ├── InGameUsername.cs │ │ ├── Quote.cs │ │ ├── SongRequestSetting.cs │ │ ├── DiscordSelfRoleAssign.cs │ │ ├── PartyUpRequest.cs │ │ ├── ErrorLog.cs │ │ ├── PartyUp.cs │ │ ├── CustomCommand.cs │ │ ├── TwitchGameCategory.cs │ │ ├── Reminder.cs │ │ ├── BossFightClassStats.cs │ │ ├── BossFightSetting.cs │ │ ├── BankHeistSetting.cs │ │ ├── BossFightBossStats.cs │ │ └── Broadcaster.cs │ ├── Repositories │ │ ├── GameDirectoryRepository.cs │ │ ├── DiscordSelfAssignRoleRepository.cs │ │ ├── QuoteRepository.cs │ │ ├── InGameUsernameRepository.cs │ │ ├── ManualSongRequestRepository.cs │ │ ├── PartyUpRepository.cs │ │ ├── SongRequestSettingRepository.cs │ │ ├── FollowerRepository.cs │ │ ├── BankRepository.cs │ │ └── SongRequestBlacklistRepository.cs │ ├── Services │ │ ├── DiscordSelfAssignRoleService.cs │ │ ├── QuoteService.cs │ │ ├── GameDirectoryService.cs │ │ ├── InGameUsernameService.cs │ │ ├── SongRequestSettingService.cs │ │ ├── BankService.cs │ │ ├── SongRequestBlackListService.cs │ │ ├── ManualSongRequestService.cs │ │ ├── FollowerService.cs │ │ └── PartyUpService.cs │ └── TwitchBotDb.csproj ├── TwitchBotShared │ ├── Models │ │ ├── RouletteUser.cs │ │ ├── JSON │ │ │ ├── Pagination.cs │ │ │ ├── ErrMsgJSON.cs │ │ │ ├── ChannelUpdateJSON.cs │ │ │ ├── GameJSON.cs │ │ │ ├── ChatterInfoJSON.cs │ │ │ ├── FollowerJSON.cs │ │ │ ├── ChannelJSON.cs │ │ │ ├── UserJSON.cs │ │ │ ├── StreamJSON.cs │ │ │ ├── SubscriptionJSON.cs │ │ │ ├── ClipJSON.cs │ │ │ └── VideoJSON.cs │ │ ├── CommandPermission.cs │ │ ├── CooldownUser.cs │ │ ├── TwitchChatterType.cs │ │ ├── DelayedMessage.cs │ │ ├── TwitchChatter.cs │ │ └── RemindUser.cs │ ├── Enums │ │ └── ChatterType.cs │ ├── Extensions │ │ ├── ListStringExtensions.cs │ │ ├── IListExtensions.cs │ │ ├── ProcessExtensions.cs │ │ ├── TimeSpanExtensions.cs │ │ └── StringExtensions.cs │ ├── Threads │ │ ├── PingSender.cs │ │ ├── DelayMessage.cs │ │ └── TwitchStreamStatus.cs │ ├── ClientLibraries │ │ ├── Singletons │ │ │ ├── RouletteSingleton.cs │ │ │ ├── DelayedMessageSingleton.cs │ │ │ ├── CustomCommandSingleton.cs │ │ │ ├── BroadcasterSingleton.cs │ │ │ ├── YouTubeClient.cs │ │ │ ├── JoinStreamerSingleton.cs │ │ │ ├── ErrorHandler.cs │ │ │ └── CooldownUsersSingleton.cs │ │ ├── TwitterClient.cs │ │ ├── TwitchChatterList.cs │ │ └── IrcClient.cs │ ├── TwitchBotShared.csproj │ ├── Commands │ │ └── SharedCommands.cs │ └── ApiLibraries │ │ └── ApiTwitchRequest.cs ├── TwitchBot.Api │ ├── DTO │ │ └── BroadcasterConfig.cs │ ├── Helpers │ │ ├── ErrorExceptions │ │ │ ├── ApiException.cs │ │ │ ├── NotFoundException.cs │ │ │ └── ErrorHandlerMiddleware.cs │ │ └── ExtendedControllerBase.cs │ ├── Properties │ │ └── launchSettings.json │ ├── TwitchBot.Api.csproj │ ├── Controllers │ │ ├── ErrorLogsController.cs │ │ ├── TwitchGameCategoriesController.cs │ │ ├── DiscordSelfRoleAssignController.cs │ │ ├── PartyUpsController.cs │ │ ├── BossFightClassStatsController.cs │ │ ├── BankHeistSettingsController.cs │ │ ├── BossFightSettingsController.cs │ │ ├── SongRequestsController.cs │ │ ├── WhitelistsController.cs │ │ ├── BossFightBossStatsController.cs │ │ ├── SongRequestSettingsController.cs │ │ ├── RemindersController.cs │ │ ├── BroadcastersController.cs │ │ ├── QuotesController.cs │ │ ├── RankFollowersController.cs │ │ └── CustomCommandsController.cs │ └── Program.cs ├── TwitchBotConsoleApp │ ├── App.config │ ├── TwitchBotConsoleApp.csproj │ ├── Program.cs │ └── TwitchBotModule.cs └── TwitchBot.sln └── .gitignore /TwitchBot/TwitchBotDb/SqlScripts/Tables/InsertDefaults.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO dbo.Broadcaster (Username, TwitchId) VALUES ('n/a', '-1') 2 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/SqlScripts/UserTableTypes/dbo.Usernames.sql: -------------------------------------------------------------------------------- 1 | CREATE TYPE [dbo].[Usernames] AS TABLE ( 2 | [Username] VARCHAR (30) NOT NULL 3 | ); -------------------------------------------------------------------------------- /TwitchBot/TwitchBotShared/Models/RouletteUser.cs: -------------------------------------------------------------------------------- 1 | namespace TwitchBotShared.Models 2 | { 3 | public class RouletteUser 4 | { 5 | public string Username { get; set; } 6 | public int ShotsTaken { get; set; } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotShared/Models/JSON/Pagination.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace TwitchBotShared.Models.JSON 4 | { 5 | public class Pagination 6 | { 7 | [JsonProperty("cursor")] 8 | public string Cursor { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/DTO/BalanceResult.cs: -------------------------------------------------------------------------------- 1 | namespace TwitchBotDb.DTO 2 | { 3 | public class BalanceResult 4 | { 5 | public string ActionType { get; set; } 6 | public string Username { get; set; } 7 | public int Wallet { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotShared/Models/CommandPermission.cs: -------------------------------------------------------------------------------- 1 | using TwitchBotShared.Enums; 2 | 3 | namespace TwitchBotShared.Models 4 | { 5 | public class CommandPermission 6 | { 7 | public ChatterType General { get; set; } 8 | public ChatterType? Elevated { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotShared/Models/CooldownUser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace TwitchBotShared.Models 4 | { 5 | public class CooldownUser 6 | { 7 | public string Username { get; set; } 8 | public DateTime Cooldown { get; set; } 9 | public string Command { get; set; } 10 | public bool Warned { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotShared/Models/TwitchChatterType.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | using TwitchBotShared.Enums; 4 | 5 | namespace TwitchBotShared.Models 6 | { 7 | public class TwitchChatterType 8 | { 9 | public List TwitchChatters { get; set; } 10 | public ChatterType ChatterType { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/Models/Rank.cs: -------------------------------------------------------------------------------- 1 | namespace TwitchBotDb.Models 2 | { 3 | public class Rank 4 | { 5 | public int Id { get; set; } 6 | public string Name { get; set; } 7 | public int ExpCap { get; set; } 8 | public int BroadcasterId { get; set; } 9 | 10 | public virtual Broadcaster Broadcaster { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBot.Api/DTO/BroadcasterConfig.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | 3 | namespace TwitchBot.Api.DTO 4 | { 5 | public class BroadcasterConfig 6 | { 7 | [FromQuery(Name = "broadcasterName")] 8 | public string BroadcasterName { get; set; } 9 | [FromQuery(Name = "botName")] 10 | public string BotName { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotShared/Enums/ChatterType.cs: -------------------------------------------------------------------------------- 1 | namespace TwitchBotShared.Enums 2 | { 3 | public enum ChatterType 4 | { 5 | DoesNotExist, 6 | Viewer, 7 | Follower, 8 | RegularFollower, 9 | Subscriber, 10 | VIP, 11 | Moderator, 12 | GlobalModerator, 13 | Admin, 14 | Staff, 15 | Broadcaster 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/Models/SongRequestIgnore.cs: -------------------------------------------------------------------------------- 1 | namespace TwitchBotDb.Models 2 | { 3 | public class SongRequestIgnore 4 | { 5 | public int Id { get; set; } 6 | public string Artist { get; set; } 7 | public string Title { get; set; } 8 | public int BroadcasterId { get; set; } 9 | 10 | public virtual Broadcaster Broadcaster { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/Models/Bank.cs: -------------------------------------------------------------------------------- 1 | namespace TwitchBotDb.Models 2 | { 3 | public class Bank 4 | { 5 | public int Id { get; set; } 6 | public string Username { get; set; } 7 | public int? TwitchId { get; set; } 8 | public int Wallet { get; set; } 9 | public int Broadcaster { get; set; } 10 | 11 | public virtual Broadcaster BroadcasterNavigation { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/DTO/PartyUpRequestResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace TwitchBotDb.DTO 4 | { 5 | public class PartyUpRequestResult 6 | { 7 | public int PartyRequestId { get; set; } 8 | public string Username { get; set; } 9 | public string PartyMemberName { get; set; } 10 | public int PartyMemberId { get; set; } 11 | public DateTime TimeRequested { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/Models/SongRequest.cs: -------------------------------------------------------------------------------- 1 | namespace TwitchBotDb.Models 2 | { 3 | public class SongRequest 4 | { 5 | public int Id { get; set; } 6 | public string Name { get; set; } 7 | public string Username { get; set; } 8 | public int? TwitchId { get; set; } 9 | public int BroadcasterId { get; set; } 10 | 11 | public virtual Broadcaster Broadcaster { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotShared/Models/DelayedMessage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace TwitchBotShared.Models 4 | { 5 | public class DelayedMessage 6 | { 7 | public int ReminderId { get; set; } 8 | public string Message { get; set; } 9 | public DateTime SendDate { get; set; } 10 | public int? ReminderEveryMin { get; set; } 11 | public DateTime? ExpirationDateUtc { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotShared/Models/JSON/ErrMsgJSON.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace TwitchBotShared.Models.JSON 4 | { 5 | public class ErrMsgJSON 6 | { 7 | [JsonProperty("error")] 8 | public string Error { get; set; } 9 | [JsonProperty("status")] 10 | public string Status { get; set; } 11 | [JsonProperty("message")] 12 | public string Message { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/Models/RankFollower.cs: -------------------------------------------------------------------------------- 1 | namespace TwitchBotDb.Models 2 | { 3 | public class RankFollower 4 | { 5 | public int Id { get; set; } 6 | public string Username { get; set; } 7 | public int? TwitchId { get; set; } 8 | public int Experience { get; set; } 9 | public int BroadcasterId { get; set; } 10 | 11 | public virtual Broadcaster Broadcaster { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/Models/Whitelist.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace TwitchBotDb.Models 5 | { 6 | public class Whitelist 7 | { 8 | public int Id { get; set; } 9 | public int CustomCommandId { get; set; } 10 | public string Username { get; set; } 11 | public int TwitchId { get; set; } 12 | 13 | public virtual CustomCommand CustomCommand { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/Models/InGameUsername.cs: -------------------------------------------------------------------------------- 1 | namespace TwitchBotDb.Models 2 | { 3 | public class InGameUsername 4 | { 5 | public int Id { get; set; } 6 | public string Message { get; set; } 7 | public int BroadcasterId { get; set; } 8 | public int? GameId { get; set; } 9 | 10 | public virtual Broadcaster Broadcaster { get; set; } 11 | public virtual TwitchGameCategory Game { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/Models/Quote.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace TwitchBotDb.Models 4 | { 5 | public class Quote 6 | { 7 | public int Id { get; set; } 8 | public string UserQuote { get; set; } 9 | public string Username { get; set; } 10 | public DateTime TimeCreated { get; set; } 11 | public int BroadcasterId { get; set; } 12 | 13 | public virtual Broadcaster Broadcaster { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/Models/SongRequestSetting.cs: -------------------------------------------------------------------------------- 1 | namespace TwitchBotDb.Models 2 | { 3 | public class SongRequestSetting 4 | { 5 | public int Id { get; set; } 6 | public string RequestPlaylistId { get; set; } 7 | public string PersonalPlaylistId { get; set; } 8 | public bool DjMode { get; set; } 9 | public int BroadcasterId { get; set; } 10 | 11 | public virtual Broadcaster Broadcaster { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotShared/Models/JSON/ChannelUpdateJSON.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace TwitchBotShared.Models.JSON 4 | { 5 | public class ChannelUpdateJSON 6 | { 7 | [JsonProperty("game_id")] 8 | public string GameId { get; set; } 9 | [JsonProperty("title")] 10 | public string Title { get; set; } 11 | //public string BroadcasterLanguage { get; set; } 12 | //public int Delay { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/Models/DiscordSelfRoleAssign.cs: -------------------------------------------------------------------------------- 1 | namespace TwitchBotDb.Models 2 | { 3 | public class DiscordSelfRoleAssign 4 | { 5 | public int Id { get; set; } 6 | public string RoleName { get; set; } 7 | public string ServerName { get; set; } 8 | public int FollowAgeMinimumHour { get; set; } 9 | public int BroadcasterId { get; set; } 10 | 11 | public virtual Broadcaster Broadcaster { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/Models/PartyUpRequest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace TwitchBotDb.Models 4 | { 5 | public class PartyUpRequest 6 | { 7 | public int Id { get; set; } 8 | public string Username { get; set; } 9 | public int? TwitchId { get; set; } 10 | public int PartyMemberId { get; set; } 11 | public DateTime TimeRequested { get; set; } 12 | 13 | public virtual PartyUp PartyMember { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotShared/Models/TwitchChatter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace TwitchBotShared.Models 4 | { 5 | public class TwitchChatter 6 | { 7 | public string Username { get; set; } 8 | public string DisplayName { get; set; } 9 | public DateTime? CreatedAt { get; set; } 10 | public string Badges { get; set; } 11 | public string Message { get; set; } 12 | public string TwitchId { get; set; } 13 | public string MessageId { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotConsoleApp/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotShared/Models/JSON/GameJSON.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | using Newtonsoft.Json; 4 | 5 | namespace TwitchBotShared.Models.JSON 6 | { 7 | public class GameJSON 8 | { 9 | [JsonProperty("id")] 10 | public string Id { get; set; } 11 | 12 | [JsonProperty("name")] 13 | public string Name { get; set; } 14 | 15 | //public string BoxArtUrl { get; set; } 16 | } 17 | 18 | public class RootGameJSON 19 | { 20 | [JsonProperty("data")] 21 | public List Games { get; set; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotShared/Extensions/ListStringExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Data; 3 | 4 | namespace TwitchBotShared.Extensions 5 | { 6 | public static class ListStringExtensions 7 | { 8 | public static DataTable ToDataTable(this List list) 9 | { 10 | DataTable table = new DataTable(); 11 | 12 | table.Columns.Add(); 13 | 14 | foreach (var array in list) 15 | { 16 | table.Rows.Add(array); 17 | } 18 | 19 | return table; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/Models/ErrorLog.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace TwitchBotDb.Models 4 | { 5 | public class ErrorLog 6 | { 7 | public int Id { get; set; } 8 | public DateTime ErrorTime { get; set; } 9 | public int ErrorLine { get; set; } 10 | public string ErrorClass { get; set; } 11 | public string ErrorMethod { get; set; } 12 | public string ErrorMsg { get; set; } 13 | public int BroadcasterId { get; set; } 14 | public string Command { get; set; } 15 | public string UserMsg { get; set; } 16 | 17 | public virtual Broadcaster Broadcaster { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBot.Api/Helpers/ErrorExceptions/ApiException.cs: -------------------------------------------------------------------------------- 1 | namespace TwitchBot.Api.Helpers.ErrorExceptions 2 | { 3 | using System.Globalization; 4 | 5 | // custom exception class for throwing application specific exceptions (e.g. for validation) 6 | // that can be caught and handled within the application 7 | public class ApiException : Exception 8 | { 9 | public ApiException() : base() { } 10 | 11 | public ApiException(string message) : base(message) { } 12 | 13 | public ApiException(string message, params object[] args) 14 | : base(string.Format(CultureInfo.CurrentCulture, message, args)) { } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotShared/Models/RemindUser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace TwitchBotShared.Models 4 | { 5 | public class RemindUser 6 | { 7 | public int Id { get; set; } 8 | public int? GameId { get; set; } 9 | public bool[] IsReminderDay { get; set; } 10 | public TimeSpan? TimeOfEvent { get; set; } 11 | public int?[] ReminderSeconds { get; set; } 12 | public int? RemindEveryMin { get; set; } 13 | public string Message { get; set; } 14 | public DateTime? ExpirationDate { get; set; } 15 | public bool IsCountdownEvent { get; set; } 16 | public bool HasCountdownTicker { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBot.Api/Helpers/ErrorExceptions/NotFoundException.cs: -------------------------------------------------------------------------------- 1 | namespace TwitchBot.Api.Helpers.ErrorExceptions 2 | { 3 | using System.Globalization; 4 | 5 | // custom exception class for throwing application specific exceptions (e.g. for validation) 6 | // that can be caught and handled within the application 7 | public class NotFoundException : Exception 8 | { 9 | public NotFoundException() : base() { } 10 | 11 | public NotFoundException(string message) : base(message) { } 12 | 13 | public NotFoundException(string message, params object[] args) 14 | : base(string.Format(CultureInfo.CurrentCulture, message, args)) { } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/SqlScripts/StoredProcedures/dbo.GetPartyUpRequestList.sql: -------------------------------------------------------------------------------- 1 | CREATE PROCEDURE [dbo].[GetPartyUpRequestList] 2 | @GameId INT, 3 | @BroadcasterId INT 4 | AS 5 | BEGIN 6 | -- SET NOCOUNT ON added to prevent extra result sets from 7 | -- interfering with SELECT statements. 8 | SET NOCOUNT ON 9 | 10 | SELECT pur.Id AS PartyRequestId 11 | , pur.Username 12 | , pu.PartyMemberName 13 | , pur.PartyMemberId 14 | , pur.TimeRequested 15 | FROM dbo.PartyUpRequest AS pur 16 | INNER JOIN dbo.PartyUp AS pu 17 | ON pur.PartyMemberId = pu.Id 18 | WHERE GameId = @GameId AND BroadcasterId = @BroadcasterId 19 | ORDER BY pur.TimeRequested 20 | END 21 | GO 22 | 23 | 24 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotShared/Extensions/IListExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace TwitchBotShared.Extensions 5 | { 6 | public static class IListExtensions 7 | { 8 | public static void Shuffle(this IList list) 9 | { 10 | Random rnd = new Random(); 11 | 12 | for (var i = 0; i < list.Count - 1; i++) 13 | list.Swap(i, rnd.Next(i, list.Count)); 14 | } 15 | 16 | public static void Swap(this IList list, int i, int j) 17 | { 18 | var temp = list[i]; 19 | list[i] = list[j]; 20 | list[j] = temp; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/Models/PartyUp.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace TwitchBotDb.Models 4 | { 5 | public class PartyUp 6 | { 7 | public PartyUp() 8 | { 9 | PartyUpRequests = new HashSet(); 10 | } 11 | 12 | public int Id { get; set; } 13 | public string PartyMemberName { get; set; } 14 | public int GameId { get; set; } 15 | public int BroadcasterId { get; set; } 16 | 17 | public virtual Broadcaster Broadcaster { get; set; } 18 | public virtual TwitchGameCategory Game { get; set; } 19 | public virtual ICollection PartyUpRequests { get; set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/Repositories/GameDirectoryRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | using TwitchBotDb.Models; 4 | 5 | namespace TwitchBotDb.Repositories 6 | { 7 | public class GameDirectoryRepository 8 | { 9 | private readonly string _twitchBotApiLink; 10 | 11 | public GameDirectoryRepository(string twitchBotApiLink) 12 | { 13 | _twitchBotApiLink = twitchBotApiLink; 14 | } 15 | 16 | public async Task GetGameIdAsync(string gameTitle) 17 | { 18 | return await ApiBotRequest.GetExecuteAsync(_twitchBotApiLink + $"twitchgamecategories/get?title={gameTitle}"); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBot.Api/Helpers/ExtendedControllerBase.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | 3 | namespace TwitchBot.Api.Helpers 4 | { 5 | public class ExtendedControllerBase : ControllerBase 6 | { 7 | /// 8 | /// Gets a value that indicates whether any model state values in this model state dictionary is 9 | /// invalid or not validated. 10 | /// 11 | /// Produces a StatusCodes.Status400BadRequest response if ModelState is not valid; 12 | /// otherwise return null 13 | protected BadRequestObjectResult? IsModelStateValid() 14 | { 15 | if (!ModelState.IsValid) 16 | { 17 | return BadRequest(ModelState); 18 | } 19 | 20 | return null; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/Services/DiscordSelfAssignRoleService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | using TwitchBotDb.Models; 4 | using TwitchBotDb.Repositories; 5 | 6 | namespace TwitchBotDb.Services 7 | { 8 | public class DiscordSelfAssignRoleService 9 | { 10 | private readonly DiscordSelfAssignRoleRepository _discordSelfAssignRoleDb; 11 | 12 | public DiscordSelfAssignRoleService(DiscordSelfAssignRoleRepository discordSelfAssignRoleDb) 13 | { 14 | _discordSelfAssignRoleDb = discordSelfAssignRoleDb; 15 | } 16 | 17 | public async Task GetDiscordRoleAsync(int broadcasterId, string serverName, string roleName) 18 | { 19 | return await _discordSelfAssignRoleDb.GetDiscordRoleAsync(broadcasterId, serverName, roleName); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/Repositories/DiscordSelfAssignRoleRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | using TwitchBotDb.Models; 4 | 5 | namespace TwitchBotDb.Repositories 6 | { 7 | public class DiscordSelfAssignRoleRepository 8 | { 9 | private readonly string _twitchBotApiLink; 10 | 11 | public DiscordSelfAssignRoleRepository(string twitchBotApiLink) 12 | { 13 | _twitchBotApiLink = twitchBotApiLink; 14 | } 15 | 16 | public async Task GetDiscordRoleAsync(int broadcasterId, string serverName, string roleName) 17 | { 18 | return await ApiBotRequest.GetExecuteAsync(_twitchBotApiLink 19 | + $"discordselfroleassign/get/{broadcasterId}?servername={serverName}&rolename={roleName}"); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/Services/QuoteService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | 4 | using TwitchBotDb.Models; 5 | using TwitchBotDb.Repositories; 6 | 7 | 8 | namespace TwitchBotDb.Services 9 | { 10 | public class QuoteService 11 | { 12 | private readonly QuoteRepository _quoteDb; 13 | 14 | public QuoteService(QuoteRepository quote) 15 | { 16 | _quoteDb = quote; 17 | } 18 | 19 | public async Task> GetQuotesAsync(int broadcasterId) 20 | { 21 | return await _quoteDb.GetQuotesAsync(broadcasterId); 22 | } 23 | 24 | public async Task AddQuoteAsync(string quote, string username, int broadcasterId) 25 | { 26 | await _quoteDb.AddQuoteAsync(quote, username, broadcasterId); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/Services/GameDirectoryService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | using TwitchBotDb.Models; 4 | using TwitchBotDb.Repositories; 5 | 6 | 7 | namespace TwitchBotDb.Services 8 | { 9 | public class GameDirectoryService 10 | { 11 | private readonly GameDirectoryRepository _gameDirectoryDb; 12 | 13 | public GameDirectoryService(GameDirectoryRepository gameDirectoryDb) 14 | { 15 | _gameDirectoryDb = gameDirectoryDb; 16 | } 17 | 18 | public async Task GetGameIdAsync(string gameTitle) 19 | { 20 | gameTitle = gameTitle.TrimEnd(); 21 | 22 | if (string.IsNullOrEmpty(gameTitle)) 23 | { 24 | return null; 25 | } 26 | 27 | return await _gameDirectoryDb.GetGameIdAsync(gameTitle); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/Models/CustomCommand.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace TwitchBotDb.Models 4 | { 5 | public class CustomCommand 6 | { 7 | public CustomCommand() 8 | { 9 | Whitelists = new HashSet(); 10 | } 11 | 12 | public int Id { get; set; } 13 | public string Name { get; set; } 14 | public string Message { get; set; } 15 | public bool IsSound { get; set; } 16 | public bool IsGlobalCooldown { get; set; } 17 | public int CooldownSec { get; set; } 18 | public int CurrencyCost { get; set; } 19 | public int? GameId { get; set; } 20 | public int BroadcasterId { get; set; } 21 | 22 | public virtual Broadcaster Broadcaster { get; set; } 23 | public virtual TwitchGameCategory Game { get; set; } 24 | public virtual ICollection Whitelists { get; set; } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBot.Api/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:14396", 8 | "sslPort": 44320 9 | } 10 | }, 11 | "profiles": { 12 | "TwitchBot.Api": { 13 | "commandName": "Project", 14 | "launchBrowser": true, 15 | "launchUrl": "swagger", 16 | "environmentVariables": { 17 | "ASPNETCORE_ENVIRONMENT": "Development" 18 | }, 19 | "hotReloadEnabled": false, 20 | "applicationUrl": "https://localhost:7062", 21 | "dotnetRunMessages": true 22 | }, 23 | "IIS Express": { 24 | "commandName": "IISExpress", 25 | "launchBrowser": true, 26 | "launchUrl": "swagger", 27 | "environmentVariables": { 28 | "ASPNETCORE_ENVIRONMENT": "Development" 29 | } 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /TwitchBot/TwitchBotShared/Models/JSON/ChatterInfoJSON.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | using Newtonsoft.Json; 4 | 5 | namespace TwitchBotShared.Models.JSON 6 | { 7 | public class Chatters 8 | { 9 | [JsonProperty("moderators")] 10 | public List Moderators { get; set; } 11 | [JsonProperty("staff")] 12 | public List Staff { get; set; } 13 | [JsonProperty("admins")] 14 | public List Admins { get; set; } 15 | [JsonProperty("global_mods")] 16 | public List GlobalMods { get; set; } 17 | [JsonProperty("viewers")] 18 | public List Viewers { get; set; } 19 | [JsonProperty("vips")] 20 | public List VIPs { get; set; } 21 | } 22 | 23 | public class ChatterInfoJSON 24 | { 25 | [JsonProperty("chatter_count")] 26 | public int ChatterCount { get; set; } 27 | [JsonProperty("chatters")] 28 | public Chatters Chatters { get; set; } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/SqlScripts/StoredProcedures/dbo.UpdateCreateBalance.sql: -------------------------------------------------------------------------------- 1 | CREATE PROCEDURE [dbo].[UpdateCreateBalance] 2 | @tvpUsernames dbo.Usernames READONLY, 3 | @intDeposit INT, 4 | @intBroadcasterID INT, 5 | @bitShowOutput BIT = 0 6 | AS 7 | CREATE TABLE #tblResult ( 8 | ActionType NVARCHAR(10), 9 | Username VARCHAR(30) NOT NULL, 10 | Wallet INT NOT NULL 11 | ); 12 | 13 | MERGE INTO dbo.Bank WITH (HOLDLOCK) AS target 14 | USING @tvpUsernames AS source 15 | ON target.Username = source.Username 16 | AND target.Broadcaster = @intBroadcasterID 17 | WHEN MATCHED THEN 18 | UPDATE SET target.Wallet = target.Wallet + @intDeposit 19 | WHEN NOT MATCHED BY TARGET THEN 20 | INSERT (Username, Wallet, Broadcaster) 21 | VALUES (source.Username, @intDeposit, @intBroadcasterID) 22 | OUTPUT $action ActionType, source.Username, inserted.Wallet 23 | INTO #tblResult (ActionType, Username, Wallet); 24 | 25 | IF @bitShowOutput = 1 26 | BEGIN 27 | SELECT * FROM #tblResult 28 | END 29 | 30 | DROP TABLE #tblResult 31 | GO 32 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/Models/TwitchGameCategory.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace TwitchBotDb.Models 4 | { 5 | public class TwitchGameCategory 6 | { 7 | public TwitchGameCategory() 8 | { 9 | BossFightBossStats = new HashSet(); 10 | CustomCommands = new HashSet(); 11 | InGameUsernames = new HashSet(); 12 | PartyUps = new HashSet(); 13 | Reminders = new HashSet(); 14 | } 15 | 16 | public int Id { get; set; } 17 | public string Title { get; set; } 18 | public bool Multiplayer { get; set; } 19 | 20 | public virtual ICollection BossFightBossStats { get; set; } 21 | public virtual ICollection CustomCommands { get; set; } 22 | public virtual ICollection InGameUsernames { get; set; } 23 | public virtual ICollection PartyUps { get; set; } 24 | public virtual ICollection Reminders { get; set; } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotShared/Threads/PingSender.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | 3 | using TwitchBotShared.ClientLibraries; 4 | 5 | namespace TwitchBotShared.Threads 6 | { 7 | /// 8 | /// Class that sends PING to irc server every 5 minutes 9 | /// 10 | public class PingSender 11 | { 12 | private readonly IrcClient _irc; 13 | private readonly Thread pingSender; 14 | 15 | public PingSender(IrcClient irc) 16 | { 17 | _irc = irc; 18 | pingSender = new Thread (new ThreadStart(this.Run)); 19 | } 20 | 21 | public void Start() 22 | { 23 | pingSender.IsBackground = true; 24 | pingSender.Start(); 25 | } 26 | 27 | /// 28 | /// Send PING to irc server every 5 minutes 29 | /// 30 | private void Run() 31 | { 32 | while (true) 33 | { 34 | _irc.SendIrcMessage("PING irc.twitch.tv"); 35 | Thread.Sleep(300000); // 5 minutes 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotShared/Models/JSON/FollowerJSON.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | using Newtonsoft.Json; 4 | 5 | namespace TwitchBotShared.Models.JSON 6 | { 7 | public class FollowerJSON 8 | { 9 | [JsonProperty("from_id")] 10 | public string FromId { get; set; } 11 | 12 | [JsonProperty("from_login")] 13 | public string FromLogin { get; set; } 14 | 15 | [JsonProperty("from_name")] 16 | public string FromName { get; set; } 17 | 18 | [JsonProperty("to_id")] 19 | public string ToId { get; set; } 20 | 21 | [JsonProperty("to_name")] 22 | public string ToName { get; set; } 23 | 24 | [JsonProperty("followed_at")] 25 | public string FollowedAt { get; set; } 26 | } 27 | 28 | public class RootFollowerJSON 29 | { 30 | [JsonProperty("total")] 31 | public int Total { get; set; } 32 | 33 | [JsonProperty("data")] 34 | public List Followers { get; set; } 35 | 36 | [JsonProperty("pagination")] 37 | public Pagination Pagination { get; set; } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/Repositories/QuoteRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | 4 | using TwitchBotDb.Models; 5 | 6 | namespace TwitchBotDb.Repositories 7 | { 8 | public class QuoteRepository 9 | { 10 | private readonly string _twitchBotApiLink; 11 | 12 | public QuoteRepository(string twitchBotApiLink) 13 | { 14 | _twitchBotApiLink = twitchBotApiLink; 15 | } 16 | 17 | public async Task> GetQuotesAsync(int broadcasterId) 18 | { 19 | return await ApiBotRequest.GetExecuteAsync>(_twitchBotApiLink + $"quotes/get/{broadcasterId}"); 20 | } 21 | 22 | public async Task AddQuoteAsync(string quote, string username, int broadcasterId) 23 | { 24 | Quote freshQuote = new Quote 25 | { 26 | Username = username, 27 | UserQuote = quote, 28 | BroadcasterId = broadcasterId 29 | }; 30 | 31 | await ApiBotRequest.PostExecuteAsync(_twitchBotApiLink + $"quotes/create", freshQuote); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotShared/ClientLibraries/Singletons/RouletteSingleton.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | using TwitchBotShared.Models; 5 | 6 | namespace TwitchBotShared.ClientLibraries.Singletons 7 | { 8 | public class RouletteSingleton 9 | { 10 | /* Singleton Instance */ 11 | private static volatile RouletteSingleton _instance; 12 | private static object _syncRoot = new Object(); 13 | 14 | public List RouletteUsers { get; set; } = new List(); 15 | 16 | private RouletteSingleton() { } 17 | 18 | public static RouletteSingleton Instance 19 | { 20 | get 21 | { 22 | // first check 23 | if (_instance == null) 24 | { 25 | lock (_syncRoot) 26 | { 27 | // second check 28 | if (_instance == null) 29 | _instance = new RouletteSingleton(); 30 | } 31 | } 32 | 33 | return _instance; 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/TwitchBotDb.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | AnyCPU 6 | 7 | 8 | 9 | 10 | 11 | all 12 | runtime; build; native; contentfiles; analyzers; buildtransitive 13 | 14 | 15 | 16 | 17 | all 18 | runtime; build; native; contentfiles; analyzers 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotShared/TwitchBotShared.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | AnyCPU 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotShared/ClientLibraries/Singletons/DelayedMessageSingleton.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | using TwitchBotShared.Models; 5 | 6 | namespace TwitchBotShared.ClientLibraries.Singletons 7 | { 8 | public class DelayedMessageSingleton 9 | { 10 | /* Singleton Instance */ 11 | private static volatile DelayedMessageSingleton _instance; 12 | private static object _syncRoot = new Object(); 13 | 14 | public List DelayedMessages { get; set; } = new List(); 15 | 16 | private DelayedMessageSingleton() { } 17 | 18 | public static DelayedMessageSingleton Instance 19 | { 20 | get 21 | { 22 | // first check 23 | if (_instance == null) 24 | { 25 | lock (_syncRoot) 26 | { 27 | // second check 28 | if (_instance == null) 29 | _instance = new DelayedMessageSingleton(); 30 | } 31 | } 32 | 33 | return _instance; 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBot.Api/TwitchBot.Api.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | enable 6 | enable 7 | False 8 | 9 | 10 | 11 | 12 | 13 | 14 | all 15 | runtime; build; native; contentfiles; analyzers; buildtransitive 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/Models/Reminder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace TwitchBotDb.Models 4 | { 5 | public class Reminder 6 | { 7 | public int Id { get; set; } 8 | public bool Sunday { get; set; } 9 | public bool Monday { get; set; } 10 | public bool Tuesday { get; set; } 11 | public bool Wednesday { get; set; } 12 | public bool Thursday { get; set; } 13 | public bool Friday { get; set; } 14 | public bool Saturday { get; set; } 15 | public int? ReminderSec1 { get; set; } 16 | public int? ReminderSec2 { get; set; } 17 | public int? ReminderSec3 { get; set; } 18 | public int? ReminderSec4 { get; set; } 19 | public int? ReminderSec5 { get; set; } 20 | public int? RemindEveryMin { get; set; } 21 | public TimeSpan? TimeOfEventUtc { get; set; } 22 | public DateTime? ExpirationDateUtc { get; set; } 23 | public bool IsCountdownEvent { get; set; } 24 | public bool HasCountdownTicker { get; set; } 25 | public string Message { get; set; } 26 | public int BroadcasterId { get; set; } 27 | public int? GameId { get; set; } 28 | 29 | public virtual Broadcaster Broadcaster { get; set; } 30 | public virtual TwitchGameCategory Game { get; set; } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/Models/BossFightClassStats.cs: -------------------------------------------------------------------------------- 1 | namespace TwitchBotDb.Models 2 | { 3 | public class BossFightClassStats 4 | { 5 | public int Id { get; set; } 6 | public int SettingsId { get; set; } 7 | public int ViewerAttack { get; set; } 8 | public int ViewerDefense { get; set; } 9 | public int ViewerEvasion { get; set; } 10 | public int ViewerHealth { get; set; } 11 | public int FollowerAttack { get; set; } 12 | public int FollowerDefense { get; set; } 13 | public int FollowerEvasion { get; set; } 14 | public int FollowerHealth { get; set; } 15 | public int RegularAttack { get; set; } 16 | public int RegularDefense { get; set; } 17 | public int RegularEvasion { get; set; } 18 | public int RegularHealth { get; set; } 19 | public int ModeratorAttack { get; set; } 20 | public int ModeratorDefense { get; set; } 21 | public int ModeratorEvasion { get; set; } 22 | public int ModeratorHealth { get; set; } 23 | public int SubscriberAttack { get; set; } 24 | public int SubscriberDefense { get; set; } 25 | public int SubscriberEvasion { get; set; } 26 | public int SubscriberHealth { get; set; } 27 | 28 | public virtual BossFightSetting Settings { get; set; } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotShared/Models/JSON/ChannelJSON.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | using Newtonsoft.Json; 5 | 6 | namespace TwitchBotShared.Models.JSON 7 | { 8 | public class ChannelJSON 9 | { 10 | //[JsonProperty("broadcaster_id")] 11 | //public string BroadcasterId { get; set; } 12 | 13 | //[JsonProperty("broadcaster_login")] 14 | //public string BroadcasterLogin { get; set; } 15 | 16 | //[JsonProperty("broadcaster_name")] 17 | //public string BroadcasterName { get; set; } 18 | 19 | //[JsonProperty("broadcaster_language")] 20 | //public string BroadcasterLanguage { get; set; } 21 | 22 | //[JsonProperty("game_id")] 23 | //public string GameId { get; set; } 24 | 25 | [JsonProperty("game_name")] 26 | public string GameName { get; set; } 27 | 28 | [JsonProperty("title")] 29 | public string Title { get; set; } 30 | 31 | internal ChannelJSON First() 32 | { 33 | throw new NotImplementedException(); 34 | } 35 | 36 | //[JsonProperty("delay")] 37 | //public int Delay { get; set; } 38 | } 39 | 40 | public class RootChannelJSON 41 | { 42 | [JsonProperty("data")] 43 | public List Channels { get; set; } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotShared/Models/JSON/UserJSON.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | using Newtonsoft.Json; 4 | 5 | namespace TwitchBotShared.Models.JSON 6 | { 7 | public class UserJSON 8 | { 9 | [JsonProperty("id")] 10 | public string Id { get; set; } 11 | 12 | [JsonProperty("login")] 13 | public string Login { get; set; } 14 | 15 | [JsonProperty("display_name")] 16 | public string DisplayName { get; set; } 17 | 18 | //[JsonProperty("type")] 19 | //public string Type { get; set; } 20 | 21 | [JsonProperty("broadcaster_type")] 22 | public string BroadcasterType { get; set; } 23 | 24 | //[JsonProperty("description")] 25 | //public string Description { get; set; } 26 | 27 | //[JsonProperty("profile_image_url")] 28 | //public string ProfileImageUrl { get; set; } 29 | 30 | //[JsonProperty("offline_image_url")] 31 | //public string OfflineImageUrl { get; set; } 32 | 33 | //[JsonProperty("view_count")] 34 | //public int ViewCount { get; set; } 35 | 36 | //[JsonProperty("created_at")] 37 | //public string CreatedAt { get; set; } 38 | } 39 | 40 | public class RootUserJSON 41 | { 42 | [JsonProperty("data")] 43 | public List Users { get; set; } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBot.Api/Controllers/ErrorLogsController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | 3 | using TwitchBot.Api.Helpers; 4 | using TwitchBotDb.Context; 5 | using TwitchBotDb.Models; 6 | 7 | namespace TwitchBot.Api.Controllers 8 | { 9 | [Route("api/[controller]/[action]")] 10 | [ApiController] 11 | public class ErrorLogsController : ExtendedControllerBase 12 | { 13 | private readonly SimpleBotContext _context; 14 | 15 | public ErrorLogsController(SimpleBotContext context) 16 | { 17 | _context = context; 18 | } 19 | 20 | // POST: api/errorlogs/create 21 | /* Body (JSON): 22 | { 23 | "errorTime": "2018-01-01T15:30:00", 24 | "errorLine": 9001, 25 | "errorClass": "SomeClass", 26 | "errorMethod": "SomeMethod", 27 | "errorMsg": "Some Error Message", 28 | "broadcaster": 2, 29 | "command": "!somecmd", 30 | "userMsg": "n/a" 31 | } 32 | */ 33 | [HttpPost] 34 | public async Task Create([FromBody] ErrorLog errorLog) 35 | { 36 | IsModelStateValid(); 37 | 38 | _context.ErrorLogs.Add(errorLog); 39 | await _context.SaveChangesAsync(); 40 | 41 | return NoContent(); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotConsoleApp/TwitchBotConsoleApp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net6.0 6 | AnyCPU 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | PreserveNewest 29 | Designer 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/Services/InGameUsernameService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | using TwitchBotDb.Models; 4 | using TwitchBotDb.Repositories; 5 | 6 | 7 | namespace TwitchBotDb.Services 8 | { 9 | public class InGameUsernameService 10 | { 11 | private readonly InGameUsernameRepository _ignDb; 12 | 13 | public InGameUsernameService(InGameUsernameRepository ignDb) 14 | { 15 | _ignDb = ignDb; 16 | } 17 | 18 | public async Task GetInGameUsernameAsync(int broadcasterId, TwitchGameCategory game = null) 19 | { 20 | int? gameId = game?.Id ?? null; 21 | return await _ignDb.GetInGameUsernameAsync(gameId, broadcasterId); 22 | } 23 | 24 | public async Task UpdateInGameUsernameAsync(int id, int broadcasterId, InGameUsername ign) 25 | { 26 | await _ignDb.UpdateInGameUsernameAsync(id, broadcasterId, ign); 27 | } 28 | 29 | public async Task CreateInGameUsernameAsync(int? gameId, int broadcasterId, string message) 30 | { 31 | await _ignDb.CreateInGameUsernameAsync(gameId, broadcasterId, message); 32 | } 33 | 34 | public async Task DeleteInGameUsernameAsync(int id, int broadcasterId) 35 | { 36 | return await _ignDb.DeleteInGameUsernameAsync(id, broadcasterId); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBot.Api/Controllers/TwitchGameCategoriesController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.EntityFrameworkCore; 3 | 4 | using TwitchBot.Api.Helpers; 5 | using TwitchBot.Api.Helpers.ErrorExceptions; 6 | using TwitchBotDb.Context; 7 | using TwitchBotDb.Models; 8 | 9 | namespace TwitchBot.Api.Controllers 10 | { 11 | [Route("api/[controller]/[action]")] 12 | [ApiController] 13 | public class TwitchGameCategoriesController : ExtendedControllerBase 14 | { 15 | private readonly SimpleBotContext _context; 16 | 17 | public TwitchGameCategoriesController(SimpleBotContext context) 18 | { 19 | _context = context; 20 | } 21 | 22 | // GET: api/twitchgamecategories/get 23 | // GET: api/twitchgamecategories/get?title=IRL 24 | [HttpGet()] 25 | public async Task Get([FromQuery] string? title = null) 26 | { 27 | IsModelStateValid(); 28 | 29 | if (string.IsNullOrEmpty(title)) 30 | { 31 | return Ok(_context.TwitchGameCategories); 32 | } 33 | 34 | TwitchGameCategory? gameList = await _context.TwitchGameCategories 35 | .FirstOrDefaultAsync(m => m.Title == title); 36 | 37 | if (gameList == null) 38 | { 39 | throw new NotFoundException("Game list cannot be found"); 40 | } 41 | 42 | return Ok(gameList); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBot.Api/Controllers/DiscordSelfRoleAssignController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.EntityFrameworkCore; 3 | 4 | using TwitchBot.Api.Helpers; 5 | using TwitchBot.Api.Helpers.ErrorExceptions; 6 | using TwitchBotDb.Context; 7 | using TwitchBotDb.Models; 8 | 9 | namespace TwitchBot.Api.Controllers 10 | { 11 | [Route("api/[controller]/[action]")] 12 | [Produces("application/json")] 13 | public class DiscordSelfRoleAssignController : ExtendedControllerBase 14 | { 15 | private readonly SimpleBotContext _context; 16 | 17 | public DiscordSelfRoleAssignController(SimpleBotContext context) 18 | { 19 | _context = context; 20 | } 21 | 22 | // GET: api/discordselfroleassign/get/2?servername=nightlyrng&rolename=minecraft 23 | [HttpGet("{broadcasterId}")] 24 | public async Task Get(int broadcasterId, [FromQuery] string serverName, [FromQuery] string roleName) 25 | { 26 | IsModelStateValid(); 27 | 28 | DiscordSelfRoleAssign? role = await _context.DiscordSelfRoleAssigns 29 | .FirstOrDefaultAsync(m => m.BroadcasterId == broadcasterId 30 | && m.ServerName == serverName 31 | && m.RoleName == roleName); 32 | 33 | if (role == null) 34 | { 35 | throw new NotFoundException("Discord self assign role does not exist"); 36 | } 37 | 38 | return Ok(role); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/Services/SongRequestSettingService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | using TwitchBotDb.Models; 4 | using TwitchBotDb.Repositories; 5 | 6 | namespace TwitchBotDb.Services 7 | { 8 | public class SongRequestSettingService 9 | { 10 | private readonly SongRequestSettingRepository _songRequestSettingDb; 11 | 12 | public SongRequestSettingService(SongRequestSettingRepository songRequestSettingDb) 13 | { 14 | _songRequestSettingDb = songRequestSettingDb; 15 | } 16 | 17 | public async Task GetSongRequestSettingAsync(int broadcasterId) 18 | { 19 | return await _songRequestSettingDb.GetSongRequestSettingAsync(broadcasterId); 20 | } 21 | 22 | public async Task CreateSongRequestSettingAsync(string requestPlaylistId, string personalPlaylistId, int broadcasterId) 23 | { 24 | if (personalPlaylistId == "") 25 | personalPlaylistId = null; 26 | 27 | return await _songRequestSettingDb.CreateSongRequestSettingAsync(requestPlaylistId, personalPlaylistId, broadcasterId); 28 | } 29 | 30 | public async Task UpdateSongRequestSettingAsync(string requestPlaylistId, string personalPlaylistId, int broadcasterId, bool djMode) 31 | { 32 | if (personalPlaylistId == "") 33 | personalPlaylistId = null; 34 | 35 | await _songRequestSettingDb.UpdateSongRequestSettingAsync(requestPlaylistId, personalPlaylistId, broadcasterId, djMode); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/Services/BankService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | 4 | using TwitchBotDb.DTO; 5 | using TwitchBotDb.Models; 6 | using TwitchBotDb.Repositories; 7 | 8 | namespace TwitchBotDb.Services 9 | { 10 | public class BankService 11 | { 12 | private readonly BankRepository _bank; 13 | 14 | public BankService(BankRepository bank) 15 | { 16 | _bank = bank; 17 | } 18 | 19 | public async Task CreateAccountAsync(string recipient, int broadcasterId, int deposit) 20 | { 21 | await _bank.CreateAccountAsync(recipient, broadcasterId, deposit); 22 | } 23 | 24 | public async Task UpdateFundsAsync(string walletOwner, int broadcasterId, int newWalletBalance) 25 | { 26 | await _bank.UpdateAccountAsync(walletOwner, broadcasterId, newWalletBalance); 27 | } 28 | 29 | public async Task> UpdateCreateBalanceAsync(List usernameList, int broadcasterId, int deposit, bool showOutput = false) 30 | { 31 | return await _bank.UpdateCreateBalance(usernameList, broadcasterId, deposit, showOutput); 32 | } 33 | 34 | public async Task CheckBalanceAsync(string username, int broadcasterId) 35 | { 36 | return await _bank.CheckBalanceAsync(username, broadcasterId); 37 | } 38 | 39 | public async Task> GetCurrencyLeaderboardAsync(string broadcasterName, int broadcasterId, string botName) 40 | { 41 | return await _bank.GetCurrencyLeaderboardAsync(broadcasterName, broadcasterId, botName); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/Models/BossFightSetting.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace TwitchBotDb.Models 4 | { 5 | public class BossFightSetting 6 | { 7 | public BossFightSetting() 8 | { 9 | BossFightBossStats = new HashSet(); 10 | BossFightClassStats = new HashSet(); 11 | } 12 | 13 | public int Id { get; set; } 14 | public int CooldownPeriodMin { get; set; } 15 | public int EntryPeriodSec { get; set; } 16 | public string EntryMessage { get; set; } 17 | public int Cost { get; set; } 18 | public string CooldownEntry { get; set; } 19 | public string CooldownOver { get; set; } 20 | public string NextLevelMessage2 { get; set; } 21 | public string NextLevelMessage3 { get; set; } 22 | public string NextLevelMessage4 { get; set; } 23 | public string NextLevelMessage5 { get; set; } 24 | public string GameStart { get; set; } 25 | public string ResultsMessage { get; set; } 26 | public string SingleUserSuccess { get; set; } 27 | public string SingleUserFail { get; set; } 28 | public string Success100 { get; set; } 29 | public string Success34 { get; set; } 30 | public string Success1 { get; set; } 31 | public string Success0 { get; set; } 32 | public int BroadcasterId { get; set; } 33 | 34 | public virtual Broadcaster Broadcaster { get; set; } 35 | public virtual ICollection BossFightBossStats { get; set; } 36 | public virtual ICollection BossFightClassStats { get; set; } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/Repositories/InGameUsernameRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | using TwitchBotDb.Models; 4 | 5 | namespace TwitchBotDb.Repositories 6 | { 7 | public class InGameUsernameRepository 8 | { 9 | private readonly string _twitchBotApiLink; 10 | 11 | public InGameUsernameRepository(string twitchBotApiLink) 12 | { 13 | _twitchBotApiLink = twitchBotApiLink; 14 | } 15 | 16 | public async Task GetInGameUsernameAsync(int? gameId, int broadcasterId) 17 | { 18 | return await ApiBotRequest.GetExecuteAsync(_twitchBotApiLink + $"ingameusernames/get/{broadcasterId}?gameid={gameId}"); 19 | } 20 | 21 | public async Task UpdateInGameUsernameAsync(int id, int broadcasterId, InGameUsername ign) 22 | { 23 | await ApiBotRequest.PutExecuteAsync(_twitchBotApiLink + $"ingameusernames/update/{broadcasterId}?id={id}", ign); 24 | } 25 | 26 | public async Task CreateInGameUsernameAsync(int? gameId, int broadcasterId, string message) 27 | { 28 | InGameUsername ign = new InGameUsername 29 | { 30 | GameId = gameId, 31 | BroadcasterId = broadcasterId, 32 | Message = message 33 | }; 34 | 35 | await ApiBotRequest.PostExecuteAsync(_twitchBotApiLink + $"ingameusernames/create", ign); 36 | } 37 | 38 | public async Task DeleteInGameUsernameAsync(int id, int broadcasterId) 39 | { 40 | return await ApiBotRequest.DeleteExecuteAsync(_twitchBotApiLink + $"ingameusernames/delete/{broadcasterId}?id={id}"); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/Repositories/ManualSongRequestRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | 4 | using TwitchBotDb.Models; 5 | 6 | namespace TwitchBotDb.Repositories 7 | { 8 | public class ManualSongRequestRepository 9 | { 10 | private readonly string _twitchBotApiLink; 11 | 12 | public ManualSongRequestRepository(string twitchBotApiLink) 13 | { 14 | _twitchBotApiLink = twitchBotApiLink; 15 | } 16 | 17 | public async Task AddSongRequestAsync(string songRequestName, string username, int broadcasterId) 18 | { 19 | SongRequest songRequest = new SongRequest 20 | { 21 | Name = songRequestName, 22 | Username = username, 23 | BroadcasterId = broadcasterId 24 | }; 25 | 26 | return await ApiBotRequest.PostExecuteAsync(_twitchBotApiLink + $"songrequests/create", songRequest); 27 | } 28 | 29 | public async Task> ListSongRequestsAsync(int broadcasterId) 30 | { 31 | return await ApiBotRequest.GetExecuteAsync>(_twitchBotApiLink + $"songrequests/get/{broadcasterId}"); 32 | } 33 | 34 | public async Task PopSongRequestAsync(int broadcasterId) 35 | { 36 | return await ApiBotRequest.DeleteExecuteAsync(_twitchBotApiLink + $"songrequests/delete/{broadcasterId}?popone=true"); 37 | } 38 | 39 | public async Task> ResetSongRequestsAsync(int broadcasterId) 40 | { 41 | return await ApiBotRequest.DeleteExecuteAsync>(_twitchBotApiLink + $"songrequests/delete/{broadcasterId}"); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotShared/Models/JSON/StreamJSON.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | using Newtonsoft.Json; 4 | 5 | namespace TwitchBotShared.Models.JSON 6 | { 7 | public class StreamJSON 8 | { 9 | //[JsonProperty("id")] 10 | //public string Id { get; set; } 11 | 12 | //[JsonProperty("user_id")] 13 | //public string UserId { get; set; } 14 | 15 | //[JsonProperty("user_login")] 16 | //public string UserLogin { get; set; } 17 | 18 | //[JsonProperty("user_name")] 19 | //public string UserName { get; set; } 20 | 21 | //[JsonProperty("game_id")] 22 | //public string GameId { get; set; } 23 | 24 | [JsonProperty("game_name")] 25 | public string GameName { get; set; } 26 | 27 | //[JsonProperty("type")] 28 | //public string Type { get; set; } 29 | 30 | [JsonProperty("title")] 31 | public string Title { get; set; } 32 | 33 | //[JsonProperty("viewer_count")] 34 | //public int ViewerCount { get; set; } 35 | 36 | [JsonProperty("started_at")] 37 | public string StartedAt { get; set; } 38 | 39 | //[JsonProperty("language")] 40 | //public string Language { get; set; } 41 | 42 | //[JsonProperty("thumbnail_url")] 43 | //public string ThumbnailUrl { get; set; } 44 | 45 | //[JsonProperty("tag_ids")] 46 | //public List TagIds { get; set; } 47 | 48 | //[JsonProperty("is_mature")] 49 | //public bool IsMature { get; set; } 50 | } 51 | 52 | public class RootStreamJSON 53 | { 54 | [JsonProperty("data")] 55 | public List Stream { get; set; } 56 | 57 | [JsonProperty("pagination")] 58 | public Pagination Pagination { get; set; } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotShared/Models/JSON/SubscriptionJSON.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | using Newtonsoft.Json; 4 | 5 | namespace TwitchBotShared.Models.JSON 6 | { 7 | public class SubscriptionJSON 8 | { 9 | [JsonProperty("broadcaster_id")] 10 | public string BroadcasterId { get; set; } 11 | 12 | [JsonProperty("broadcaster_login")] 13 | public string BroadcasterLogin { get; set; } 14 | 15 | [JsonProperty("broadcaster_name")] 16 | public string BroadcasterName { get; set; } 17 | 18 | [JsonProperty("gifter_id")] 19 | public string GifterId { get; set; } 20 | 21 | [JsonProperty("gifter_login")] 22 | public string GifterLogin { get; set; } 23 | 24 | [JsonProperty("gifter_name")] 25 | public string GifterName { get; set; } 26 | 27 | [JsonProperty("is_gift")] 28 | public bool IsGift { get; set; } 29 | 30 | [JsonProperty("plan_name")] 31 | public string PlanName { get; set; } 32 | 33 | [JsonProperty("tier")] 34 | public string Tier { get; set; } 35 | 36 | [JsonProperty("user_id")] 37 | public string UserId { get; set; } 38 | 39 | [JsonProperty("user_name")] 40 | public string UserName { get; set; } 41 | 42 | [JsonProperty("user_login")] 43 | public string UserLogin { get; set; } 44 | } 45 | 46 | public class RootSubscriptionJSON 47 | { 48 | [JsonProperty("data")] 49 | public List Subscriptions { get; set; } 50 | 51 | [JsonProperty("pagination")] 52 | public Pagination Pagination { get; set; } 53 | 54 | [JsonProperty("points")] 55 | public int Points { get; set; } 56 | 57 | [JsonProperty("total")] 58 | public int Total { get; set; } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotShared/Extensions/ProcessExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace TwitchBotShared.Extensions 5 | { 6 | public static class ProcessExtensions 7 | { 8 | /// 9 | /// Used to navigate to a URL based on the OS platform 10 | /// 11 | /// 12 | /// 13 | public static void StartUrlCrossPlatform(this Process process, string url) 14 | { 15 | try 16 | { 17 | process.Start(); 18 | } 19 | catch 20 | { 21 | // Reference: https://stackoverflow.com/a/4580317/2113548 22 | // Hack because of this: https://github.com/dotnet/corefx/issues/10361 23 | if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) 24 | { 25 | url = url.Replace("&", "^&"); 26 | 27 | process.StartInfo = new ProcessStartInfo("cmd", $"/c start {url}") { CreateNoWindow = true }; 28 | process.Start(); 29 | } 30 | else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) 31 | { 32 | process.StartInfo.FileName = "xdg-open"; 33 | process.StartInfo.Arguments = url; 34 | process.Start(); 35 | } 36 | else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) 37 | { 38 | process.StartInfo.FileName = "open"; 39 | process.StartInfo.Arguments = url; 40 | process.Start(); 41 | } 42 | else 43 | { 44 | throw; 45 | } 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBot.Api/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.OpenApi.Models; 3 | using TwitchBot.Api.Helpers.ErrorExceptions; 4 | using TwitchBotDb.Context; 5 | 6 | #region Builder 7 | WebApplicationBuilder builder = WebApplication.CreateBuilder(args); 8 | 9 | // Add services to the container. 10 | builder.Services.AddControllers(); 11 | builder.Services.AddDistributedMemoryCache(); 12 | 13 | // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle 14 | builder.Services.AddSwaggerGen(); 15 | 16 | builder.Services.AddDbContext(options => 17 | { 18 | options.UseSqlServer(builder.Configuration.GetConnectionString("SimpleBotContext")); 19 | }); 20 | 21 | builder.Services.AddSwaggerGen(c => 22 | { 23 | c.SwaggerDoc("v1", new OpenApiInfo 24 | { 25 | Title = "Twitch Bot API", 26 | Version = "v2022.07.26", 27 | Description = "Back-end of chat bot specific calls", 28 | Contact = new OpenApiContact 29 | { 30 | Name = "GitHub", 31 | Email = string.Empty, 32 | Url = new Uri("https://github.com/SimpleSandman/TwitchBot") 33 | } 34 | }); 35 | }); 36 | #endregion 37 | 38 | #region App 39 | WebApplication app = builder.Build(); 40 | 41 | // Configure the HTTP request pipeline. 42 | if (app.Environment.IsDevelopment()) 43 | { 44 | app.UseSwagger(); 45 | app.UseSwaggerUI(); 46 | app.UseDeveloperExceptionPage(); 47 | } 48 | else 49 | { 50 | app.UseExceptionHandler("/Error"); 51 | app.UseHsts(); 52 | } 53 | 54 | app.UseMiddleware(); // global error handler 55 | //app.UseRouting(); 56 | 57 | app.UseHttpsRedirection(); 58 | 59 | app.UseAuthorization(); 60 | 61 | app.MapControllers(); 62 | //app.UseEndpoints(endpoints => 63 | //{ 64 | // endpoints.MapControllers(); 65 | //}); 66 | 67 | app.Run(); 68 | #endregion -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/Services/SongRequestBlackListService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | 4 | using TwitchBotDb.Models; 5 | using TwitchBotDb.Repositories; 6 | 7 | 8 | namespace TwitchBotDb.Services 9 | { 10 | public class SongRequestBlacklistService 11 | { 12 | private readonly SongRequestBlacklistRepository _songRequestDb; 13 | 14 | public SongRequestBlacklistService(SongRequestBlacklistRepository songRequestDb) 15 | { 16 | _songRequestDb = songRequestDb; 17 | } 18 | 19 | public async Task> GetSongRequestIgnoreAsync(int broadcasterId) 20 | { 21 | return await _songRequestDb.GetSongRequestIgnoreAsync(broadcasterId); 22 | } 23 | 24 | public async Task IgnoreArtistAsync(string artist, int broadcasterId) 25 | { 26 | return await _songRequestDb.IgnoreArtistAsync(artist, broadcasterId); 27 | } 28 | 29 | public async Task IgnoreSongAsync(string title, string artist, int broadcasterId) 30 | { 31 | return await _songRequestDb.IgnoreSongAsync(title, artist, broadcasterId); 32 | } 33 | 34 | public async Task> AllowArtistAsync(string artist, int broadcasterId) 35 | { 36 | return await _songRequestDb.AllowArtistAsync(artist, broadcasterId); 37 | } 38 | 39 | public async Task AllowSongAsync(string title, string artist, int broadcasterId) 40 | { 41 | return await _songRequestDb.AllowSongAsync(title, artist, broadcasterId); 42 | } 43 | 44 | public async Task> ResetIgnoreListAsync(int broadcasterId) 45 | { 46 | return await _songRequestDb.ResetIgnoreListAsync(broadcasterId); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotShared/Models/JSON/ClipJSON.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | using Newtonsoft.Json; 4 | 5 | namespace TwitchBotShared.Models.JSON 6 | { 7 | public class ClipJSON 8 | { 9 | //[JsonProperty("id")] 10 | //public string Id { get; set; } 11 | 12 | //[JsonProperty("url")] 13 | //public string Url { get; set; } 14 | 15 | //[JsonProperty("embed_url")] 16 | //public string EmbedUrl { get; set; } 17 | 18 | //[JsonProperty("broadcaster_id")] 19 | //public string BroadcasterId { get; set; } 20 | 21 | [JsonProperty("broadcaster_name")] 22 | public string BroadcasterName { get; set; } 23 | 24 | //[JsonProperty("creator_id")] 25 | //public string CreatorId { get; set; } 26 | 27 | //[JsonProperty("creator_name")] 28 | //public string CreatorName { get; set; } 29 | 30 | //[JsonProperty("video_id")] 31 | //public string VideoId { get; set; } 32 | 33 | //[JsonProperty("game_id")] 34 | //public string GameId { get; set; } 35 | 36 | //[JsonProperty("language")] 37 | //public string Language { get; set; } 38 | 39 | //[JsonProperty("title")] 40 | //public string Title { get; set; } 41 | 42 | //[JsonProperty("view_count")] 43 | //public int ViewCount { get; set; } 44 | 45 | //[JsonProperty("created_at")] 46 | //public string CreatedAt { get; set; } 47 | 48 | //[JsonProperty("thumbnail_url")] 49 | //public string ThumbnailUrl { get; set; } 50 | 51 | //[JsonProperty("duration")] 52 | //public double Duration { get; set; } 53 | } 54 | 55 | public class RootClipJSON 56 | { 57 | [JsonProperty("data")] 58 | public List Clips { get; set; } 59 | 60 | [JsonProperty("pagination")] 61 | public Pagination Pagination { get; set; } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotShared/Extensions/TimeSpanExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace TwitchBotShared.Extensions 4 | { 5 | public static class TimeSpanExtensions 6 | { 7 | public static string ToReadableString(this TimeSpan span) 8 | { 9 | string formatted = string.Format("{0}{1}{2}{3}", 10 | span.Duration().Days > 0 ? string.Format("{0:0} day{1}, ", span.Days, span.Days == 1 ? String.Empty : "s") : string.Empty, 11 | span.Duration().Hours > 0 ? string.Format("{0:0} hour{1}, ", span.Hours, span.Hours == 1 ? String.Empty : "s") : string.Empty, 12 | span.Duration().Minutes > 0 ? string.Format("{0:0} minute{1}, ", span.Minutes, span.Minutes == 1 ? String.Empty : "s") : string.Empty, 13 | span.Duration().Seconds > 0 ? string.Format("{0:0} second{1}", span.Seconds, span.Seconds == 1 ? String.Empty : "s") : string.Empty); 14 | 15 | if (formatted.EndsWith(", ")) formatted = formatted.Substring(0, formatted.Length - 2); 16 | 17 | if (string.IsNullOrEmpty(formatted)) formatted = "0 seconds"; 18 | 19 | return formatted; 20 | } 21 | 22 | public static string ReformatTimeSpan(this TimeSpan ts) 23 | { 24 | string response = ""; 25 | 26 | // format minutes 27 | if (ts.Minutes < 1) 28 | response += $"[00:"; 29 | else if (ts.Minutes > 0 && ts.Minutes < 10) 30 | response += $"[0{ts.Minutes}:"; 31 | else 32 | response += $"[{ts.Minutes}:"; 33 | 34 | // format seconds 35 | if (ts.Seconds < 1) 36 | response += $"00]"; 37 | else if (ts.Seconds > 0 && ts.Seconds < 10) 38 | response += $"0{ts.Seconds}]"; 39 | else 40 | response += $"{ts.Seconds}]"; 41 | 42 | return response; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotShared/ClientLibraries/TwitterClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using CoreTweet; 4 | 5 | namespace TwitchBotShared.ClientLibraries 6 | { 7 | public class TwitterClient 8 | { 9 | private static volatile TwitterClient _instance; 10 | private static object _syncRoot = new Object(); 11 | 12 | private TwitterClient() { } 13 | 14 | public bool HasCredentials { get; set; } = false; 15 | public string ScreenName { get; set; } 16 | public Tokens Tokens { get; set; } 17 | 18 | public static TwitterClient Instance 19 | { 20 | get 21 | { 22 | // first check 23 | if (_instance == null) 24 | { 25 | lock (_syncRoot) 26 | { 27 | // second check 28 | if (_instance == null) 29 | _instance = new TwitterClient(); 30 | } 31 | } 32 | 33 | return _instance; 34 | } 35 | } 36 | 37 | public string SendTweet(string message) 38 | { 39 | try 40 | { 41 | if (message.Length <= 280) 42 | { 43 | Tokens.Statuses.Update(new { status = message }); 44 | return "Tweet successfully published!"; 45 | } 46 | else 47 | { 48 | int overCharLimit = message.Length - 280; 49 | return $"The message you attempted to tweet had {overCharLimit}" + 50 | " characters more than the 280 character limit. Please shorten your message and try again"; 51 | } 52 | } 53 | catch 54 | { 55 | return "Unknown error has occurred for your proposed tweet."; 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/Services/ManualSongRequestService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | 4 | using TwitchBotDb.Models; 5 | using TwitchBotDb.Repositories; 6 | 7 | namespace TwitchBotDb.Services 8 | { 9 | public class ManualSongRequestService 10 | { 11 | private readonly ManualSongRequestRepository _songRequestDb; 12 | 13 | public ManualSongRequestService(ManualSongRequestRepository songRequestDb) 14 | { 15 | _songRequestDb = songRequestDb; 16 | } 17 | 18 | public async Task AddSongRequestAsync(string songRequestName, string username, int broadcasterId) 19 | { 20 | return await _songRequestDb.AddSongRequestAsync(songRequestName, username, broadcasterId); 21 | } 22 | 23 | public async Task ListSongRequestsAsync(int broadcasterId) 24 | { 25 | List songRequests = await _songRequestDb.ListSongRequestsAsync(broadcasterId); 26 | 27 | if (songRequests == null || songRequests.Count == 0) 28 | { 29 | return "No song requests have been made"; 30 | } 31 | 32 | string message = "Current list of requested songs: "; 33 | 34 | foreach (SongRequest member in songRequests) 35 | { 36 | message += $"\"{member.Name}\" ({member.Username}) >< "; 37 | } 38 | 39 | message = message.Substring(0, message.Length - 4); 40 | 41 | return message; 42 | } 43 | 44 | public async Task PopSongRequestAsync(int broadcasterId) 45 | { 46 | return await _songRequestDb.PopSongRequestAsync(broadcasterId); 47 | } 48 | 49 | public async Task> ResetSongRequestsAsync(int broadcasterId) 50 | { 51 | return await _songRequestDb.ResetSongRequestsAsync(broadcasterId); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBot.Api/Helpers/ErrorExceptions/ErrorHandlerMiddleware.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using System.Text.Json; 3 | 4 | namespace TwitchBot.Api.Helpers.ErrorExceptions 5 | { 6 | // Reference: https://jasonwatmore.com/post/2022/03/15/net-6-crud-api-example-and-tutorial 7 | public class ErrorHandlerMiddleware 8 | { 9 | private readonly RequestDelegate _next; 10 | private readonly ILogger _logger; 11 | 12 | public ErrorHandlerMiddleware(RequestDelegate next, ILogger logger) 13 | { 14 | _next = next; 15 | _logger = logger; 16 | } 17 | 18 | public async Task Invoke(HttpContext context) 19 | { 20 | try 21 | { 22 | await _next(context); 23 | } 24 | catch (Exception error) 25 | { 26 | HttpResponse? response = context.Response; 27 | response.ContentType = "application/json"; 28 | 29 | switch (error) 30 | { 31 | case ApiException: 32 | // api exception (400) 33 | response.StatusCode = (int)HttpStatusCode.BadRequest; 34 | break; 35 | case NotFoundException: 36 | // cannot find (404) 37 | response.StatusCode = (int)HttpStatusCode.NotFound; 38 | break; 39 | default: 40 | // unhandled error (500) 41 | _logger.LogError(error, error.Message); 42 | response.StatusCode = (int)HttpStatusCode.InternalServerError; 43 | break; 44 | } 45 | 46 | string? result = JsonSerializer.Serialize(new { message = error?.Message }); 47 | await response.WriteAsync(result); 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/Repositories/PartyUpRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | 4 | using TwitchBotDb.DTO; 5 | using TwitchBotDb.Models; 6 | 7 | namespace TwitchBotDb.Repositories 8 | { 9 | public class PartyUpRepository 10 | { 11 | private readonly string _twitchBotApiLink; 12 | 13 | public PartyUpRepository(string twitchBotApiLink) 14 | { 15 | _twitchBotApiLink = twitchBotApiLink; 16 | } 17 | 18 | public async Task GetPartyMemberAsync(string partyMember, int gameId, int broadcasterId) 19 | { 20 | return await ApiBotRequest.GetExecuteAsync(_twitchBotApiLink + $"partyups/get/{broadcasterId}?gameId={gameId}&partymember={partyMember}"); 21 | } 22 | 23 | public async Task AddRequestedPartyMemberAsync(string username, int partyMemberId) 24 | { 25 | PartyUpRequest requestedPartyMember = new PartyUpRequest 26 | { 27 | Username = username, 28 | PartyMemberId = partyMemberId 29 | }; 30 | 31 | await ApiBotRequest.PostExecuteAsync(_twitchBotApiLink + $"partyuprequests/create", requestedPartyMember); 32 | } 33 | 34 | public async Task> GetPartyListAsync(int gameId, int broadcasterId) 35 | { 36 | return await ApiBotRequest.GetExecuteAsync>(_twitchBotApiLink + $"partyups/get/{broadcasterId}?gameId={gameId}"); 37 | } 38 | 39 | public async Task> GetRequestListAsync(int gameId, int broadcasterId) 40 | { 41 | return await ApiBotRequest.GetExecuteAsync>(_twitchBotApiLink + $"partyuprequests/getlist/{broadcasterId}?gameId={gameId}"); 42 | } 43 | 44 | public async Task PopRequestedPartyMemberAsync(int gameId, int broadcasterId) 45 | { 46 | return await ApiBotRequest.DeleteExecuteAsync(_twitchBotApiLink + $"partyuprequests/deletefirst/{broadcasterId}?gameid={gameId}"); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/Repositories/SongRequestSettingRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | using TwitchBotDb.Models; 4 | 5 | namespace TwitchBotDb.Repositories 6 | { 7 | public class SongRequestSettingRepository 8 | { 9 | private readonly string _twitchBotApiLink; 10 | 11 | public SongRequestSettingRepository(string twitchBotApiLink) 12 | { 13 | _twitchBotApiLink = twitchBotApiLink; 14 | } 15 | 16 | public async Task GetSongRequestSettingAsync(int broadcasterId) 17 | { 18 | var response = await ApiBotRequest.GetExecuteAsync(_twitchBotApiLink + $"songrequestsettings/get/{broadcasterId}"); 19 | 20 | if (response != null) 21 | { 22 | return response; 23 | } 24 | 25 | return new SongRequestSetting(); 26 | } 27 | 28 | public async Task CreateSongRequestSettingAsync(string requestPlaylistId, string personalPlaylistId, int broadcasterId) 29 | { 30 | SongRequestSetting setting = new SongRequestSetting 31 | { 32 | RequestPlaylistId = requestPlaylistId, 33 | PersonalPlaylistId = personalPlaylistId, 34 | BroadcasterId = broadcasterId, 35 | DjMode = false 36 | }; 37 | 38 | return await ApiBotRequest.PostExecuteAsync(_twitchBotApiLink + "songrequestsettings/create", setting); 39 | } 40 | 41 | public async Task UpdateSongRequestSettingAsync(string requestPlaylistId, string personalPlaylistId, int broadcasterId, bool djMode) 42 | { 43 | SongRequestSetting updatedSettings = new SongRequestSetting 44 | { 45 | RequestPlaylistId = requestPlaylistId, 46 | PersonalPlaylistId = personalPlaylistId, 47 | BroadcasterId = broadcasterId, 48 | DjMode = djMode 49 | }; 50 | 51 | await ApiBotRequest.PutExecuteAsync(_twitchBotApiLink + $"songrequestsettings/update/{broadcasterId}", updatedSettings); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBot.Api/Controllers/PartyUpsController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.EntityFrameworkCore; 3 | 4 | using TwitchBot.Api.Helpers; 5 | using TwitchBot.Api.Helpers.ErrorExceptions; 6 | using TwitchBotDb.Context; 7 | 8 | namespace TwitchBot.Api.Controllers 9 | { 10 | [Route("api/[controller]/[action]")] 11 | [ApiController] 12 | public class PartyUpsController : ExtendedControllerBase 13 | { 14 | private readonly SimpleBotContext _context; 15 | 16 | public PartyUpsController(SimpleBotContext context) 17 | { 18 | _context = context; 19 | } 20 | 21 | // GET: api/partyups/get/2 22 | // GET: api/partyups/get/2?gameid=2 23 | // GET: api/partyups/get/2?gameid=2?partymember=Sinon 24 | [HttpGet("{broadcasterId:int}")] 25 | public async Task Get([FromRoute] int broadcasterId, [FromQuery] int gameId = 0, [FromQuery] string? partyMember = null) 26 | { 27 | IsModelStateValid(); 28 | 29 | object? partyUp = new object(); 30 | 31 | if (gameId > 0 && !string.IsNullOrEmpty(partyMember)) 32 | { 33 | partyUp = await _context.PartyUps 34 | .SingleOrDefaultAsync(m => 35 | m.BroadcasterId == broadcasterId 36 | && m.GameId == gameId 37 | && m.PartyMemberName.Contains(partyMember, StringComparison.CurrentCultureIgnoreCase)); 38 | } 39 | else if (gameId > 0) 40 | { 41 | partyUp = await _context.PartyUps.Where(m => m.BroadcasterId == broadcasterId && m.GameId == gameId) 42 | .Select(m => m.PartyMemberName) 43 | .ToListAsync(); 44 | } 45 | else 46 | { 47 | partyUp = await _context.PartyUps.Where(m => m.BroadcasterId == broadcasterId).ToListAsync(); 48 | } 49 | 50 | if (partyUp == null) 51 | { 52 | throw new NotFoundException("Party cannot be found"); 53 | } 54 | 55 | return Ok(partyUp); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotShared/Models/JSON/VideoJSON.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | using Newtonsoft.Json; 4 | 5 | namespace TwitchBotShared.Models.JSON 6 | { 7 | public class VideoJSON 8 | { 9 | //[JsonProperty("id")] 10 | //public string Id { get; set; } 11 | 12 | //[JsonProperty("stream_id")] 13 | //public string StreamId { get; set; } 14 | 15 | //[JsonProperty("user_id")] 16 | //public string UserId { get; set; } 17 | 18 | [JsonProperty("user_login")] 19 | public string UserLogin { get; set; } 20 | 21 | //[JsonProperty("user_name")] 22 | //public string UserName { get; set; } 23 | 24 | //[JsonProperty("title")] 25 | //public string Title { get; set; } 26 | 27 | //[JsonProperty("description")] 28 | //public string Description { get; set; } 29 | 30 | //[JsonProperty("created_at")] 31 | //public string CreatedAt { get; set; } 32 | 33 | //[JsonProperty("published_at")] 34 | //public string PublishedAt { get; set; } 35 | 36 | //[JsonProperty("url")] 37 | //public string Url { get; set; } 38 | 39 | //[JsonProperty("thumbnail_url")] 40 | //public string ThumbnailUrl { get; set; } 41 | 42 | //[JsonProperty("viewable")] 43 | //public string Viewable { get; set; } 44 | 45 | //[JsonProperty("view_count")] 46 | //public int ViewCount { get; set; } 47 | 48 | //[JsonProperty("language")] 49 | //public string Language { get; set; } 50 | 51 | //[JsonProperty("type")] 52 | //public string Type { get; set; } 53 | 54 | //[JsonProperty("duration")] 55 | //public string Duration { get; set; } 56 | 57 | //[JsonProperty("muted_segments")] 58 | //public List MutedSegments { get; set; } 59 | } 60 | 61 | //public class MutedSegment 62 | //{ 63 | // [JsonProperty("duration")] 64 | // public int Duration { get; set; } 65 | 66 | // [JsonProperty("offset")] 67 | // public int Offset { get; set; } 68 | //} 69 | 70 | public class RootVideoJSON 71 | { 72 | [JsonProperty("data")] 73 | public List Videos { get; set; } 74 | 75 | [JsonProperty("pagination")] 76 | public Pagination Pagination { get; set; } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/Models/BankHeistSetting.cs: -------------------------------------------------------------------------------- 1 | namespace TwitchBotDb.Models 2 | { 3 | public class BankHeistSetting 4 | { 5 | public int Id { get; set; } 6 | public int BroadcasterId { get; set; } 7 | public int CooldownPeriodMin { get; set; } 8 | public int EntryPeriodSec { get; set; } 9 | public string EntryMessage { get; set; } 10 | public int MaxGamble { get; set; } 11 | public string MaxGambleText { get; set; } 12 | public string EntryInstructions { get; set; } 13 | public string CooldownEntry { get; set; } 14 | public string CooldownOver { get; set; } 15 | public string NextLevelMessage2 { get; set; } 16 | public string NextLevelMessage3 { get; set; } 17 | public string NextLevelMessage4 { get; set; } 18 | public string NextLevelMessage5 { get; set; } 19 | public string GameStart { get; set; } 20 | public string SingleUserSuccess { get; set; } 21 | public string SingleUserFail { get; set; } 22 | public string ResultsMessage { get; set; } 23 | public string Success100 { get; set; } 24 | public string Success34 { get; set; } 25 | public string Success1 { get; set; } 26 | public string Success0 { get; set; } 27 | public string LevelName1 { get; set; } 28 | public int LevelMaxUsers1 { get; set; } 29 | public string LevelName2 { get; set; } 30 | public int LevelMaxUsers2 { get; set; } 31 | public string LevelName3 { get; set; } 32 | public int LevelMaxUsers3 { get; set; } 33 | public string LevelName4 { get; set; } 34 | public int LevelMaxUsers4 { get; set; } 35 | public string LevelName5 { get; set; } 36 | public int LevelMaxUsers5 { get; set; } 37 | public decimal PayoutSuccessRate1 { get; set; } 38 | public decimal PayoutMultiplier1 { get; set; } 39 | public decimal PayoutSuccessRate2 { get; set; } 40 | public decimal PayoutMultiplier2 { get; set; } 41 | public decimal PayoutSuccessRate3 { get; set; } 42 | public decimal PayoutMultiplier3 { get; set; } 43 | public decimal PayoutSuccessRate4 { get; set; } 44 | public decimal PayoutMultiplier4 { get; set; } 45 | public decimal PayoutSuccessRate5 { get; set; } 46 | public decimal PayoutMultiplier5 { get; set; } 47 | 48 | public virtual Broadcaster Broadcaster { get; set; } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/Models/BossFightBossStats.cs: -------------------------------------------------------------------------------- 1 | namespace TwitchBotDb.Models 2 | { 3 | public class BossFightBossStats 4 | { 5 | public int Id { get; set; } 6 | public int SettingsId { get; set; } 7 | public int? GameId { get; set; } 8 | public string Name1 { get; set; } 9 | public int MaxUsers1 { get; set; } 10 | public int Attack1 { get; set; } 11 | public int Defense1 { get; set; } 12 | public int Evasion1 { get; set; } 13 | public int Health1 { get; set; } 14 | public int TurnLimit1 { get; set; } 15 | public int Loot1 { get; set; } 16 | public int LastAttackBonus1 { get; set; } 17 | public string Name2 { get; set; } 18 | public int MaxUsers2 { get; set; } 19 | public int Attack2 { get; set; } 20 | public int Defense2 { get; set; } 21 | public int Evasion2 { get; set; } 22 | public int Health2 { get; set; } 23 | public int TurnLimit2 { get; set; } 24 | public int Loot2 { get; set; } 25 | public int LastAttackBonus2 { get; set; } 26 | public string Name3 { get; set; } 27 | public int MaxUsers3 { get; set; } 28 | public int Attack3 { get; set; } 29 | public int Defense3 { get; set; } 30 | public int Evasion3 { get; set; } 31 | public int Health3 { get; set; } 32 | public int TurnLimit3 { get; set; } 33 | public int Loot3 { get; set; } 34 | public int LastAttackBonus3 { get; set; } 35 | public string Name4 { get; set; } 36 | public int MaxUsers4 { get; set; } 37 | public int Attack4 { get; set; } 38 | public int Defense4 { get; set; } 39 | public int Evasion4 { get; set; } 40 | public int Health4 { get; set; } 41 | public int TurnLimit4 { get; set; } 42 | public int Loot4 { get; set; } 43 | public int LastAttackBonus4 { get; set; } 44 | public string Name5 { get; set; } 45 | public int MaxUsers5 { get; set; } 46 | public int Attack5 { get; set; } 47 | public int Defense5 { get; set; } 48 | public int Evasion5 { get; set; } 49 | public int Health5 { get; set; } 50 | public int TurnLimit5 { get; set; } 51 | public int Loot5 { get; set; } 52 | public int LastAttackBonus5 { get; set; } 53 | 54 | public virtual TwitchGameCategory Game { get; set; } 55 | public virtual BossFightSetting Settings { get; set; } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/Repositories/FollowerRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | 4 | using TwitchBotDb.Models; 5 | 6 | namespace TwitchBotDb.Repositories 7 | { 8 | public class FollowerRepository 9 | { 10 | private readonly string _twitchBotApiLink; 11 | 12 | public FollowerRepository(string twitchBotApiLink) 13 | { 14 | _twitchBotApiLink = twitchBotApiLink; 15 | } 16 | 17 | public async Task CurrentExpAsync(string chatter, int broadcasterId) 18 | { 19 | RankFollower follower = await ApiBotRequest.GetExecuteAsync(_twitchBotApiLink + $"rankfollowers/get/{broadcasterId}?username={chatter}"); 20 | 21 | return follower.Experience; 22 | } 23 | 24 | public async Task UpdateExpAsync(string chatter, int broadcasterId, int exp) 25 | { 26 | await ApiBotRequest.PutExecuteAsync(_twitchBotApiLink + $"rankfollowers/updateexp/{broadcasterId}?username={chatter}&exp={exp}"); 27 | } 28 | 29 | public async Task EnlistRecruitAsync(string chatter, int broadcasterId) 30 | { 31 | RankFollower freshRecruit = new RankFollower 32 | { 33 | Username = chatter, 34 | Experience = 0, 35 | BroadcasterId = broadcasterId 36 | }; 37 | 38 | await ApiBotRequest.PostExecuteAsync(_twitchBotApiLink + $"rankfollowers/create", freshRecruit); 39 | } 40 | 41 | public async Task> GetRankListAsync(int broadcasterId) 42 | { 43 | return await ApiBotRequest.GetExecuteAsync>(_twitchBotApiLink + $"ranks/get/{broadcasterId}"); 44 | } 45 | 46 | public async Task> CreateDefaultRanksAsync(int broadcasterId) 47 | { 48 | List rank = new List 49 | { 50 | new Rank 51 | { 52 | BroadcasterId = broadcasterId 53 | } 54 | }; 55 | 56 | return await ApiBotRequest.PostExecuteAsync(_twitchBotApiLink + $"ranks/createdefault", rank); 57 | } 58 | 59 | public async Task> GetFollowersLeaderboardAsync(int broadcasterId) 60 | { 61 | return await ApiBotRequest.GetExecuteAsync>(_twitchBotApiLink + $"rankfollowers/getleaderboard/{broadcasterId}?topnumber=3"); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/Models/Broadcaster.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace TwitchBotDb.Models 5 | { 6 | public class Broadcaster 7 | { 8 | public Broadcaster() 9 | { 10 | BankHeistSettings = new HashSet(); 11 | Banks = new HashSet(); 12 | BossFightSettings = new HashSet(); 13 | CustomCommands = new HashSet(); 14 | DiscordSelfRoleAssigns = new HashSet(); 15 | ErrorLogs = new HashSet(); 16 | InGameUsernames = new HashSet(); 17 | PartyUps = new HashSet(); 18 | Quotes = new HashSet(); 19 | RankFollowers = new HashSet(); 20 | Ranks = new HashSet(); 21 | Reminders = new HashSet(); 22 | SongRequestIgnores = new HashSet(); 23 | SongRequestSettings = new HashSet(); 24 | SongRequests = new HashSet(); 25 | } 26 | 27 | public int Id { get; set; } 28 | public string Username { get; set; } 29 | public int TwitchId { get; set; } 30 | public DateTime LastUpdated { get; set; } 31 | 32 | public virtual ICollection BankHeistSettings { get; set; } 33 | public virtual ICollection Banks { get; set; } 34 | public virtual ICollection BossFightSettings { get; set; } 35 | public virtual ICollection CustomCommands { get; set; } 36 | public virtual ICollection DiscordSelfRoleAssigns { get; set; } 37 | public virtual ICollection ErrorLogs { get; set; } 38 | public virtual ICollection InGameUsernames { get; set; } 39 | public virtual ICollection PartyUps { get; set; } 40 | public virtual ICollection Quotes { get; set; } 41 | public virtual ICollection RankFollowers { get; set; } 42 | public virtual ICollection Ranks { get; set; } 43 | public virtual ICollection Reminders { get; set; } 44 | public virtual ICollection SongRequestIgnores { get; set; } 45 | public virtual ICollection SongRequestSettings { get; set; } 46 | public virtual ICollection SongRequests { get; set; } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotShared/ClientLibraries/Singletons/CustomCommandSingleton.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | 5 | using TwitchBotDb; 6 | using TwitchBotDb.Models; 7 | 8 | namespace TwitchBotShared.ClientLibraries.Singletons 9 | { 10 | public class CustomCommandSingleton 11 | { 12 | private static volatile CustomCommandSingleton _instance; 13 | private static readonly object _syncRoot = new object(); 14 | 15 | private List _customCommands = new List(); 16 | 17 | private CustomCommandSingleton() { } 18 | 19 | public static CustomCommandSingleton Instance 20 | { 21 | get 22 | { 23 | // first check 24 | if (_instance == null) 25 | { 26 | lock (_syncRoot) 27 | { 28 | // second check 29 | if (_instance == null) 30 | _instance = new CustomCommandSingleton(); 31 | } 32 | } 33 | 34 | return _instance; 35 | } 36 | } 37 | 38 | public async Task LoadCustomCommands(string twitchBotApiLink, int broadcasterId) 39 | { 40 | _customCommands = await ApiBotRequest.GetExecuteAsync>(twitchBotApiLink + $"customcommands/get/{broadcasterId}"); 41 | } 42 | 43 | public IEnumerable GetSoundCommands() 44 | { 45 | return _customCommands.FindAll(s => s.IsSound).OrderBy(o => o.Name); 46 | } 47 | 48 | public CustomCommand FindCustomCommand(string commandName, int? gameId = null) 49 | { 50 | return _customCommands.SingleOrDefault(c => c.Name == commandName && (c.GameId == gameId || c.GameId == null)); 51 | } 52 | 53 | public async Task AddCustomCommand(string twitchBotApiLink, CustomCommand customCommand) 54 | { 55 | await ApiBotRequest.PostExecuteAsync(twitchBotApiLink + $"customcommands/create", customCommand); 56 | 57 | _customCommands.Add(customCommand); 58 | } 59 | 60 | public async Task DeleteCustomCommand(string twitchBotApiLink, int broadcasterId, string username) 61 | { 62 | CustomCommand customCommand = await ApiBotRequest.DeleteExecuteAsync(twitchBotApiLink + $"customcommands/delete/{broadcasterId}?name={username}"); 63 | 64 | _customCommands.Remove(customCommand); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotConsoleApp/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using System.Configuration; 5 | using System.Threading; 6 | using System.Net.Http; 7 | 8 | using Autofac; 9 | 10 | using TwitchBotConsoleApp.Modules; 11 | 12 | using TwitchBotShared.ClientLibraries; 13 | using TwitchBotShared.Config; 14 | using TwitchBotShared.Models; 15 | 16 | namespace TwitchBotConsoleApp 17 | { 18 | class Program 19 | { 20 | public static List RouletteUsers = new List(); // used to handle russian roulette 21 | public static readonly HttpClient HttpClient = new HttpClient(); 22 | 23 | static void Main(string[] args) 24 | { 25 | try 26 | { 27 | var appConfig = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); 28 | var botConfigSection = appConfig.GetSection("TwitchBotConfig") as TwitchBotConfigurationSection; 29 | 30 | //Create a container builder and register all classes that will be composed for the application 31 | var builder = new ContainerBuilder(); 32 | 33 | // Password from www.twitchapps.com/tmi/ 34 | // include the "oauth:" portion 35 | // Use chat bot's oauth 36 | /* main server: irc.chat.twitch.tv, 6667 */ 37 | builder.RegisterModule(new TwitchBotModule() 38 | { 39 | AppConfig = appConfig, 40 | TwitchBotApiLink = new NamedParameter("twitchBotApiLink", botConfigSection.TwitchBotApiLink), 41 | TwitchBotConfigurationSection = botConfigSection, 42 | Irc = new IrcClient(botConfigSection.BotName.ToLower(), botConfigSection.TwitchOAuth, botConfigSection.Broadcaster.ToLower()) 43 | }); 44 | 45 | var container = builder.Build(); 46 | 47 | //Define main lifetime scope 48 | //Get an instance of TwitchBotApplication and execute main loop 49 | using (var scope = container.BeginLifetimeScope()) 50 | { 51 | var app = scope.Resolve(); 52 | Task.WaitAll(app.RunAsync()); 53 | } 54 | } 55 | catch (Exception ex) 56 | { 57 | Console.WriteLine("Local error found: " + ex.Message + "\n"); 58 | Console.WriteLine("Local error found: " + ex.InnerException); 59 | Thread.Sleep(5000); 60 | Environment.Exit(1); 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/Repositories/BankRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | 5 | using TwitchBotDb.DTO; 6 | using TwitchBotDb.Models; 7 | 8 | namespace TwitchBotDb.Repositories 9 | { 10 | public class BankRepository 11 | { 12 | private readonly string _twitchBotApiLink; 13 | 14 | public BankRepository(string twitchBotApiLink) 15 | { 16 | _twitchBotApiLink = twitchBotApiLink; 17 | } 18 | 19 | public async Task CreateAccountAsync(string username, int broadcasterId, int deposit) 20 | { 21 | Bank freshAccount = new Bank 22 | { 23 | Username = username, 24 | Wallet = deposit, 25 | Broadcaster = broadcasterId 26 | }; 27 | 28 | await ApiBotRequest.PostExecuteAsync(_twitchBotApiLink + $"banks/createaccount", freshAccount); 29 | } 30 | 31 | public async Task UpdateAccountAsync(string walletOwner, int broadcasterId, int newWalletBalance) 32 | { 33 | await ApiBotRequest.PutExecuteAsync(_twitchBotApiLink + $"banks/updateaccount/{broadcasterId}?updatedwallet={newWalletBalance}&username={walletOwner}"); 34 | } 35 | 36 | public async Task> UpdateCreateBalance(List usernameList, int broadcasterId, int deposit, bool showOutput = false) 37 | { 38 | return await ApiBotRequest.PutExecuteAsync>(_twitchBotApiLink + $"banks/updatecreateaccount/{broadcasterId}?deposit={deposit}&showOutput={showOutput}", usernameList); 39 | } 40 | 41 | public async Task CheckBalanceAsync(string username, int broadcasterId) 42 | { 43 | var response = await ApiBotRequest.GetExecuteAsync>(_twitchBotApiLink + $"banks/get/{broadcasterId}?username={username}"); 44 | 45 | if (response != null && response.Count > 0) 46 | { 47 | return response.Find(m => m.Username.ToLower() == username.ToLower()).Wallet; 48 | } 49 | 50 | return -1; 51 | } 52 | 53 | public async Task> GetCurrencyLeaderboardAsync(string broadcasterName, int broadcasterId, string botName) 54 | { 55 | var response = await ApiBotRequest.GetExecuteAsync>(_twitchBotApiLink + $"banks/getleaderboard/{broadcasterId}?broadcastername={broadcasterName}&botname={botName}&topnumber=3"); 56 | 57 | if (response != null && response.Count > 0) 58 | { 59 | return response; 60 | } 61 | 62 | return new List(); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBot.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.2.32616.157 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TwitchBotDb", "TwitchBotDb\TwitchBotDb.csproj", "{069BC4AA-B2EA-4400-BCDE-8A6AC793DA33}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TwitchBotShared", "TwitchBotShared\TwitchBotShared.csproj", "{7633DB4D-C143-46A8-9B3E-6D4063D08831}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TwitchBotConsoleApp", "TwitchBotConsoleApp\TwitchBotConsoleApp.csproj", "{17A76B96-ED27-4653-9AA9-8639A2B8A24F}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TwitchBot.Api", "TwitchBot.Api\TwitchBot.Api.csproj", "{A88BEC11-658A-4F15-941E-5E383492F7F0}" 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Release|Any CPU = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {069BC4AA-B2EA-4400-BCDE-8A6AC793DA33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {069BC4AA-B2EA-4400-BCDE-8A6AC793DA33}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {069BC4AA-B2EA-4400-BCDE-8A6AC793DA33}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {069BC4AA-B2EA-4400-BCDE-8A6AC793DA33}.Release|Any CPU.Build.0 = Release|Any CPU 24 | {7633DB4D-C143-46A8-9B3E-6D4063D08831}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {7633DB4D-C143-46A8-9B3E-6D4063D08831}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {7633DB4D-C143-46A8-9B3E-6D4063D08831}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {7633DB4D-C143-46A8-9B3E-6D4063D08831}.Release|Any CPU.Build.0 = Release|Any CPU 28 | {17A76B96-ED27-4653-9AA9-8639A2B8A24F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {17A76B96-ED27-4653-9AA9-8639A2B8A24F}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {17A76B96-ED27-4653-9AA9-8639A2B8A24F}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {17A76B96-ED27-4653-9AA9-8639A2B8A24F}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {A88BEC11-658A-4F15-941E-5E383492F7F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {A88BEC11-658A-4F15-941E-5E383492F7F0}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {A88BEC11-658A-4F15-941E-5E383492F7F0}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {A88BEC11-658A-4F15-941E-5E383492F7F0}.Release|Any CPU.Build.0 = Release|Any CPU 36 | EndGlobalSection 37 | GlobalSection(SolutionProperties) = preSolution 38 | HideSolutionNode = FALSE 39 | EndGlobalSection 40 | GlobalSection(ExtensibilityGlobals) = postSolution 41 | SolutionGuid = {2673A69A-8013-4237-841E-56D8319F9D86} 42 | EndGlobalSection 43 | EndGlobal 44 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotShared/ClientLibraries/TwitchChatterList.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | using TwitchBotShared.ClientLibraries.Singletons; 7 | using TwitchBotShared.Enums; 8 | using TwitchBotShared.Models; 9 | 10 | namespace TwitchBotShared.ClientLibraries 11 | { 12 | public class TwitchChatterList 13 | { 14 | private static volatile TwitchChatterList _instance; 15 | private static object _syncRoot = new object(); 16 | private static readonly ErrorHandler _errHndlrInstance = ErrorHandler.Instance; 17 | 18 | public bool AreListsAvailable { get; set; } = false; 19 | 20 | public List ChattersByType { get; } = new List(); 21 | 22 | public List ChattersByName { get; } = new List(); 23 | 24 | public List TwitchFollowers { get; } = new List(); 25 | 26 | public List TwitchSubscribers { get; } = new List(); 27 | 28 | public List TwitchRegularFollowers { get; } = new List(); 29 | 30 | private TwitchChatterList() { } 31 | 32 | public static TwitchChatterList Instance 33 | { 34 | get 35 | { 36 | // first check 37 | if (_instance == null) 38 | { 39 | lock (_syncRoot) 40 | { 41 | // second check 42 | if (_instance == null) 43 | _instance = new TwitchChatterList(); 44 | } 45 | } 46 | 47 | return _instance; 48 | } 49 | } 50 | 51 | public async Task GetUserChatterTypeAsync(string username) 52 | { 53 | try 54 | { 55 | DateTime timeToGetOut = DateTime.Now.AddSeconds(3); 56 | 57 | // wait until lists are available 58 | while (!AreListsAvailable && DateTime.Now < timeToGetOut) 59 | { 60 | 61 | } 62 | 63 | foreach (TwitchChatterType chatterType in ChattersByType.OrderByDescending(t => t.ChatterType)) 64 | { 65 | if (chatterType.TwitchChatters.Any(u => u.Username == username)) 66 | return chatterType.ChatterType; 67 | } 68 | } 69 | catch (Exception ex) 70 | { 71 | await _errHndlrInstance.LogErrorAsync(ex, "TwitchChatterList", "GetUserChatterTypeAsync(string)", false); 72 | } 73 | 74 | return ChatterType.DoesNotExist; 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotShared/ClientLibraries/Singletons/BroadcasterSingleton.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | 4 | using TwitchBotDb; 5 | using TwitchBotDb.Models; 6 | 7 | namespace TwitchBotShared.ClientLibraries.Singletons 8 | { 9 | public class BroadcasterSingleton 10 | { 11 | public string Username { get; set; } 12 | public int DatabaseId { get; set; } 13 | public string TwitchId { get; set; } 14 | 15 | private static volatile BroadcasterSingleton _instance; 16 | private static object _syncRoot = new Object(); 17 | 18 | private BroadcasterSingleton() { } 19 | 20 | public static BroadcasterSingleton Instance 21 | { 22 | get 23 | { 24 | // first check 25 | if (_instance == null) 26 | { 27 | lock (_syncRoot) 28 | { 29 | // second check 30 | if (_instance == null) 31 | _instance = new BroadcasterSingleton(); 32 | } 33 | } 34 | 35 | return _instance; 36 | } 37 | } 38 | 39 | public async Task FindBroadcaster(string twitchId, string twitchBotApiLink, string username = "") 40 | { 41 | Broadcaster broadcaster = null; 42 | 43 | if (!string.IsNullOrEmpty(username)) 44 | broadcaster = await ApiBotRequest.GetExecuteAsync(twitchBotApiLink + $"broadcasters/get/{twitchId}?username={username}"); 45 | else 46 | broadcaster = await ApiBotRequest.GetExecuteAsync(twitchBotApiLink + $"broadcasters/get/{twitchId}"); 47 | 48 | if (broadcaster != null) 49 | { 50 | Username = broadcaster.Username; 51 | TwitchId = twitchId; 52 | DatabaseId = broadcaster.Id; 53 | } 54 | } 55 | 56 | public async Task AddBroadcaster(string twitchBotApiLink) 57 | { 58 | Broadcaster freshBroadcaster = new Broadcaster 59 | { 60 | Username = Username, 61 | TwitchId = int.Parse(TwitchId) 62 | }; 63 | 64 | await ApiBotRequest.PostExecuteAsync(twitchBotApiLink + $"broadcasters/create", freshBroadcaster); 65 | } 66 | 67 | public async Task UpdateBroadcaster(string twitchBotApiLink) 68 | { 69 | Broadcaster updatedBroadcaster = new Broadcaster 70 | { 71 | Username = Username, 72 | TwitchId = int.Parse(TwitchId) 73 | }; 74 | 75 | await ApiBotRequest.PutExecuteAsync(twitchBotApiLink + $"broadcasters/update/{TwitchId}", updatedBroadcaster); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/Repositories/SongRequestBlacklistRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | 4 | using TwitchBotDb.Models; 5 | 6 | 7 | 8 | namespace TwitchBotDb.Repositories 9 | { 10 | public class SongRequestBlacklistRepository 11 | { 12 | private readonly string _twitchBotApiLink; 13 | 14 | public SongRequestBlacklistRepository(string twitchBotApiLink) 15 | { 16 | _twitchBotApiLink = twitchBotApiLink; 17 | } 18 | 19 | public async Task> GetSongRequestIgnoreAsync(int broadcasterId) 20 | { 21 | var response = await ApiBotRequest.GetExecuteAsync>(_twitchBotApiLink + $"songrequestignores/get/{broadcasterId}"); 22 | 23 | if (response != null && response.Count > 0) 24 | { 25 | return response; 26 | } 27 | 28 | return new List(); 29 | } 30 | 31 | public async Task IgnoreArtistAsync(string artist, int broadcasterId) 32 | { 33 | SongRequestIgnore ignoreArtist = new SongRequestIgnore 34 | { 35 | Artist = artist, 36 | Title = "", 37 | BroadcasterId = broadcasterId 38 | }; 39 | 40 | return await ApiBotRequest.PostExecuteAsync(_twitchBotApiLink + $"songrequestignores/create", ignoreArtist); 41 | } 42 | 43 | public async Task IgnoreSongAsync(string title, string artist, int broadcasterId) 44 | { 45 | SongRequestIgnore ignoreSong = new SongRequestIgnore 46 | { 47 | Artist = artist, 48 | Title = title, 49 | BroadcasterId = broadcasterId 50 | }; 51 | 52 | return await ApiBotRequest.PostExecuteAsync(_twitchBotApiLink + $"songrequestignores/create", ignoreSong); 53 | } 54 | 55 | public async Task> AllowArtistAsync(string artist, int broadcasterId) 56 | { 57 | return await ApiBotRequest.DeleteExecuteAsync>(_twitchBotApiLink + $"songrequestignores/delete/{broadcasterId}?artist={artist}"); 58 | } 59 | 60 | public async Task AllowSongAsync(string title, string artist, int broadcasterId) 61 | { 62 | return await ApiBotRequest.DeleteExecuteAsync(_twitchBotApiLink + $"songrequestignores/delete/{broadcasterId}?artist={artist}&title={title}"); 63 | } 64 | 65 | public async Task> ResetIgnoreListAsync(int broadcasterId) 66 | { 67 | return await ApiBotRequest.DeleteExecuteAsync>(_twitchBotApiLink + $"songrequestignores/delete/{broadcasterId}"); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/SqlScripts/StoredProcedures/dbo.CreateNewBossesWithDefaultStats.sql: -------------------------------------------------------------------------------- 1 | CREATE PROCEDURE [dbo].[CreateNewBossesWithDefaultStats] 2 | ( 3 | @SettingsId INT 4 | , @NewGameId INT 5 | , @Name1 VARCHAR(50) 6 | , @Name2 VARCHAR(50) 7 | , @Name3 VARCHAR(50) 8 | , @Name4 VARCHAR(50) 9 | , @Name5 VARCHAR(50) 10 | ) 11 | AS 12 | BEGIN 13 | -- SET NOCOUNT ON added to prevent extra result sets from 14 | -- interfering with SELECT statements. 15 | SET NOCOUNT ON; 16 | 17 | IF @SettingsId IS NULL 18 | OR @NewGameId IS NULL 19 | OR @Name1 IS NULL 20 | OR @Name2 IS NULL 21 | OR @Name2 IS NULL 22 | OR @Name2 IS NULL 23 | OR @Name2 IS NULL 24 | BEGIN 25 | RAISERROR('Found a NULL value when validating the input parameters', 16, 1); 26 | SET NOEXEC ON; 27 | END; 28 | 29 | INSERT INTO dbo.BossFightBossStats (SettingsId, GameId, Name1, MaxUsers1, Attack1, Defense1, Evasion1, Health1, TurnLimit1, Loot1, LastAttackBonus1, Name2 30 | , MaxUsers2, Attack2, Defense2, Evasion2, Health2, TurnLimit2, Loot2, LastAttackBonus2, Name3, MaxUsers3, Attack3 31 | , Defense3, Evasion3, Health3, TurnLimit3, Loot3, LastAttackBonus3, Name4, MaxUsers4, Attack4, Defense4, Evasion4 32 | , Health4, TurnLimit4, Loot4, LastAttackBonus4, Name5, MaxUsers5, Attack5, Defense5, Evasion5, Health5, TurnLimit5, Loot5 33 | , LastAttackBonus5) 34 | SELECT @SettingsId AS SettingsId 35 | , @NewGameId AS GameId 36 | , @Name1 AS Name1 37 | , MaxUsers1 38 | , Attack1 39 | , Defense1 40 | , Evasion1 41 | , Health1 42 | , TurnLimit1 43 | , Loot1 44 | , LastAttackBonus1 45 | , @Name2 AS Name2 46 | , MaxUsers2 47 | , Attack2 48 | , Defense2 49 | , Evasion2 50 | , Health2 51 | , TurnLimit2 52 | , Loot2 53 | , LastAttackBonus2 54 | , @Name3 AS Name3 55 | , MaxUsers3 56 | , Attack3 57 | , Defense3 58 | , Evasion3 59 | , Health3 60 | , TurnLimit3 61 | , Loot3 62 | , LastAttackBonus3 63 | , @Name4 AS Name4 64 | , MaxUsers4 65 | , Attack4 66 | , Defense4 67 | , Evasion4 68 | , Health4 69 | , TurnLimit4 70 | , Loot4 71 | , LastAttackBonus4 72 | , @Name5 AS Name5 73 | , MaxUsers5 74 | , Attack5 75 | , Defense5 76 | , Evasion5 77 | , Health5 78 | , TurnLimit5 79 | , Loot5 80 | , LastAttackBonus5 81 | FROM dbo.BossFightBossStats 82 | WHERE GameId IS NULL 83 | AND SettingsId = @SettingsId; 84 | END; 85 | GO 86 | 87 | 88 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/Services/FollowerService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | using TwitchBotDb.Repositories; 7 | 8 | using TwitchBotDb.Models; 9 | 10 | namespace TwitchBotDb.Services 11 | { 12 | public class FollowerService 13 | { 14 | private readonly FollowerRepository _followerDb; 15 | 16 | public FollowerService(FollowerRepository followerDb) 17 | { 18 | _followerDb = followerDb; 19 | } 20 | 21 | public async Task CurrentExpAsync(string chatter, int broadcasterId) 22 | { 23 | return await _followerDb.CurrentExpAsync(chatter, broadcasterId); 24 | } 25 | 26 | public async Task UpdateExpAsync(string chatter, int broadcasterId, int exp) 27 | { 28 | await _followerDb.UpdateExpAsync(chatter, broadcasterId, exp); 29 | } 30 | 31 | public async Task EnlistRecruitAsync(string chatter, int broadcasterId) 32 | { 33 | await _followerDb.EnlistRecruitAsync(chatter, broadcasterId); 34 | } 35 | 36 | public async Task> GetRankListAsync(int broadcasterId) 37 | { 38 | return await _followerDb.GetRankListAsync(broadcasterId); 39 | } 40 | 41 | public async Task> CreateDefaultRanksAsync(int broadcasterId) 42 | { 43 | return await _followerDb.CreateDefaultRanksAsync(broadcasterId); 44 | } 45 | 46 | public Rank GetCurrentRank(IEnumerable rankList, int currentExp) 47 | { 48 | Rank currentRank = new Rank(); 49 | 50 | // find the user's current rank by experience cap 51 | foreach (Rank rank in rankList.OrderBy(r => r.ExpCap)) 52 | { 53 | // search until current experience < experience cap 54 | if (currentExp >= rank.ExpCap) 55 | { 56 | continue; 57 | } 58 | else 59 | { 60 | currentRank.Name = rank.Name; 61 | currentRank.ExpCap = rank.ExpCap; 62 | break; 63 | } 64 | } 65 | 66 | return currentRank; 67 | } 68 | 69 | public decimal GetHoursWatched(int currentExp) 70 | { 71 | return Math.Round(Convert.ToDecimal(currentExp) / (decimal)60.0, 2); 72 | } 73 | 74 | public bool IsRegularFollower(int currentExp, int regularFollowerHours) 75 | { 76 | return GetHoursWatched(currentExp) >= regularFollowerHours; 77 | } 78 | 79 | public async Task> GetFollowersLeaderboardAsync(int broadcasterId) 80 | { 81 | return await _followerDb.GetFollowersLeaderboardAsync(broadcasterId); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotShared/Extensions/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | 5 | namespace TwitchBotShared.Extensions 6 | { 7 | public static class StringExtensions 8 | { 9 | /// 10 | /// Get the index of a character based on the Nth occurrence 11 | /// 12 | /// The expression to be searched 13 | /// The character in question 14 | /// Nth index (zero-based) 15 | /// 16 | public static int GetNthCharIndex(this string s, char findChar, int n) 17 | { 18 | int count = 0; 19 | 20 | for (int i = 0; i < s.Length; i++) 21 | { 22 | if (s[i] == findChar) 23 | { 24 | count++; 25 | if (count == n) 26 | { 27 | return i; 28 | } 29 | } 30 | } 31 | return -1; 32 | } 33 | 34 | public static List AllIndexesOf(this string s, string searchCriteria) 35 | { 36 | List foundIndexes = new List(); 37 | 38 | for (int i = s.IndexOf(searchCriteria); i > -1; i = s.IndexOf(searchCriteria, i + 1)) 39 | { 40 | foundIndexes.Add(i); 41 | } 42 | 43 | return foundIndexes; 44 | } 45 | 46 | public static Stream ToStream(this string s) 47 | { 48 | MemoryStream stream = new MemoryStream(); 49 | StreamWriter writer = new StreamWriter(stream); 50 | writer.Write(s); 51 | writer.Flush(); 52 | stream.Position = 0; 53 | return stream; 54 | } 55 | 56 | public static bool Contains(this string source, string toCheck, StringComparison comp) 57 | { 58 | return source.IndexOf(toCheck, comp) >= 0; 59 | } 60 | 61 | public static string ReplaceLastOccurrence(this string source, string find, string replace) 62 | { 63 | int place = source.LastIndexOf(find); 64 | 65 | if (place == -1) 66 | return source; 67 | 68 | return source.Remove(place, find.Length).Insert(place, replace); 69 | } 70 | 71 | public static DateTime? ToNullableDateTime(this string s) 72 | { 73 | if (DateTime.TryParse(s, out DateTime i)) return i; 74 | 75 | return null; 76 | } 77 | 78 | public static int? ToNullableInt(this string s) 79 | { 80 | if (int.TryParse(s, out int i)) return i; 81 | 82 | return null; 83 | } 84 | 85 | public static TimeSpan? ToNullableTimeSpan(this string s) 86 | { 87 | if (TimeSpan.TryParse(s, out TimeSpan i)) return i; 88 | 89 | return null; 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBot.Api/Controllers/BossFightClassStatsController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.EntityFrameworkCore; 3 | 4 | using TwitchBot.Api.Helpers; 5 | using TwitchBot.Api.Helpers.ErrorExceptions; 6 | using TwitchBotDb.Context; 7 | using TwitchBotDb.Models; 8 | 9 | namespace TwitchBot.Api.Controllers 10 | { 11 | [Route("api/[controller]/[action]")] 12 | [ApiController] 13 | public class BossFightClassStatsController : ExtendedControllerBase 14 | { 15 | private readonly SimpleBotContext _context; 16 | 17 | public BossFightClassStatsController(SimpleBotContext context) 18 | { 19 | _context = context; 20 | } 21 | 22 | // GET: api/bossfightclassstats/get/1 23 | [HttpGet("{settingsId}")] 24 | public async Task Get(int settingsId) 25 | { 26 | IsModelStateValid(); 27 | 28 | BossFightClassStats? bossFightClassStats = await _context.BossFightClassStats 29 | .SingleOrDefaultAsync(m => m.SettingsId == settingsId); 30 | 31 | if (bossFightClassStats == null) 32 | { 33 | throw new NotFoundException("Boss fight class stats not found"); 34 | } 35 | 36 | return Ok(bossFightClassStats); 37 | } 38 | 39 | // PUT: api/bossfightclassstats/update/1?id=1 40 | [HttpPut("{settingsId}")] 41 | public async Task Update(int settingsId, [FromQuery] int id, [FromBody] BossFightClassStats bossFightClassStats) 42 | { 43 | IsModelStateValid(); 44 | 45 | if (id != bossFightClassStats.Id || settingsId != bossFightClassStats.SettingsId) 46 | { 47 | throw new ApiException("Settings ID does not match with boss fight class stats's settings ID"); 48 | } 49 | 50 | _context.Entry(bossFightClassStats).State = EntityState.Modified; 51 | 52 | try 53 | { 54 | await _context.SaveChangesAsync(); 55 | } 56 | catch (DbUpdateConcurrencyException) 57 | { 58 | if (!BossFightClassStatsExists(id)) 59 | { 60 | throw new NotFoundException("Boss fight class stats not found"); 61 | } 62 | else 63 | { 64 | throw; 65 | } 66 | } 67 | 68 | return NoContent(); 69 | } 70 | 71 | // POST: api/bossfightclassstats/create 72 | [HttpPost] 73 | public async Task Create([FromBody] BossFightClassStats bossFightClassStats) 74 | { 75 | IsModelStateValid(); 76 | 77 | _context.BossFightClassStats.Add(bossFightClassStats); 78 | await _context.SaveChangesAsync(); 79 | 80 | return CreatedAtAction("Get", new { settingsId = bossFightClassStats.Id }, bossFightClassStats); 81 | } 82 | 83 | private bool BossFightClassStatsExists(int id) 84 | { 85 | return _context.BossFightClassStats.Any(e => e.Id == id); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotShared/Threads/DelayMessage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading; 4 | 5 | using TwitchBotShared.ClientLibraries.Singletons; 6 | using TwitchBotShared.ClientLibraries; 7 | using TwitchBotShared.Models; 8 | 9 | namespace TwitchBotShared.Threads 10 | { 11 | public class DelayMessage 12 | { 13 | private readonly Thread _msgSender; 14 | private readonly IrcClient _irc; 15 | private readonly DelayedMessageSingleton _delayedMessagesInstance = DelayedMessageSingleton.Instance; 16 | private readonly ErrorHandler _errHndlrInstance = ErrorHandler.Instance; 17 | 18 | public DelayMessage(IrcClient irc) 19 | { 20 | _irc = irc; 21 | _msgSender = new Thread(new ThreadStart(this.Run)); 22 | } 23 | 24 | public void Start() 25 | { 26 | _msgSender.IsBackground = true; 27 | _msgSender.Start(); 28 | } 29 | 30 | private async void Run() 31 | { 32 | try 33 | { 34 | while (true) 35 | { 36 | if (_delayedMessagesInstance.DelayedMessages.Count > 0) 37 | { 38 | /* Make sure to send messages at the proper time */ 39 | DelayedMessage delayedMessage = _delayedMessagesInstance.DelayedMessages 40 | .FirstOrDefault(m => m.SendDate < DateTime.Now 41 | && (m.ExpirationDateUtc == null || m.ExpirationDateUtc > DateTime.UtcNow)); 42 | 43 | if (delayedMessage != null) 44 | { 45 | _irc.SendPublicChatMessage(delayedMessage.Message); 46 | Console.WriteLine($"Delayed message sent: {delayedMessage.Message}"); 47 | _delayedMessagesInstance.DelayedMessages.Remove(delayedMessage); // remove sent message from list 48 | 49 | // re-add message if set as reminder 50 | if (delayedMessage.ReminderEveryMin > 0) 51 | { 52 | _delayedMessagesInstance.DelayedMessages.Add(new DelayedMessage 53 | { 54 | ReminderId = delayedMessage.ReminderId, 55 | Message = delayedMessage.Message, 56 | SendDate = delayedMessage.SendDate.AddMinutes((double)delayedMessage.ReminderEveryMin), 57 | ReminderEveryMin = delayedMessage.ReminderEveryMin, 58 | ExpirationDateUtc = delayedMessage.ExpirationDateUtc 59 | }); 60 | } 61 | } 62 | } 63 | 64 | Thread.Sleep(100); 65 | } 66 | } 67 | catch (Exception ex) 68 | { 69 | await _errHndlrInstance.LogErrorAsync(ex, "DelayMsg", "Run()", false); 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBot.Api/Controllers/BankHeistSettingsController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.EntityFrameworkCore; 3 | 4 | using TwitchBot.Api.Helpers; 5 | using TwitchBot.Api.Helpers.ErrorExceptions; 6 | using TwitchBotDb.Context; 7 | using TwitchBotDb.Models; 8 | 9 | namespace TwitchBot.Api.Controllers 10 | { 11 | [Route("api/[controller]/[action]")] 12 | [ApiController] 13 | public class BankHeistSettingsController : ExtendedControllerBase 14 | { 15 | private readonly SimpleBotContext _context; 16 | 17 | public BankHeistSettingsController(SimpleBotContext context) 18 | { 19 | _context = context; 20 | } 21 | 22 | // GET: api/bankheistsettings/get/2 23 | [HttpGet("{broadcasterId}")] 24 | public async Task Get(int broadcasterId) 25 | { 26 | IsModelStateValid(); 27 | 28 | BankHeistSetting? bankHeistSetting = await _context.BankHeistSettings 29 | .SingleOrDefaultAsync(m => m.BroadcasterId == broadcasterId); 30 | 31 | if (bankHeistSetting == null) 32 | { 33 | throw new NotFoundException("Bank heist setting not found"); 34 | } 35 | 36 | return Ok(bankHeistSetting); 37 | } 38 | 39 | // PUT: api/bankheistsettings/update/2 40 | [HttpPut("{broadcasterId}")] 41 | public async Task Update(int broadcasterId, [FromBody] BankHeistSetting bankHeistSetting) 42 | { 43 | IsModelStateValid(); 44 | 45 | if (broadcasterId != bankHeistSetting.BroadcasterId) 46 | { 47 | throw new ApiException("Broadcaster ID does not match with bank heist setting's broadcaster ID"); 48 | } 49 | 50 | _context.Entry(bankHeistSetting).State = EntityState.Modified; 51 | 52 | try 53 | { 54 | await _context.SaveChangesAsync(); 55 | } 56 | catch (DbUpdateConcurrencyException) 57 | { 58 | if (!BankHeistSettingExists(broadcasterId)) 59 | { 60 | throw new NotFoundException("Bank heist setting not found"); 61 | } 62 | else 63 | { 64 | throw; 65 | } 66 | } 67 | 68 | return NoContent(); 69 | } 70 | 71 | // POST: api/bankheistsettings/create 72 | // Body (JSON): { "broadcaster": 2 } 73 | [HttpPost] 74 | public async Task Create([FromBody] BankHeistSetting bankHeistSetting) 75 | { 76 | IsModelStateValid(); 77 | 78 | _context.BankHeistSettings.Add(bankHeistSetting); 79 | await _context.SaveChangesAsync(); 80 | 81 | return CreatedAtAction("Get", new { broadcasterId = bankHeistSetting.BroadcasterId }, bankHeistSetting); 82 | } 83 | 84 | private bool BankHeistSettingExists(int broadcasterId) 85 | { 86 | return _context.BankHeistSettings.Any(e => e.BroadcasterId == broadcasterId); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBot.Api/Controllers/BossFightSettingsController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.EntityFrameworkCore; 3 | 4 | using TwitchBot.Api.Helpers; 5 | using TwitchBot.Api.Helpers.ErrorExceptions; 6 | using TwitchBotDb.Context; 7 | using TwitchBotDb.Models; 8 | 9 | namespace TwitchBot.Api.Controllers 10 | { 11 | [Route("api/[controller]/[action]")] 12 | [ApiController] 13 | public class BossFightSettingsController : ExtendedControllerBase 14 | { 15 | private readonly SimpleBotContext _context; 16 | 17 | public BossFightSettingsController(SimpleBotContext context) 18 | { 19 | _context = context; 20 | } 21 | 22 | // GET: api/bossfightsettings/get/2 23 | [HttpGet("{broadcasterId}")] 24 | public async Task Get(int broadcasterId) 25 | { 26 | IsModelStateValid(); 27 | 28 | BossFightSetting? bossFightSetting = await _context.BossFightSettings 29 | .SingleOrDefaultAsync(m => m.BroadcasterId == broadcasterId); 30 | 31 | if (bossFightSetting == null) 32 | { 33 | throw new NotFoundException("Boss fight settings not found"); 34 | } 35 | 36 | return Ok(bossFightSetting); 37 | } 38 | 39 | // PUT: api/bossfightsettings/update/2 40 | [HttpPut("{broadcasterId}")] 41 | public async Task Update(int broadcasterId, [FromBody] BossFightSetting bossFightSetting) 42 | { 43 | IsModelStateValid(); 44 | 45 | if (broadcasterId != bossFightSetting.BroadcasterId) 46 | { 47 | throw new ApiException("Broadcaster ID does not match with boss fight class stats's broadcaster ID"); 48 | } 49 | 50 | _context.Entry(bossFightSetting).State = EntityState.Modified; 51 | 52 | try 53 | { 54 | await _context.SaveChangesAsync(); 55 | } 56 | catch (DbUpdateConcurrencyException) 57 | { 58 | if (!BossFightSettingExists(broadcasterId)) 59 | { 60 | throw new NotFoundException("Boss fight settings not found"); 61 | } 62 | else 63 | { 64 | throw; 65 | } 66 | } 67 | 68 | return NoContent(); 69 | } 70 | 71 | // POST: api/bossfightsettings/create 72 | // Body (JSON): { "broadcaster": 2 } 73 | [HttpPost] 74 | public async Task Create([FromBody] BossFightSetting bossFightSetting) 75 | { 76 | IsModelStateValid(); 77 | 78 | _context.BossFightSettings.Add(bossFightSetting); 79 | await _context.SaveChangesAsync(); 80 | 81 | return CreatedAtAction("Get", new { broadcasterId = bossFightSetting.BroadcasterId }, bossFightSetting); 82 | } 83 | 84 | private bool BossFightSettingExists(int broadcasterId) 85 | { 86 | return _context.BossFightSettings.Any(e => e.BroadcasterId == broadcasterId); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotShared/ClientLibraries/Singletons/YouTubeClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | using Google.Apis.Auth.OAuth2; 7 | using Google.Apis.Services; 8 | using Google.Apis.Util.Store; 9 | using Google.Apis.YouTube.v3; 10 | 11 | using TwitchBotShared.ApiLibraries; 12 | using TwitchBotShared.Extensions; 13 | 14 | namespace TwitchBotShared.ClientLibraries.Singletons 15 | { 16 | public class YoutubeClient : YoutubeClientApi 17 | { 18 | private static volatile YoutubeClient _instance; 19 | private static object _syncRoot = new object(); 20 | 21 | private ErrorHandler _errHndlrInstance = ErrorHandler.Instance; 22 | 23 | public bool HasCredentials { get; set; } 24 | 25 | public static YoutubeClient Instance 26 | { 27 | get 28 | { 29 | // first check 30 | if (_instance == null) 31 | { 32 | lock (_syncRoot) 33 | { 34 | // second check 35 | if (_instance == null) 36 | _instance = new YoutubeClient(); 37 | } 38 | } 39 | 40 | return _instance; 41 | } 42 | } 43 | 44 | protected override YouTubeService YouTubeService { get; set; } 45 | 46 | /// 47 | /// Get access and refresh tokens from user's account 48 | /// 49 | /// 50 | /// 51 | public override async Task GetAuthAsync(string youTubeClientId, string youTubeClientSecret) 52 | { 53 | try 54 | { 55 | string clientSecrets = @"{ 'installed': {'client_id': '" + youTubeClientId + "', 'client_secret': '" + youTubeClientSecret + "'} }"; 56 | 57 | UserCredential credential; 58 | using (Stream stream = clientSecrets.ToStream()) 59 | { 60 | credential = await GoogleWebAuthorizationBroker.AuthorizeAsync( 61 | GoogleClientSecrets.FromStream(stream).Secrets, 62 | new[] { YouTubeService.Scope.Youtube }, 63 | "user", 64 | CancellationToken.None, 65 | new FileDataStore("Twitch Bot") 66 | ); 67 | } 68 | 69 | YouTubeService = new YouTubeService(new BaseClientService.Initializer() 70 | { 71 | HttpClientInitializer = credential, 72 | ApplicationName = "Twitch Bot" 73 | }); 74 | 75 | return true; 76 | } 77 | catch (Exception ex) 78 | { 79 | if (!ex.Message.Contains("access_denied")) // record unknown error 80 | await _errHndlrInstance.LogErrorAsync(ex, "YouTubeClient", "GetAuth(string, string)", false); 81 | 82 | return false; 83 | } 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotShared/Commands/SharedCommands.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | 4 | using SpotifyAPI.Web; 5 | 6 | using TwitchBotShared.ClientLibraries; 7 | using TwitchBotShared.Extensions; 8 | using TwitchBotShared.Models; 9 | 10 | namespace TwitchBotShared.Commands 11 | { 12 | public static class SharedCommands 13 | { 14 | /// 15 | /// Displays the last song played from Spotify 16 | /// 17 | /// User that sent the message 18 | public static async Task SpotifyLastPlayedSongAsync(TwitchChatter chatter, SpotifyWebClient spotify) 19 | { 20 | FullTrack fullTrack = await spotify.GetLastPlayedSongAsync(); 21 | if (fullTrack != null) 22 | { 23 | string artistName = ""; 24 | 25 | foreach (SimpleArtist simpleArtist in fullTrack.Artists) 26 | { 27 | artistName += $"{simpleArtist.Name}, "; 28 | } 29 | 30 | artistName = artistName.ReplaceLastOccurrence(", ", ""); 31 | 32 | return $"@{chatter.DisplayName} <-- Last played from Spotify: \"{fullTrack.Name}\" by {artistName} " 33 | + "https://open.spotify.com/track/" + fullTrack.Id + " WARNING: This is currently a feature in BETA --> " 34 | + "https://developer.spotify.com/documentation/web-api/reference/player/get-recently-played/"; 35 | } 36 | else 37 | { 38 | return $"Nothing was played recently @{chatter.DisplayName}"; 39 | } 40 | } 41 | 42 | /// 43 | /// Displays the current song being played from Spotify 44 | /// 45 | /// User that sent the message 46 | public static async Task SpotifyCurrentSongAsync(TwitchChatter chatter, SpotifyWebClient spotify) 47 | { 48 | CurrentlyPlayingContext playbackContext = await spotify.GetPlaybackAsync(); 49 | if (playbackContext != null && playbackContext.IsPlaying) 50 | { 51 | string artistName = ""; 52 | FullTrack fullTrack = (FullTrack)playbackContext.Item; 53 | 54 | foreach (SimpleArtist simpleArtist in fullTrack.Artists) 55 | { 56 | artistName += $"{simpleArtist.Name}, "; 57 | } 58 | 59 | artistName = artistName.ReplaceLastOccurrence(", ", ""); 60 | 61 | TimeSpan progressTimeSpan = TimeSpan.FromMilliseconds(playbackContext.ProgressMs); 62 | TimeSpan durationTimeSpan = TimeSpan.FromMilliseconds(fullTrack.DurationMs); 63 | 64 | return $"@{chatter.DisplayName} <-- Now playing from Spotify: \"{fullTrack.Name}\" by {artistName} " 65 | + "https://open.spotify.com/track/" + fullTrack.Id + " " 66 | + $"Currently playing at {progressTimeSpan.ReformatTimeSpan()} of {durationTimeSpan.ReformatTimeSpan()}"; 67 | } 68 | else 69 | { 70 | return $"Nothing is playing at the moment @{chatter.DisplayName}"; 71 | } 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotShared/ClientLibraries/Singletons/JoinStreamerSingleton.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | using TwitchBotShared.Models; 5 | 6 | namespace TwitchBotShared.ClientLibraries.Singletons 7 | { 8 | public class JoinStreamerSingleton 9 | { 10 | private static volatile JoinStreamerSingleton _instance; 11 | private static object _syncRoot = new object(); 12 | 13 | private Queue _joinStreamerList = new Queue(); 14 | 15 | private JoinStreamerSingleton() { } 16 | 17 | public static JoinStreamerSingleton Instance 18 | { 19 | get 20 | { 21 | // first check 22 | if (_instance == null) 23 | { 24 | lock (_syncRoot) 25 | { 26 | // second check 27 | if (_instance == null) 28 | _instance = new JoinStreamerSingleton(); 29 | } 30 | } 31 | 32 | return _instance; 33 | } 34 | } 35 | 36 | /// 37 | /// Put a cooldown for a user on a command 38 | /// 39 | /// 40 | public string Invite(TwitchChatter chatter) 41 | { 42 | if (_joinStreamerList.Contains(chatter.Username)) 43 | { 44 | return $"Don't worry @{chatter.DisplayName}. You're on the list to play with " + 45 | $"the streamer with your current position at {_joinStreamerList.ToList().IndexOf(chatter.Username) + 1} " + 46 | $"of {_joinStreamerList.Count} user(s)"; 47 | } 48 | else 49 | { 50 | _joinStreamerList.Enqueue(chatter.Username); 51 | 52 | return $"Congrats @{chatter.DisplayName}! You're currently in line with your current position at " + 53 | $"{_joinStreamerList.ToList().IndexOf(chatter.Username) + 1}"; 54 | } 55 | } 56 | 57 | public void ResetList() 58 | { 59 | _joinStreamerList.Clear(); 60 | } 61 | 62 | public string ListJoin() 63 | { 64 | if (_joinStreamerList.Count == 0) 65 | { 66 | return $"No one wants to play with the streamer at the moment. Be the first to play with !join"; 67 | } 68 | 69 | // Show list of queued users 70 | string message = $"List of users waiting to play with the streamer (in order from left to right): < "; 71 | 72 | foreach (string user in _joinStreamerList) 73 | { 74 | message += user + " >< "; 75 | } 76 | 77 | return message; 78 | } 79 | 80 | public string PopJoin(TwitchChatter chatter) 81 | { 82 | if (_joinStreamerList.Count == 0) 83 | { 84 | return $"Queue is empty @{chatter.DisplayName}"; 85 | } 86 | else 87 | { 88 | string poppedUser = _joinStreamerList.Dequeue(); 89 | return $"{poppedUser} has been removed from the queue @{chatter.DisplayName}"; 90 | } 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotShared/Threads/TwitchStreamStatus.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | using TwitchBotShared.ClientLibraries.Singletons; 6 | using TwitchBotShared.ClientLibraries; 7 | using TwitchBotShared.Models; 8 | using TwitchBotShared.Models.JSON; 9 | 10 | namespace TwitchBotShared.Threads 11 | { 12 | public class TwitchStreamStatus 13 | { 14 | private readonly IrcClient _irc; 15 | private readonly Thread _checkStreamStatus; 16 | private readonly TwitchInfoService _twitchInfo; 17 | private readonly string _broadcasterName; 18 | private readonly DelayedMessageSingleton _delayedMessagesInstance = DelayedMessageSingleton.Instance; 19 | 20 | public static bool IsLive { get; private set; } = false; 21 | public static string CurrentCategory { get; private set; } 22 | public static string CurrentTitle { get; private set; } 23 | 24 | public TwitchStreamStatus(IrcClient irc, TwitchInfoService twitchInfo, string broadcasterName) 25 | { 26 | _irc = irc; 27 | _twitchInfo = twitchInfo; 28 | _broadcasterName = broadcasterName; 29 | _checkStreamStatus = new Thread(new ThreadStart(this.Run)); 30 | } 31 | 32 | #region Public Methods 33 | public void Start() 34 | { 35 | _checkStreamStatus.IsBackground = true; 36 | _checkStreamStatus.Start(); 37 | } 38 | 39 | public async Task LoadChannelInfoAsync() 40 | { 41 | ChannelJSON channelJSON = await _twitchInfo.GetBroadcasterChannelByIdAsync(); 42 | 43 | if (channelJSON != null) 44 | { 45 | CurrentCategory = channelJSON.GameName; 46 | CurrentTitle = channelJSON.Title; 47 | } 48 | } 49 | #endregion 50 | 51 | private async void Run() 52 | { 53 | while (true) 54 | { 55 | StreamJSON streamJSON = await _twitchInfo.GetBroadcasterStreamAsync(); 56 | 57 | if (streamJSON == null) 58 | { 59 | if (IsLive) 60 | { 61 | // ToDo: Clear greeted user list 62 | } 63 | 64 | IsLive = false; 65 | } 66 | else 67 | { 68 | CurrentCategory = streamJSON.GameName; 69 | CurrentTitle = streamJSON.Title; 70 | 71 | // Tell the chat the stream is now live 72 | if (!IsLive) 73 | { 74 | // ToDo: Add setting if user wants preset reminder 75 | _delayedMessagesInstance.DelayedMessages.Add(new DelayedMessage 76 | { 77 | Message = $"Did you remind Twitter you're \"!live\"? @{_broadcasterName}", 78 | SendDate = DateTime.Now.AddMinutes(5) 79 | }); 80 | 81 | _irc.SendPublicChatMessage($"Live on Twitch playing {CurrentCategory} \"{CurrentTitle}\""); 82 | } 83 | 84 | IsLive = true; 85 | } 86 | 87 | Thread.Sleep(15000); // check every 15 seconds 88 | } 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBot.Api/Controllers/SongRequestsController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.EntityFrameworkCore; 3 | 4 | using TwitchBot.Api.Helpers; 5 | using TwitchBot.Api.Helpers.ErrorExceptions; 6 | using TwitchBotDb.Context; 7 | using TwitchBotDb.Models; 8 | 9 | namespace TwitchBot.Api.Controllers 10 | { 11 | [Route("api/[controller]/[action]")] 12 | [ApiController] 13 | public class SongRequestsController : ExtendedControllerBase 14 | { 15 | private readonly SimpleBotContext _context; 16 | 17 | public SongRequestsController(SimpleBotContext context) 18 | { 19 | _context = context; 20 | } 21 | 22 | // GET: api/songrequests/5 23 | [HttpGet("{broadcasterId:int}")] 24 | public async Task Get([FromRoute] int broadcasterId) 25 | { 26 | IsModelStateValid(); 27 | 28 | List songRequests = await _context.SongRequests.Where(m => m.BroadcasterId == broadcasterId).ToListAsync(); 29 | 30 | if (songRequests == null) 31 | { 32 | throw new NotFoundException("Song requests cannot be found"); 33 | } 34 | 35 | return Ok(songRequests); 36 | } 37 | 38 | // POST: api/songrequests/create 39 | // Body (JSON): 40 | [HttpPost] 41 | public async Task Create([FromBody] SongRequest songRequests) 42 | { 43 | IsModelStateValid(); 44 | 45 | _context.SongRequests.Add(songRequests); 46 | await _context.SaveChangesAsync(); 47 | 48 | return CreatedAtAction("Get", new { broadcasterId = songRequests.BroadcasterId }, songRequests); 49 | } 50 | 51 | // DELETE: api/songrequests/2 52 | // DELETE: api/songrequests/2?popOne=true 53 | [HttpDelete("{broadcasterId:int}")] 54 | public async Task Delete([FromRoute] int broadcasterId, [FromQuery] bool popOne = false) 55 | { 56 | IsModelStateValid(); 57 | 58 | object songRequests = new object(); 59 | 60 | if (popOne) 61 | { 62 | SongRequest? songRequest = await _context.SongRequests 63 | .Where(m => m.BroadcasterId == broadcasterId) 64 | .OrderBy(m => m.Id) 65 | .Take(1) 66 | .SingleOrDefaultAsync(); 67 | 68 | if (songRequest == null) 69 | { 70 | throw new NotFoundException("Song request cannot be found"); 71 | } 72 | 73 | _context.SongRequests.Remove(songRequest); 74 | 75 | songRequests = songRequest; 76 | } 77 | else 78 | { 79 | List removedSong = await _context.SongRequests 80 | .Where(m => m.BroadcasterId == broadcasterId) 81 | .ToListAsync(); 82 | 83 | if (removedSong == null || removedSong.Count == 0) 84 | { 85 | throw new NotFoundException("Song requests cannot be found"); 86 | } 87 | 88 | _context.SongRequests.RemoveRange(removedSong); 89 | 90 | songRequests = removedSong; 91 | } 92 | 93 | await _context.SaveChangesAsync(); 94 | 95 | return Ok(songRequests); 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotConsoleApp/TwitchBotModule.cs: -------------------------------------------------------------------------------- 1 | using System.Configuration; 2 | 3 | using Autofac; 4 | 5 | using TwitchBotDb.Repositories; 6 | using TwitchBotDb.Services; 7 | 8 | using TwitchBotShared.ClientLibraries; 9 | using TwitchBotShared.Config; 10 | using TwitchBotShared.Threads; 11 | 12 | namespace TwitchBotConsoleApp.Modules 13 | { 14 | public class TwitchBotModule : Module 15 | { 16 | public Configuration AppConfig { get; set; } 17 | public Autofac.Core.Parameter TwitchBotApiLink { get; set; } 18 | public TwitchBotConfigurationSection TwitchBotConfigurationSection { get; set; } 19 | public IrcClient Irc { get; set; } 20 | 21 | protected override void Load(ContainerBuilder builder) 22 | { 23 | // configuration 24 | builder.RegisterInstance(AppConfig); 25 | builder.RegisterInstance(TwitchBotConfigurationSection); 26 | builder.RegisterInstance(Irc); 27 | 28 | // main app 29 | builder.RegisterType(); 30 | 31 | // repositories 32 | builder.RegisterType() 33 | .WithParameter(TwitchBotApiLink); 34 | builder.RegisterType() 35 | .WithParameter(TwitchBotApiLink); 36 | builder.RegisterType() 37 | .WithParameter(TwitchBotApiLink); 38 | builder.RegisterType() 39 | .WithParameter(TwitchBotApiLink); 40 | builder.RegisterType() 41 | .WithParameter(TwitchBotApiLink); 42 | builder.RegisterType() 43 | .WithParameter(TwitchBotApiLink); 44 | builder.RegisterType() 45 | .WithParameter(TwitchBotApiLink); 46 | builder.RegisterType() 47 | .WithParameter(TwitchBotApiLink); 48 | builder.RegisterType() 49 | .WithParameter(TwitchBotApiLink); 50 | builder.RegisterType() 51 | .WithParameter(TwitchBotApiLink); 52 | 53 | // services 54 | builder.RegisterType(); 55 | builder.RegisterType(); 56 | builder.RegisterType(); 57 | builder.RegisterType(); 58 | builder.RegisterType(); 59 | builder.RegisterType(); 60 | builder.RegisterType(); 61 | builder.RegisterType(); 62 | builder.RegisterType(); 63 | builder.RegisterType(); 64 | builder.RegisterType(); 65 | 66 | // threads 67 | builder.RegisterType() 68 | .WithParameter(TwitchBotApiLink); 69 | builder.RegisterType(); 70 | builder.RegisterType() 71 | .WithParameter(TwitchBotApiLink); 72 | builder.RegisterType() 73 | .WithParameter(TwitchBotApiLink); 74 | builder.RegisterType(); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/Services/PartyUpService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | 5 | using TwitchBotDb.DTO; 6 | using TwitchBotDb.Models; 7 | using TwitchBotDb.Repositories; 8 | 9 | namespace TwitchBotDb.Services 10 | { 11 | public class PartyUpService 12 | { 13 | private readonly PartyUpRepository _partyUpDb; 14 | 15 | public PartyUpService(PartyUpRepository partyUpDb) 16 | { 17 | _partyUpDb = partyUpDb; 18 | } 19 | 20 | public async Task HasUserAlreadyRequestedAsync(string username, int gameId, int broadcasterId) 21 | { 22 | List partyRequestList = await _partyUpDb.GetRequestListAsync(gameId, broadcasterId); 23 | 24 | return partyRequestList.Any(m => m.Username == username); 25 | } 26 | 27 | public async Task GetPartyMemberAsync(string partyMember, int gameId, int broadcasterId) 28 | { 29 | return await _partyUpDb.GetPartyMemberAsync(partyMember, gameId, broadcasterId); 30 | } 31 | 32 | public async Task AddRequestedPartyMemberAsync(string username, int partyMemberId) 33 | { 34 | await _partyUpDb.AddRequestedPartyMemberAsync(username, partyMemberId); 35 | } 36 | 37 | public async Task GetPartyListAsync(int gameId, int broadcasterId) 38 | { 39 | List partyList = await _partyUpDb.GetPartyListAsync(gameId, broadcasterId); 40 | 41 | if (partyList == null || partyList.Count == 0) 42 | { 43 | return "No party members are set for this game"; 44 | } 45 | 46 | string message = "The available party members are: "; 47 | 48 | foreach (string member in partyList) 49 | { 50 | message += member + " >< "; 51 | } 52 | 53 | message = message.Substring(0, message.Length - 4); 54 | 55 | return message; 56 | } 57 | 58 | public async Task GetRequestListAsync(int gameId, int broadcasterId) 59 | { 60 | List partyRequestList = await _partyUpDb.GetRequestListAsync(gameId, broadcasterId); 61 | 62 | if (partyRequestList == null || partyRequestList.Count == 0) 63 | { 64 | return "The party request list is empty. Request a member with !partyup [name]"; 65 | } 66 | 67 | string message = "Here are the requested party members: "; 68 | 69 | foreach (PartyUpRequestResult member in partyRequestList) 70 | { 71 | message += member.PartyMemberName + " <-- " + member.Username + " || "; 72 | } 73 | 74 | message = message.Substring(0, message.Length - 4); 75 | 76 | return message; 77 | } 78 | 79 | public async Task PopRequestedPartyMemberAsync(int gameId, int broadcasterId) 80 | { 81 | PartyUpRequestResult firstPartyMember = await _partyUpDb.PopRequestedPartyMemberAsync(gameId, broadcasterId); 82 | 83 | if (firstPartyMember == null) 84 | { 85 | return "There are no party members that can be removed from the request list"; 86 | } 87 | 88 | return $"The requested party member, \"{firstPartyMember.PartyMemberName}\" from @{firstPartyMember.Username}, has been removed"; 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBot.Api/Controllers/WhitelistsController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.EntityFrameworkCore; 3 | 4 | using TwitchBot.Api.Helpers; 5 | using TwitchBot.Api.Helpers.ErrorExceptions; 6 | using TwitchBotDb.Context; 7 | using TwitchBotDb.Models; 8 | 9 | namespace TwitchBot.Api.Controllers 10 | { 11 | [Route("api/[controller]/[action]")] 12 | [ApiController] 13 | public class WhitelistsController : ExtendedControllerBase 14 | { 15 | private readonly SimpleBotContext _context; 16 | 17 | public WhitelistsController(SimpleBotContext context) 18 | { 19 | _context = context; 20 | } 21 | 22 | // GET: api/Whitelists 23 | [HttpGet] 24 | public async Task>> GetWhitelist() 25 | { 26 | return await _context.Whitelists.ToListAsync(); 27 | } 28 | 29 | // GET: api/Whitelists/5 30 | [HttpGet("{id}")] 31 | public async Task> GetWhitelist(int id) 32 | { 33 | IsModelStateValid(); 34 | 35 | Whitelist? whitelist = await _context.Whitelists.FindAsync(id); 36 | 37 | if (whitelist == null) 38 | { 39 | throw new NotFoundException("Whitelist cannot be found"); 40 | } 41 | 42 | return whitelist; 43 | } 44 | 45 | // PUT: api/Whitelists/5 46 | [HttpPut("{id}")] 47 | public async Task PutWhitelist(int id, Whitelist whitelist) 48 | { 49 | IsModelStateValid(); 50 | 51 | if (id != whitelist.Id) 52 | { 53 | throw new ApiException("ID does not match whitelist's ID"); 54 | } 55 | 56 | _context.Entry(whitelist).State = EntityState.Modified; 57 | 58 | try 59 | { 60 | await _context.SaveChangesAsync(); 61 | } 62 | catch (DbUpdateConcurrencyException) 63 | { 64 | if (!WhitelistExists(id)) 65 | { 66 | throw new NotFoundException("Whitelist cannot be found"); 67 | } 68 | else 69 | { 70 | throw; 71 | } 72 | } 73 | 74 | return NoContent(); 75 | } 76 | 77 | // POST: api/Whitelists 78 | [HttpPost] 79 | public async Task> PostWhitelist(Whitelist whitelist) 80 | { 81 | IsModelStateValid(); 82 | 83 | _context.Whitelists.Add(whitelist); 84 | await _context.SaveChangesAsync(); 85 | 86 | return CreatedAtAction("GetWhitelist", new { id = whitelist.Id }, whitelist); 87 | } 88 | 89 | // DELETE: api/Whitelists/5 90 | [HttpDelete("{id}")] 91 | public async Task> DeleteWhitelist(int id) 92 | { 93 | IsModelStateValid(); 94 | 95 | Whitelist? whitelist = await _context.Whitelists.FindAsync(id); 96 | if (whitelist == null) 97 | { 98 | throw new NotFoundException("Whitelist cannot be found"); 99 | } 100 | 101 | _context.Whitelists.Remove(whitelist); 102 | await _context.SaveChangesAsync(); 103 | 104 | return whitelist; 105 | } 106 | 107 | private bool WhitelistExists(int id) 108 | { 109 | return _context.Whitelists.Any(e => e.Id == id); 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBot.Api/Controllers/BossFightBossStatsController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.EntityFrameworkCore; 3 | 4 | using TwitchBot.Api.Helpers; 5 | using TwitchBot.Api.Helpers.ErrorExceptions; 6 | using TwitchBotDb.Context; 7 | using TwitchBotDb.Models; 8 | 9 | namespace TwitchBot.Api.Controllers 10 | { 11 | [Route("api/[controller]/[action]")] 12 | [ApiController] 13 | public class BossFightBossStatsController : ExtendedControllerBase 14 | { 15 | private readonly SimpleBotContext _context; 16 | 17 | public BossFightBossStatsController(SimpleBotContext context) 18 | { 19 | _context = context; 20 | } 21 | 22 | // GET: api/bossfightbossstats/get/1 23 | // GET: api/bossfightbossstats/get/1?gameId=1 24 | [HttpGet("{settingsId}")] 25 | public async Task Get(int settingsId, [FromQuery] int? gameId = null) 26 | { 27 | IsModelStateValid(); 28 | 29 | BossFightBossStats? bossFightBossStats = await _context.BossFightBossStats 30 | .SingleOrDefaultAsync(m => m.SettingsId == settingsId && m.GameId == gameId); 31 | 32 | if (bossFightBossStats == null) 33 | { 34 | // User hasn't set the boss stats for a particular game that is in the game list 35 | // Try to get their general settings as a fallback 36 | bossFightBossStats = await _context.BossFightBossStats 37 | .SingleOrDefaultAsync(m => m.SettingsId == settingsId && m.GameId == null); 38 | 39 | if (bossFightBossStats == null) 40 | { 41 | throw new NotFoundException("Cannot find boss fight boss stats"); 42 | } 43 | } 44 | 45 | return Ok(bossFightBossStats); 46 | } 47 | 48 | // PUT: api/bossfightbossstats/update/1?id=1 49 | [HttpPut("{settingsId}")] 50 | public async Task Update(int settingsId, [FromQuery] int id, [FromBody] BossFightBossStats bossFightBossStats) 51 | { 52 | IsModelStateValid(); 53 | 54 | if (id != bossFightBossStats.Id && settingsId != bossFightBossStats.SettingsId) 55 | { 56 | throw new ApiException("Settings ID does not match with boss fight boss stats's settings ID"); 57 | } 58 | 59 | _context.Entry(bossFightBossStats).State = EntityState.Modified; 60 | 61 | try 62 | { 63 | await _context.SaveChangesAsync(); 64 | } 65 | catch (DbUpdateConcurrencyException) 66 | { 67 | if (!BossFightBossStatsExists(id)) 68 | { 69 | throw new NotFoundException("Cannot find boss fight boss stats"); 70 | } 71 | else 72 | { 73 | throw; 74 | } 75 | } 76 | 77 | return NoContent(); 78 | } 79 | 80 | // POST: api/bossfightbossstats/create 81 | [HttpPost] 82 | public async Task Create([FromBody] BossFightBossStats bossFightBossStats) 83 | { 84 | IsModelStateValid(); 85 | 86 | _context.BossFightBossStats.Add(bossFightBossStats); 87 | await _context.SaveChangesAsync(); 88 | 89 | return CreatedAtAction("Get", new { settingsId = bossFightBossStats.Id, gameId = bossFightBossStats.GameId }, bossFightBossStats); 90 | } 91 | 92 | private bool BossFightBossStatsExists(int id) 93 | { 94 | return _context.BossFightBossStats.Any(e => e.Id == id); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotDb/SqlScripts/StoredProcedures/dbo.CreateDefaultRanks.sql: -------------------------------------------------------------------------------- 1 | CREATE PROCEDURE [dbo].[CreateDefaultRanks] 2 | @BroadcasterId INT 3 | AS 4 | BEGIN 5 | -- SET NOCOUNT ON added to prevent extra result sets from 6 | -- interfering with SELECT statements. 7 | SET NOCOUNT ON 8 | 9 | IF NOT EXISTS (SELECT Name FROM dbo.Rank WHERE BroadcasterId = @BroadcasterId) 10 | BEGIN 11 | INSERT INTO dbo.Rank (Name, ExpCap, BroadcasterId) VALUES ('Recruit', 120, @BroadcasterId) 12 | INSERT INTO dbo.Rank (Name, ExpCap, BroadcasterId) VALUES ('Private', 240, @BroadcasterId) 13 | INSERT INTO dbo.Rank (Name, ExpCap, BroadcasterId) VALUES ('Private First Class', 480, @BroadcasterId) 14 | INSERT INTO dbo.Rank (Name, ExpCap, BroadcasterId) VALUES ('Sergeant', 960, @BroadcasterId) 15 | INSERT INTO dbo.Rank (Name, ExpCap, BroadcasterId) VALUES ('Staff Sergeant', 1920, @BroadcasterId) 16 | INSERT INTO dbo.Rank (Name, ExpCap, BroadcasterId) VALUES ('Technical Sergeant', 3840, @BroadcasterId) 17 | INSERT INTO dbo.Rank (Name, ExpCap, BroadcasterId) VALUES ('Master Sergeant', 7680, @BroadcasterId) 18 | INSERT INTO dbo.Rank (Name, ExpCap, BroadcasterId) VALUES ('First Master Sergeant', 11520, @BroadcasterId) 19 | INSERT INTO dbo.Rank (Name, ExpCap, BroadcasterId) VALUES ('Senior Master Sergeant', 14400, @BroadcasterId) 20 | INSERT INTO dbo.Rank (Name, ExpCap, BroadcasterId) VALUES ('First Senior Master Sergeant', 18000, @BroadcasterId) 21 | INSERT INTO dbo.Rank (Name, ExpCap, BroadcasterId) VALUES ('Chief Master Sergeant', 22500, @BroadcasterId) 22 | INSERT INTO dbo.Rank (Name, ExpCap, BroadcasterId) VALUES ('First Chief Master Sergeant', 28140, @BroadcasterId) 23 | INSERT INTO dbo.Rank (Name, ExpCap, BroadcasterId) VALUES ('Sergeant Major', 35160, @BroadcasterId) 24 | INSERT INTO dbo.Rank (Name, ExpCap, BroadcasterId) VALUES ('Sergeant Major of the Army', 43920, @BroadcasterId) 25 | INSERT INTO dbo.Rank (Name, ExpCap, BroadcasterId) VALUES ('Warrant Officer 1', 50520, @BroadcasterId) 26 | INSERT INTO dbo.Rank (Name, ExpCap, BroadcasterId) VALUES ('Warrant Officer 2', 58140, @BroadcasterId) 27 | INSERT INTO dbo.Rank (Name, ExpCap, BroadcasterId) VALUES ('Warrant Officer 3', 66840, @BroadcasterId) 28 | INSERT INTO dbo.Rank (Name, ExpCap, BroadcasterId) VALUES ('Warrant Officer 4', 75540, @BroadcasterId) 29 | INSERT INTO dbo.Rank (Name, ExpCap, BroadcasterId) VALUES ('Chief Warrant Officer', 85320, @BroadcasterId) 30 | INSERT INTO dbo.Rank (Name, ExpCap, BroadcasterId) VALUES ('Second Lieutenant', 96420, @BroadcasterId) 31 | INSERT INTO dbo.Rank (Name, ExpCap, BroadcasterId) VALUES ('First Lieutenant', 108960, @BroadcasterId) 32 | INSERT INTO dbo.Rank (Name, ExpCap, BroadcasterId) VALUES ('Captain', 123120, @BroadcasterId) 33 | INSERT INTO dbo.Rank (Name, ExpCap, BroadcasterId) VALUES ('Major', 136680, @BroadcasterId) 34 | INSERT INTO dbo.Rank (Name, ExpCap, BroadcasterId) VALUES ('Lieutenant Colonel', 151740, @BroadcasterId) 35 | INSERT INTO dbo.Rank (Name, ExpCap, BroadcasterId) VALUES ('Colonel', 165360, @BroadcasterId) 36 | INSERT INTO dbo.Rank (Name, ExpCap, BroadcasterId) VALUES ('Brigadier General', 180240, @BroadcasterId) 37 | INSERT INTO dbo.Rank (Name, ExpCap, BroadcasterId) VALUES ('Major General', 196500, @BroadcasterId) 38 | INSERT INTO dbo.Rank (Name, ExpCap, BroadcasterId) VALUES ('Lieutenant General', 214140, @BroadcasterId) 39 | INSERT INTO dbo.Rank (Name, ExpCap, BroadcasterId) VALUES ('General of the Army', 233460, @BroadcasterId) 40 | 41 | SELECT Id, Name, ExpCap, BroadcasterId FROM dbo.Rank WHERE BroadcasterId = @BroadcasterId 42 | END 43 | END 44 | GO -------------------------------------------------------------------------------- /TwitchBot/TwitchBotShared/ApiLibraries/ApiTwitchRequest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | using Newtonsoft.Json; 7 | 8 | using RestSharp; 9 | using RestSharp.Serializers; 10 | 11 | namespace TwitchBotShared.ApiLibraries 12 | { 13 | public class ApiTwitchRequest 14 | { 15 | public static async Task GetExecuteAsync(string basicUrl, string accessToken, string clientId) 16 | { 17 | try 18 | { 19 | RestClient client = new RestClient(basicUrl); 20 | RestRequest request = new RestRequest(); 21 | request.AddHeader("Cache-Control", "no-cache"); 22 | request.AddHeader("Content-Type", "application/json"); 23 | request.AddHeader("Authorization", $"Bearer {accessToken}"); 24 | request.AddHeader("Client-ID", clientId); 25 | request.Method = Method.Get; 26 | 27 | var cancellationToken = new CancellationTokenSource(); 28 | 29 | try 30 | { 31 | RestResponse response = await client.ExecuteAsync(request, cancellationToken.Token); 32 | string statResponse = response.StatusCode.ToString(); 33 | 34 | if (statResponse.Contains("OK") || statResponse.Contains("NoContent")) 35 | { 36 | return JsonConvert.DeserializeObject(response.Content); 37 | } 38 | else 39 | { 40 | Console.WriteLine(response.Content); 41 | } 42 | } 43 | catch (WebException ex) 44 | { 45 | Console.WriteLine(ex.Message); 46 | } 47 | } 48 | catch (Exception ex) 49 | { 50 | Console.WriteLine(ex.Message); 51 | } 52 | 53 | return default; 54 | } 55 | 56 | public static async Task PatchExecuteAsync(string basicUrl, string accessToken, string clientId, T updateObject) 57 | { 58 | try 59 | { 60 | RestClient client = new RestClient(basicUrl); 61 | RestRequest request = new RestRequest(); 62 | request.AddHeader("Cache-Control", "no-cache"); 63 | request.AddHeader("Content-Type", "application/json"); 64 | request.AddHeader("Authorization", $"Bearer {accessToken}"); 65 | request.AddHeader("Client-ID", clientId); 66 | request.AddStringBody(JsonConvert.SerializeObject(updateObject), ContentType.Json); 67 | request.Method = Method.Patch; 68 | 69 | var cancellationToken = new CancellationTokenSource(); 70 | 71 | try 72 | { 73 | RestResponse response = await client.ExecuteAsync(request, cancellationToken.Token); 74 | string statResponse = response.StatusCode.ToString(); 75 | 76 | if (statResponse.Contains("OK") || statResponse.Contains("NoContent")) 77 | { 78 | return JsonConvert.DeserializeObject(response.Content); 79 | } 80 | else 81 | { 82 | Console.WriteLine(response.Content); 83 | } 84 | } 85 | catch (WebException ex) 86 | { 87 | Console.WriteLine($"Error: {ex.Message}"); 88 | } 89 | } 90 | catch (Exception ex) 91 | { 92 | Console.WriteLine($"Error: {ex.Message}"); 93 | } 94 | 95 | return default; 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBot.Api/Controllers/SongRequestSettingsController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.EntityFrameworkCore; 3 | 4 | using TwitchBot.Api.Helpers; 5 | using TwitchBot.Api.Helpers.ErrorExceptions; 6 | using TwitchBotDb.Context; 7 | using TwitchBotDb.Models; 8 | 9 | namespace TwitchBot.Api.Controllers 10 | { 11 | [Route("api/[controller]/[action]")] 12 | [ApiController] 13 | public class SongRequestSettingsController : ExtendedControllerBase 14 | { 15 | private readonly SimpleBotContext _context; 16 | 17 | public SongRequestSettingsController(SimpleBotContext context) 18 | { 19 | _context = context; 20 | } 21 | 22 | // GET: api/songrequestsettings/get/5 23 | [HttpGet("{broadcasterId:int}")] 24 | public async Task Get([FromRoute] int broadcasterId) 25 | { 26 | IsModelStateValid(); 27 | 28 | SongRequestSetting? songRequestSetting = await _context.SongRequestSettings 29 | .SingleOrDefaultAsync(m => m.BroadcasterId == broadcasterId); 30 | 31 | if (songRequestSetting == null) 32 | { 33 | throw new ApiException("Song request setting cannot be found"); 34 | } 35 | 36 | return Ok(songRequestSetting); 37 | } 38 | 39 | // PUT: api/songrequestsettings/update/5 40 | [HttpPut("{broadcasterId:int}")] 41 | public async Task Update([FromRoute] int broadcasterId, [FromBody] SongRequestSetting songRequestSetting) 42 | { 43 | IsModelStateValid(); 44 | 45 | if (broadcasterId != songRequestSetting.BroadcasterId) 46 | { 47 | throw new ApiException("Broadcaster ID does not match song request setting's broadcaster ID"); 48 | } 49 | 50 | SongRequestSetting? updatedSongRequestSetting = await _context.SongRequestSettings 51 | .FirstOrDefaultAsync(m => m.BroadcasterId == broadcasterId); 52 | 53 | if (updatedSongRequestSetting == null) 54 | { 55 | throw new ApiException("Song request setting cannot be found"); 56 | } 57 | 58 | updatedSongRequestSetting.PersonalPlaylistId = songRequestSetting.PersonalPlaylistId; 59 | updatedSongRequestSetting.RequestPlaylistId = songRequestSetting.RequestPlaylistId; 60 | updatedSongRequestSetting.DjMode = songRequestSetting.DjMode; 61 | _context.SongRequestSettings.Update(updatedSongRequestSetting); 62 | 63 | try 64 | { 65 | await _context.SaveChangesAsync(); 66 | } 67 | catch (DbUpdateConcurrencyException) 68 | { 69 | if (!SongRequestSettingExists(broadcasterId)) 70 | { 71 | throw new ApiException("Song request setting cannot be found"); 72 | } 73 | else 74 | { 75 | throw; 76 | } 77 | } 78 | 79 | return NoContent(); 80 | } 81 | 82 | // POST: api/songrequestsettings/create 83 | // Body (JSON): 84 | [HttpPost] 85 | public async Task Create([FromBody] SongRequestSetting songRequestSetting) 86 | { 87 | IsModelStateValid(); 88 | 89 | _context.SongRequestSettings.Add(songRequestSetting); 90 | await _context.SaveChangesAsync(); 91 | 92 | return CreatedAtAction("Get", new { broadcasterId = songRequestSetting.BroadcasterId }, songRequestSetting); 93 | } 94 | 95 | private bool SongRequestSettingExists(int broadcasterId) 96 | { 97 | return _context.SongRequestSettings.Any(e => e.BroadcasterId == broadcasterId); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBot.Api/Controllers/RemindersController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.EntityFrameworkCore; 3 | 4 | using TwitchBot.Api.Helpers; 5 | using TwitchBot.Api.Helpers.ErrorExceptions; 6 | using TwitchBotDb.Context; 7 | using TwitchBotDb.Models; 8 | 9 | namespace TwitchBot.Api.Controllers 10 | { 11 | [Route("api/[controller]/[action]")] 12 | [ApiController] 13 | public class RemindersController : ExtendedControllerBase 14 | { 15 | private readonly SimpleBotContext _context; 16 | 17 | public RemindersController(SimpleBotContext context) 18 | { 19 | _context = context; 20 | } 21 | 22 | // GET: api/reminders/get/5 23 | // GET: api/reminders/get/5?id=1 24 | [HttpGet("{broadcasterId}")] 25 | public async Task Get(int broadcasterId, [FromQuery] int id = 0) 26 | { 27 | IsModelStateValid(); 28 | 29 | object? reminders = new object(); 30 | 31 | if (id == 0) 32 | reminders = await _context.Reminders.Where(m => m.BroadcasterId == broadcasterId).ToListAsync(); 33 | else 34 | reminders = await _context.Reminders.SingleOrDefaultAsync(m => m.BroadcasterId == broadcasterId && m.Id == id); 35 | 36 | if (reminders == null) 37 | { 38 | throw new NotFoundException("Reminder cannot be found"); 39 | } 40 | 41 | return Ok(reminders); 42 | } 43 | 44 | // PUT: api/reminders/update/5?broadcasterId=2 45 | [HttpPut("{id}")] 46 | public async Task Update(int id, [FromQuery] int broadcasterId, [FromBody] Reminder reminder) 47 | { 48 | IsModelStateValid(); 49 | 50 | if (id != reminder.Id && broadcasterId != reminder.BroadcasterId) 51 | { 52 | throw new ApiException("ID or broadcaster id does not match reminder's ID or broadcaster ID"); 53 | } 54 | 55 | _context.Entry(reminder).State = EntityState.Modified; 56 | 57 | try 58 | { 59 | await _context.SaveChangesAsync(); 60 | } 61 | catch (DbUpdateConcurrencyException) 62 | { 63 | if (!RemindersExists(id)) 64 | { 65 | throw new NotFoundException("Reminder cannot be found"); 66 | } 67 | else 68 | { 69 | throw; 70 | } 71 | } 72 | 73 | return NoContent(); 74 | } 75 | 76 | // POST: api/reminders/create 77 | [HttpPost] 78 | public async Task Create([FromBody] Reminder reminder) 79 | { 80 | IsModelStateValid(); 81 | 82 | _context.Reminders.Add(reminder); 83 | await _context.SaveChangesAsync(); 84 | 85 | return NoContent(); 86 | } 87 | 88 | // DELETE: api/reminders/delete/5?broadcasterId=2 89 | [HttpDelete("{id}")] 90 | public async Task Delete(int id, [FromQuery] int broadcasterId) 91 | { 92 | IsModelStateValid(); 93 | 94 | Reminder? reminder = await _context.Reminders 95 | .SingleOrDefaultAsync(m => m.Id == id && m.BroadcasterId == broadcasterId); 96 | 97 | if (reminder == null) 98 | { 99 | throw new NotFoundException("Reminder cannot be found"); 100 | } 101 | 102 | _context.Reminders.Remove(reminder); 103 | await _context.SaveChangesAsync(); 104 | 105 | return NoContent(); 106 | } 107 | 108 | private bool RemindersExists(int id) 109 | { 110 | return _context.Reminders.Any(e => e.Id == id); 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBot.Api/Controllers/BroadcastersController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.EntityFrameworkCore; 3 | 4 | using TwitchBot.Api.Helpers; 5 | using TwitchBot.Api.Helpers.ErrorExceptions; 6 | using TwitchBotDb.Context; 7 | using TwitchBotDb.Models; 8 | 9 | namespace TwitchBot.Api.Controllers 10 | { 11 | [Route("api/[controller]/[action]")] 12 | [ApiController] 13 | public class BroadcastersController : ExtendedControllerBase 14 | { 15 | private readonly SimpleBotContext _context; 16 | 17 | public BroadcastersController(SimpleBotContext context) 18 | { 19 | _context = context; 20 | } 21 | 22 | // GET: api/broadcasters/get/12345678 23 | // GET: api/broadcasters/get/12345678?username=simple_sandman 24 | [HttpGet("{twitchId}")] 25 | public async Task Get(int twitchId, [FromQuery] string? username = null) 26 | { 27 | IsModelStateValid(); 28 | 29 | Broadcaster? broadcaster = new Broadcaster(); 30 | 31 | if (!string.IsNullOrEmpty(username)) 32 | { 33 | broadcaster = await _context.Broadcasters 34 | .SingleOrDefaultAsync(m => m.Username == username && m.TwitchId == twitchId); 35 | } 36 | else 37 | { 38 | broadcaster = await _context.Broadcasters 39 | .SingleOrDefaultAsync(m => m.TwitchId == twitchId); 40 | } 41 | 42 | if (broadcaster == null) 43 | { 44 | throw new NotFoundException("Broadcaster not found"); 45 | } 46 | 47 | return Ok(broadcaster); 48 | } 49 | 50 | // PUT: api/broadcasters/update/12345678 51 | // Body (JSON): { "username": "simple_sandman", "twitchId": 12345678 } 52 | [HttpPut("{twitchId}")] 53 | public async Task Update(int twitchId, [FromBody] Broadcaster broadcaster) 54 | { 55 | IsModelStateValid(); 56 | 57 | if (twitchId != broadcaster.TwitchId) 58 | { 59 | throw new ApiException("Twitch ID does not match with the broadcaster's Twitch ID"); 60 | } 61 | 62 | Broadcaster? updatedbroadcaster = await _context.Broadcasters 63 | .FirstOrDefaultAsync(m => m.TwitchId == twitchId); 64 | 65 | if (updatedbroadcaster == null) 66 | { 67 | throw new NotFoundException("Broadcaster not found"); 68 | } 69 | 70 | updatedbroadcaster.Username = broadcaster.Username; 71 | _context.Broadcasters.Update(updatedbroadcaster); 72 | 73 | try 74 | { 75 | await _context.SaveChangesAsync(); 76 | } 77 | catch (DbUpdateConcurrencyException) 78 | { 79 | if (!BroadcasterExists(twitchId)) 80 | { 81 | throw new NotFoundException("Broadcaster not found"); 82 | } 83 | else 84 | { 85 | throw; 86 | } 87 | } 88 | 89 | return NoContent(); 90 | } 91 | 92 | // POST: api/broadcasters/create 93 | // Body (JSON): { "username": "simple_sandman", "twitchId": 12345678 } 94 | [HttpPost] 95 | public async Task Create([FromBody] Broadcaster broadcaster) 96 | { 97 | IsModelStateValid(); 98 | 99 | _context.Broadcasters.Add(broadcaster); 100 | await _context.SaveChangesAsync(); 101 | 102 | return CreatedAtAction("Get", new { twitchId = broadcaster.TwitchId }, broadcaster); 103 | } 104 | 105 | private bool BroadcasterExists(int twitchId) 106 | { 107 | return _context.Broadcasters.Any(e => e.TwitchId == twitchId); 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBot.Api/Controllers/QuotesController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.JsonPatch; 2 | using Microsoft.AspNetCore.JsonPatch.Adapters; 3 | using Microsoft.AspNetCore.Mvc; 4 | using Microsoft.EntityFrameworkCore; 5 | 6 | using TwitchBot.Api.Helpers; 7 | using TwitchBot.Api.Helpers.ErrorExceptions; 8 | using TwitchBotDb.Context; 9 | using TwitchBotDb.Models; 10 | 11 | namespace TwitchBot.Api.Controllers 12 | { 13 | [Route("api/[controller]/[action]")] 14 | [ApiController] 15 | public class QuotesController : ExtendedControllerBase 16 | { 17 | private readonly SimpleBotContext _context; 18 | 19 | public QuotesController(SimpleBotContext context) 20 | { 21 | _context = context; 22 | } 23 | 24 | // GET: api/quotes/get/5 25 | [HttpGet("{broadcasterId}")] 26 | public async Task Get(int broadcasterId) 27 | { 28 | IsModelStateValid(); 29 | 30 | List quote = await _context.Quotes.Where(m => m.BroadcasterId == broadcasterId).ToListAsync(); 31 | 32 | if (quote == null || quote.Count == 0) 33 | { 34 | throw new NotFoundException("Quote not found"); 35 | } 36 | 37 | return Ok(quote); 38 | } 39 | 40 | // PATCH: api/quotes/patch/5?broadcasterId=2 41 | // Body (JSON): [{ "op": "replace", "path": "/userquote", "value": "repalce old quote with this" }] 42 | [HttpPatch("{id}")] 43 | public async Task Patch(int id, [FromQuery] int broadcasterId, [FromBody] JsonPatchDocument quotePatch) 44 | { 45 | IsModelStateValid(); 46 | 47 | Quote? quote = await _context.Quotes.SingleOrDefaultAsync(m => m.Id == id && m.BroadcasterId == broadcasterId); 48 | 49 | if (quote == null) 50 | { 51 | throw new NotFoundException("Quote not found"); 52 | } 53 | 54 | quotePatch.ApplyTo(quote, (IObjectAdapter)ModelState); 55 | 56 | if (!ModelState.IsValid) 57 | { 58 | return new BadRequestObjectResult(ModelState); 59 | } 60 | 61 | _context.Quotes.Update(quote); 62 | 63 | try 64 | { 65 | await _context.SaveChangesAsync(); 66 | } 67 | catch (DbUpdateConcurrencyException) 68 | { 69 | if (!QuoteExists(id)) 70 | { 71 | throw new NotFoundException("Quote not found"); 72 | } 73 | else 74 | { 75 | throw; 76 | } 77 | } 78 | 79 | return NoContent(); 80 | } 81 | 82 | // POST: api/quotes/create 83 | // Body (JSON): { "userquote": "insert new broadcaster quote here", "username": "quoter", "broadcaster": "2" } 84 | [HttpPost] 85 | public async Task Create([FromBody] Quote quote) 86 | { 87 | IsModelStateValid(); 88 | 89 | _context.Quotes.Add(quote); 90 | await _context.SaveChangesAsync(); 91 | 92 | return NoContent(); 93 | } 94 | 95 | // DELETE: api/quotes/delete/5?broadcasterId=2 96 | [HttpDelete("{id}")] 97 | public async Task Delete(int id, [FromQuery] int broadcasterId) 98 | { 99 | IsModelStateValid(); 100 | 101 | Quote? quote = await _context.Quotes.SingleOrDefaultAsync(m => m.Id == id && m.BroadcasterId == broadcasterId); 102 | if (quote == null) 103 | { 104 | throw new NotFoundException("Quote not found"); 105 | } 106 | 107 | _context.Quotes.Remove(quote); 108 | await _context.SaveChangesAsync(); 109 | 110 | return NoContent(); 111 | } 112 | 113 | private bool QuoteExists(int id) 114 | { 115 | return _context.Quotes.Any(e => e.Id == id); 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBot.Api/Controllers/RankFollowersController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.EntityFrameworkCore; 3 | 4 | using TwitchBot.Api.Helpers; 5 | using TwitchBot.Api.Helpers.ErrorExceptions; 6 | using TwitchBotDb.Context; 7 | using TwitchBotDb.Models; 8 | 9 | namespace TwitchBot.Api.Controllers 10 | { 11 | [Route("api/[controller]/[action]")] 12 | [ApiController] 13 | public class RankFollowersController : ExtendedControllerBase 14 | { 15 | private readonly SimpleBotContext _context; 16 | 17 | public RankFollowersController(SimpleBotContext context) 18 | { 19 | _context = context; 20 | } 21 | 22 | // GET: api/rankfollowers/get/2?username=simple_sandman 23 | [HttpGet("{broadcasterId}")] 24 | public async Task Get(int broadcasterId, [FromQuery] string username) 25 | { 26 | IsModelStateValid(); 27 | 28 | RankFollower? rankFollower = await _context.RankFollowers 29 | .SingleOrDefaultAsync(m => m.BroadcasterId == broadcasterId && m.Username == username); 30 | 31 | if (rankFollower == null) 32 | { 33 | return Ok(new RankFollower { Username = username, Experience = -1, BroadcasterId = broadcasterId }); 34 | } 35 | 36 | return Ok(rankFollower); 37 | } 38 | 39 | // GET: api/rankfollowers/getleaderboard/2?topnumber=5 40 | [HttpGet("{broadcasterId}")] 41 | public async Task GetLeaderboard(int broadcasterId, [FromQuery] int topNumber = 3) 42 | { 43 | IsModelStateValid(); 44 | 45 | IEnumerable topFollowers = await _context.RankFollowers 46 | .Where(m => m.BroadcasterId == broadcasterId) 47 | .OrderByDescending(m => m.Experience) 48 | .Take(topNumber) 49 | .ToListAsync(); 50 | 51 | if (topFollowers == null || !topFollowers.Any()) 52 | { 53 | throw new NotFoundException("Top followers cannot be found"); 54 | } 55 | 56 | return Ok(topFollowers); 57 | } 58 | 59 | // PUT: api/rankfollowers/updateexp/2?username=simple_sandman&exp=9001 60 | [HttpPut("{broadcasterId}")] 61 | public async Task UpdateExp(int broadcasterId, [FromQuery] string username, [FromQuery] int exp) 62 | { 63 | IsModelStateValid(); 64 | 65 | RankFollower? follower = await _context.RankFollowers 66 | .FirstOrDefaultAsync(t => t.BroadcasterId == broadcasterId && t.Username == username); 67 | 68 | if (follower == null) 69 | { 70 | throw new NotFoundException("Follower cannot be found"); 71 | } 72 | 73 | follower.Experience = exp; 74 | _context.RankFollowers.Update(follower); 75 | 76 | try 77 | { 78 | await _context.SaveChangesAsync(); 79 | } 80 | catch (DbUpdateConcurrencyException) 81 | { 82 | if (!RankFollowersExists(broadcasterId, username)) 83 | { 84 | throw new NotFoundException("Follower cannot be found"); 85 | } 86 | else 87 | { 88 | throw; 89 | } 90 | } 91 | 92 | return NoContent(); 93 | } 94 | 95 | // POST: api/rankfollowers/create 96 | // Body (JSON): { "username": "simple_sandman", "broadcaster": 2 } 97 | [HttpPost] 98 | public async Task Create([FromBody] RankFollower rankFollowers) 99 | { 100 | IsModelStateValid(); 101 | 102 | _context.RankFollowers.Add(rankFollowers); 103 | await _context.SaveChangesAsync(); 104 | 105 | return NoContent(); 106 | } 107 | 108 | private bool RankFollowersExists(int broadcasterId, string username) 109 | { 110 | return _context.RankFollowers.Any(e => e.BroadcasterId == broadcasterId && e.Username == username); 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotShared/ClientLibraries/Singletons/ErrorHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | using TwitchBotDb; 6 | using TwitchBotDb.Models; 7 | 8 | using TwitchBotShared.Config; 9 | 10 | namespace TwitchBotShared.ClientLibraries.Singletons 11 | { 12 | // Using Singleton design pattern 13 | public sealed class ErrorHandler 14 | { 15 | private static ErrorHandler _instance; 16 | 17 | private static int _broadcasterId; 18 | private static IrcClient _irc; 19 | private static TwitchBotConfigurationSection _botConfig; 20 | 21 | static ErrorHandler() { _instance = new ErrorHandler(); } 22 | 23 | public static ErrorHandler Instance 24 | { 25 | get { return _instance; } 26 | } 27 | 28 | /// 29 | /// Used first chance that error logging can be possible 30 | /// 31 | public static void Configure(int broadcasterId, IrcClient irc, TwitchBotConfigurationSection botConfig) 32 | { 33 | _broadcasterId = broadcasterId; 34 | _irc = irc; 35 | _botConfig = botConfig; 36 | } 37 | 38 | public async Task LogErrorAsync(Exception ex, string className, string methodName, bool hasToExit, string botCmd = "N/A", string userMsg = "N/A") 39 | { 40 | Console.WriteLine("Error: " + ex.Message); 41 | 42 | try 43 | { 44 | /* If username not available, grab default user to show local error after db connection */ 45 | if (_broadcasterId == 0) 46 | { 47 | Broadcaster broadcaster = await ApiBotRequest.GetExecuteAsync(_botConfig.TwitchBotApiLink + $"broadcasters/get/-1"); 48 | _broadcasterId = broadcaster.Id; 49 | } 50 | 51 | /* Get line number from error message */ 52 | int lineNumber = 0; 53 | const string lineSearch = ":line "; 54 | int index = ex.StackTrace.LastIndexOf(lineSearch); 55 | 56 | if (index != -1) 57 | { 58 | string lineNumberText = ex.StackTrace.Substring(index + lineSearch.Length); 59 | if (!int.TryParse(lineNumberText, out lineNumber)) 60 | { 61 | lineNumber = -1; // couldn't parse line number 62 | } 63 | } 64 | 65 | ErrorLog error = new ErrorLog 66 | { 67 | ErrorTime = DateTime.UtcNow, 68 | ErrorLine = lineNumber, 69 | ErrorClass = className, 70 | ErrorMethod = methodName, 71 | ErrorMsg = ex.Message, 72 | BroadcasterId = _broadcasterId, 73 | Command = botCmd, 74 | UserMsg = userMsg 75 | }; 76 | 77 | await ApiBotRequest.PostExecuteAsync(_botConfig.TwitchBotApiLink + $"errorlogs/create", error); 78 | 79 | string publicErrMsg = "I ran into an unexpected internal error! " 80 | + "@" + _botConfig.Broadcaster + " please look into the error log when you have time"; 81 | 82 | if (hasToExit) 83 | publicErrMsg += ". I am leaving as well. Have a great time with this stream everyone KonCha"; 84 | 85 | if (_irc != null) 86 | _irc.SendPublicChatMessage(publicErrMsg); 87 | 88 | if (hasToExit) 89 | { 90 | Console.WriteLine(); 91 | Console.WriteLine("Shutting down now..."); 92 | Thread.Sleep(3000); 93 | Environment.Exit(1); 94 | } 95 | } 96 | catch (Exception e) 97 | { 98 | Console.WriteLine(); 99 | Console.WriteLine("Logging error found: " + e.Message); 100 | Console.WriteLine("Please inform author of this error!"); 101 | Thread.Sleep(5000); 102 | } 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBot.Api/Controllers/CustomCommandsController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.EntityFrameworkCore; 3 | 4 | using TwitchBot.Api.Helpers; 5 | using TwitchBot.Api.Helpers.ErrorExceptions; 6 | using TwitchBotDb.Context; 7 | using TwitchBotDb.Models; 8 | 9 | namespace TwitchBot.Api.Controllers 10 | { 11 | [Route("api/[controller]/[action]")] 12 | [ApiController] 13 | public class CustomCommandsController : ExtendedControllerBase 14 | { 15 | private readonly SimpleBotContext _context; 16 | 17 | public CustomCommandsController(SimpleBotContext context) 18 | { 19 | _context = context; 20 | } 21 | 22 | // GET: api/customcommands/get/2 23 | // GET: api/customcommands/get/2?name=!custom 24 | [HttpGet("{broadcasterId}")] 25 | public async Task Get(int broadcasterId, [FromQuery] string? name = null) 26 | { 27 | IsModelStateValid(); 28 | 29 | object? customCommands = new object(); 30 | 31 | if (string.IsNullOrEmpty(name)) 32 | { 33 | customCommands = await _context.CustomCommands 34 | .Where(m => m.BroadcasterId == broadcasterId) 35 | .ToListAsync(); 36 | } 37 | else 38 | { 39 | customCommands = await _context.CustomCommands 40 | .SingleOrDefaultAsync(m => m.BroadcasterId == broadcasterId && m.Name == name); 41 | } 42 | 43 | if (customCommands == null) 44 | { 45 | throw new NotFoundException("Custom commands not found"); 46 | } 47 | 48 | return Ok(customCommands); 49 | } 50 | 51 | // PUT: api/customcommands/5?broadcasterId=2 52 | [HttpPut("{id}")] 53 | public async Task Update(int id, [FromQuery] int broadcasterId, [FromBody] CustomCommand customCommand) 54 | { 55 | IsModelStateValid(); 56 | 57 | if (id != customCommand.Id || broadcasterId != customCommand.BroadcasterId) 58 | { 59 | throw new ApiException("ID or broadcaster ID does not match custom command ID or broadcaster ID"); 60 | } 61 | 62 | _context.Entry(customCommand).State = EntityState.Modified; 63 | 64 | try 65 | { 66 | await _context.SaveChangesAsync(); 67 | } 68 | catch (DbUpdateConcurrencyException) 69 | { 70 | if (!CustomCommandExists(id)) 71 | { 72 | throw new NotFoundException("Custom commands not found"); 73 | } 74 | else 75 | { 76 | throw; 77 | } 78 | } 79 | 80 | return NoContent(); 81 | } 82 | 83 | // POST: api/customcommands 84 | [HttpPost] 85 | public async Task Create([FromBody] CustomCommand customCommand) 86 | { 87 | IsModelStateValid(); 88 | 89 | _context.CustomCommands.Add(customCommand); 90 | await _context.SaveChangesAsync(); 91 | 92 | return CreatedAtAction("Get", new { id = customCommand.Id }, customCommand); 93 | } 94 | 95 | // DELETE: api/customcommands/5?name=!custom 96 | [HttpDelete("{broadcasterId}")] 97 | public async Task Delete(int broadcasterId, [FromQuery] string name) 98 | { 99 | IsModelStateValid(); 100 | 101 | CustomCommand? customCommand = await _context.CustomCommands 102 | .SingleOrDefaultAsync(m => m.BroadcasterId == broadcasterId && m.Name == name); 103 | 104 | if (customCommand == null) 105 | { 106 | throw new NotFoundException("Custom commands not found"); 107 | } 108 | 109 | _context.CustomCommands.Remove(customCommand); 110 | await _context.SaveChangesAsync(); 111 | 112 | return Ok(customCommand); 113 | } 114 | 115 | private bool CustomCommandExists(int id) 116 | { 117 | return _context.CustomCommands.Any(e => e.Id == id); 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotShared/ClientLibraries/IrcClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Security; 3 | using System.Net.Sockets; 4 | using System.IO; 5 | using System.Threading.Tasks; 6 | 7 | using TwitchBotShared.ClientLibraries.Singletons; 8 | using TwitchBotShared.Models; 9 | 10 | namespace TwitchBotShared.ClientLibraries 11 | { 12 | public class IrcClient 13 | { 14 | private readonly string _username; 15 | private readonly string _channel; 16 | private readonly TcpClient _tcpClient; 17 | private readonly SslStream _sslStream; 18 | private readonly StreamReader _inputStream; 19 | private readonly StreamWriter _outputStream; 20 | private readonly ErrorHandler _errHndlrInstance = ErrorHandler.Instance; 21 | 22 | public IrcClient(string username, string password, string channel) 23 | { 24 | _username = username; 25 | _channel = channel; 26 | 27 | _tcpClient = new TcpClient("irc.chat.twitch.tv", 6697); 28 | 29 | _sslStream = new SslStream(_tcpClient.GetStream()); 30 | _sslStream.AuthenticateAsClient("irc.chat.twitch.tv"); 31 | 32 | _inputStream = new StreamReader(_sslStream); 33 | _outputStream = new StreamWriter(_sslStream); 34 | 35 | _outputStream.WriteLine("CAP REQ :twitch.tv/tags"); // Reference: https://dev.twitch.tv/docs/irc/tags/ 36 | _outputStream.WriteLine("CAP REQ :twitch.tv/commands"); // Reference: https://dev.twitch.tv/docs/irc/commands/ 37 | _outputStream.WriteLine("CAP REQ :twitch.tv/membership"); // Reference: https://dev.twitch.tv/docs/irc/membership/ 38 | _outputStream.WriteLine($"PASS {password}"); 39 | _outputStream.WriteLine($"NICK {username}"); 40 | _outputStream.WriteLine($"USER {username} 8 * :{username}"); 41 | _outputStream.WriteLine($"JOIN #{channel}"); 42 | _outputStream.Flush(); 43 | } 44 | 45 | public async void SendIrcMessage(string message) 46 | { 47 | try 48 | { 49 | _outputStream.WriteLine(message); 50 | _outputStream.Flush(); 51 | } 52 | catch (Exception ex) 53 | { 54 | await _errHndlrInstance.LogErrorAsync(ex, "IrcClient", "SendIrcMessage(string)", false); 55 | } 56 | } 57 | 58 | public async void SendPublicChatMessage(string message) 59 | { 60 | try 61 | { 62 | SendIrcMessage(":" + _username + "!" + _username + "@" + _username + 63 | ".tmi.twitch.tv PRIVMSG #" + _channel + " :" + message); 64 | } 65 | catch (Exception ex) 66 | { 67 | await _errHndlrInstance.LogErrorAsync(ex, "IrcClient", "SendPublicChatMessage(string)", false); 68 | } 69 | } 70 | 71 | public async void ClearMessage(TwitchChatter chatter) 72 | { 73 | try 74 | { 75 | SendIrcMessage(":" + _username + "!" + _username + "@" + _username + 76 | ".tmi.twitch.tv PRIVMSG #" + _channel + " :/delete " + chatter.MessageId); 77 | } 78 | catch (Exception ex) 79 | { 80 | await _errHndlrInstance.LogErrorAsync(ex, "IrcClient", "ClearMessage(TwitchChatter)", false); 81 | } 82 | } 83 | 84 | public async void SendChatTimeout(string offender, int timeout = 1) 85 | { 86 | try 87 | { 88 | SendIrcMessage(":" + _username + "!" + _username + "@" + _username + 89 | ".tmi.twitch.tv PRIVMSG #" + _channel + " :/timeout " + offender + " " + timeout); 90 | } 91 | catch (Exception ex) 92 | { 93 | await _errHndlrInstance.LogErrorAsync(ex, "IrcClient", "SendChatTimeout(string, int)", false); 94 | } 95 | } 96 | 97 | public async Task ReadMessageAsync() 98 | { 99 | try 100 | { 101 | return _inputStream.ReadLine(); // chat message 102 | } 103 | catch (Exception ex) 104 | { 105 | await _errHndlrInstance.LogErrorAsync(ex, "IrcClient", "ReadMessageAsync()", true); 106 | } 107 | 108 | return ""; 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/visualstudio 2 | 3 | ### VisualStudio ### 4 | ## Ignore Visual Studio temporary files, build results, and 5 | ## files generated by popular Visual Studio add-ons. 6 | 7 | # User-specific files 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | build/ 24 | bld/ 25 | [Bb]in/ 26 | [Oo]bj/ 27 | 28 | # Visual Studio 2015 cache/options directory 29 | .vs/ 30 | # Uncomment if you have tasks that create the project's static files in wwwroot 31 | #wwwroot/ 32 | 33 | # MSTest test Results 34 | [Tt]est[Rr]esult*/ 35 | [Bb]uild[Ll]og.* 36 | 37 | # NUNIT 38 | *.VisualState.xml 39 | TestResult.xml 40 | 41 | # Build Results of an ATL Project 42 | [Dd]ebugPS/ 43 | [Rr]eleasePS/ 44 | dlldata.c 45 | 46 | # DNX 47 | project.lock.json 48 | artifacts/ 49 | 50 | *_i.c 51 | *_p.c 52 | *_i.h 53 | *.ilk 54 | *.meta 55 | *.obj 56 | *.pch 57 | *.pdb 58 | *.pgc 59 | *.pgd 60 | *.rsp 61 | *.sbr 62 | *.tlb 63 | *.tli 64 | *.tlh 65 | *.tmp 66 | *.tmp_proj 67 | *.log 68 | *.vspscc 69 | *.vssscc 70 | .builds 71 | *.pidb 72 | *.svclog 73 | *.scc 74 | 75 | # Chutzpah Test files 76 | _Chutzpah* 77 | 78 | # Visual C++ cache files 79 | ipch/ 80 | *.aps 81 | *.ncb 82 | *.opensdf 83 | *.sdf 84 | *.cachefile 85 | 86 | # Visual Studio profiler 87 | *.psess 88 | *.vsp 89 | *.vspx 90 | 91 | # TFS 2012 Local Workspace 92 | $tf/ 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | *.DotSettings.user 101 | 102 | # JustCode is a .NET coding add-in 103 | .JustCode 104 | 105 | # TeamCity is a build add-in 106 | _TeamCity* 107 | 108 | # DotCover is a Code Coverage Tool 109 | *.dotCover 110 | 111 | # NCrunch 112 | _NCrunch_* 113 | .*crunch*.local.xml 114 | nCrunchTemp_* 115 | 116 | # MightyMoose 117 | *.mm.* 118 | AutoTest.Net/ 119 | 120 | # Web workbench (sass) 121 | .sass-cache/ 122 | 123 | # Installshield output folder 124 | [Ee]xpress/ 125 | 126 | # DocProject is a documentation generator add-in 127 | DocProject/buildhelp/ 128 | DocProject/Help/*.HxT 129 | DocProject/Help/*.HxC 130 | DocProject/Help/*.hhc 131 | DocProject/Help/*.hhk 132 | DocProject/Help/*.hhp 133 | DocProject/Help/Html2 134 | DocProject/Help/html 135 | 136 | # Click-Once directory 137 | publish/ 138 | 139 | # Publish Web Output 140 | *.[Pp]ublish.xml 141 | *.azurePubxml 142 | # TODO: Comment the next line if you want to checkin your web deploy settings 143 | # but database connection strings (with potential passwords) will be unencrypted 144 | *.pubxml 145 | *.publishproj 146 | 147 | # NuGet Packages 148 | *.nupkg 149 | # The packages folder can be ignored because of Package Restore 150 | **/packages/* 151 | # except build/, which is used as an MSBuild target. 152 | !**/packages/build/ 153 | # Uncomment if necessary however generally it will be regenerated when needed 154 | #!**/packages/repositories.config 155 | 156 | # Windows Azure Build Output 157 | csx/ 158 | *.build.csdef 159 | 160 | # Windows Store app package directory 161 | AppPackages/ 162 | 163 | # Visual Studio cache files 164 | # files ending in .cache can be ignored 165 | *.[Cc]ache 166 | # but keep track of directories ending in .cache 167 | !*.[Cc]ache/ 168 | 169 | # Others 170 | ClientBin/ 171 | [Ss]tyle[Cc]op.* 172 | ~$* 173 | *~ 174 | *.dbmdl 175 | *.dbproj.schemaview 176 | *.pfx 177 | *.publishsettings 178 | node_modules/ 179 | orleans.codegen.cs 180 | 181 | # RIA/Silverlight projects 182 | Generated_Code/ 183 | 184 | # Backup & report files from converting an old project file 185 | # to a newer Visual Studio version. Backup files are not needed, 186 | # because we have git ;-) 187 | _UpgradeReport_Files/ 188 | Backup*/ 189 | UpgradeLog*.XML 190 | UpgradeLog*.htm 191 | 192 | # SQL Server files 193 | *.mdf 194 | *.ldf 195 | 196 | # Business Intelligence projects 197 | *.rdl.data 198 | *.bim.layout 199 | *.bim_*.settings 200 | 201 | # Microsoft Fakes 202 | FakesAssemblies/ 203 | 204 | # Node.js Tools for Visual Studio 205 | .ntvs_analysis.dat 206 | 207 | # Visual Studio 6 build log 208 | *.plg 209 | 210 | # Visual Studio 6 workspace options file 211 | *.opt 212 | 213 | # Visual Studio LightSwitch build output 214 | **/*.HTMLClient/GeneratedArtifacts 215 | **/*.DesktopClient/GeneratedArtifacts 216 | **/*.DesktopClient/ModelManifest.xml 217 | **/*.Server/GeneratedArtifacts 218 | **/*.Server/ModelManifest.xml 219 | _Pvt_Extensions 220 | /TwitchBot/TwitchBotApi/appsettings*.json 221 | /TwitchBot/TwitchBotConsoleApp/AppConfigSecrets.config 222 | /TwitchBot/TwitchBot.Api/appsettings*.json 223 | -------------------------------------------------------------------------------- /TwitchBot/TwitchBotShared/ClientLibraries/Singletons/CooldownUsersSingleton.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | using TwitchBotShared.ClientLibraries; 6 | using TwitchBotShared.Models; 7 | 8 | namespace TwitchBotShared.ClientLibraries.Singletons 9 | { 10 | public class CooldownUsersSingleton 11 | { 12 | private static volatile CooldownUsersSingleton _instance; 13 | private static object _syncRoot = new object(); 14 | 15 | private List _cooldownUsers = new List(); 16 | 17 | private CooldownUsersSingleton() { } 18 | 19 | public static CooldownUsersSingleton Instance 20 | { 21 | get 22 | { 23 | // first check 24 | if (_instance == null) 25 | { 26 | lock (_syncRoot) 27 | { 28 | // second check 29 | if (_instance == null) 30 | _instance = new CooldownUsersSingleton(); 31 | } 32 | } 33 | 34 | return _instance; 35 | } 36 | } 37 | 38 | /// 39 | /// Put a cooldown for a user on a command 40 | /// 41 | /// 42 | /// 43 | public void AddCooldown(TwitchChatter chatter, DateTime cooldown, string command) 44 | { 45 | if (cooldown > DateTime.Now) 46 | { 47 | _cooldownUsers.Add(new CooldownUser 48 | { 49 | Username = chatter.Username, 50 | Cooldown = cooldown, 51 | Command = command, 52 | Warned = false 53 | }); 54 | } 55 | 56 | if (command == "!wrongsong") 57 | { 58 | // Allow the user to request another song in case if cooldown exists 59 | CooldownUser songRequestCooldown = 60 | _cooldownUsers.FirstOrDefault(u => u.Username == chatter.Username && u.Command == "!ytsr"); 61 | 62 | if (songRequestCooldown != null) 63 | { 64 | _cooldownUsers.Remove(songRequestCooldown); 65 | } 66 | } 67 | } 68 | 69 | /// 70 | /// Checks if a user/command is on a cooldown from a particular command 71 | /// 72 | /// 73 | /// 74 | /// 75 | public bool IsCommandOnCooldown(string command, TwitchChatter chatter, IrcClient irc, bool hasGlobalCooldown = false) 76 | { 77 | CooldownUser cooldown = null; 78 | 79 | if (!hasGlobalCooldown) 80 | cooldown = _cooldownUsers.FirstOrDefault(u => u.Username == chatter.Username && u.Command == command); 81 | else 82 | cooldown = _cooldownUsers.FirstOrDefault(u => u.Command == command); 83 | 84 | if (cooldown == null) return false; 85 | else if (cooldown.Cooldown < DateTime.Now) 86 | { 87 | _cooldownUsers.Remove(cooldown); 88 | return false; 89 | } 90 | 91 | if (!cooldown.Warned) 92 | { 93 | string specialCooldownMessage = ""; 94 | 95 | // ToDo: Find more graceful way to prevent spam of commands with a global cooldown 96 | if (!hasGlobalCooldown) 97 | { 98 | cooldown.Warned = true; // prevent spamming cooldown message per personal cooldown 99 | specialCooldownMessage = "a PERSONAL"; 100 | } 101 | else 102 | { 103 | specialCooldownMessage = "a GLOBAL"; 104 | } 105 | 106 | string timespanMessage = ""; 107 | TimeSpan timespan = cooldown.Cooldown - DateTime.Now; 108 | 109 | if (timespan.Minutes > 0) 110 | timespanMessage = $"{timespan.Minutes} minute(s) and {timespan.Seconds} second(s)"; 111 | else if (timespan.Seconds == 0) 112 | timespanMessage = $"{timespan.Milliseconds} millisecond(s)"; 113 | else 114 | timespanMessage = $"{timespan.Seconds} second(s)"; 115 | 116 | irc.SendPublicChatMessage($"The {command} command is currently on {specialCooldownMessage} cooldown @{chatter.DisplayName} for {timespanMessage}"); 117 | } 118 | 119 | return true; 120 | } 121 | } 122 | } 123 | --------------------------------------------------------------------------------