diff --git a/src/NadekoBot/Bot.cs b/src/NadekoBot/Bot.cs index 1a653f2ed..62e4a350d 100644 --- a/src/NadekoBot/Bot.cs +++ b/src/NadekoBot/Bot.cs @@ -364,26 +364,27 @@ public sealed class Bot if (arg.Exception is { InnerException: WebSocketClosedException { CloseCode: 4014 } }) { - Log.Error(@" -Login failed. - -*** Please enable privileged intents *** - -Certain Nadeko features require Discord's privileged gateway intents. -These include greeting and goodbye messages, as well as creating the Owner message channels for DM forwarding. - -How to enable privileged intents: -1. Head over to the Discord Developer Portal https://discord.com/developers/applications/ -2. Select your Application. -3. Click on `Bot` in the left side navigation panel, and scroll down to the intents section. -4. Enable all intents. -5. Restart your bot. - -Read this only if your bot is in 100 or more servers: - -You'll need to apply to use the intents with Discord, but for small selfhosts, all that is required is enabling the intents in the developer portal. -Yes, this is a new thing from Discord, as of October 2020. No, there's nothing we can do about it. Yes, we're aware it worked before. -While waiting for your bot to be accepted, you can change the 'usePrivilegedIntents' inside your creds.yml to 'false', although this will break many of the nadeko's features"); + Log.Error(""" + Login failed. + + *** Please enable privileged intents *** + + Certain Nadeko features require Discord's privileged gateway intents. + These include greeting and goodbye messages, as well as creating the Owner message channels for DM forwarding. + + How to enable privileged intents: + 1. Head over to the Discord Developer Portal https://discord.com/developers/applications/ + 2. Select your Application. + 3. Click on `Bot` in the left side navigation panel, and scroll down to the intents section. + 4. Enable all intents. + 5. Restart your bot. + + Read this only if your bot is in 100 or more servers: + + You'll need to apply to use the intents with Discord, but for small selfhosts, all that is required is enabling the intents in the developer portal. + Yes, this is a new thing from Discord, as of October 2020. No, there's nothing we can do about it. Yes, we're aware it worked before. + While waiting for your bot to be accepted, you can change the 'usePrivilegedIntents' inside your creds.yml to 'false', although this will break many of the nadeko's features + """); return Task.CompletedTask; } diff --git a/src/NadekoBot/Common/Configs/BotConfig.cs b/src/NadekoBot/Common/Configs/BotConfig.cs index 9570271a5..fad137834 100644 --- a/src/NadekoBot/Common/Configs/BotConfig.cs +++ b/src/NadekoBot/Common/Configs/BotConfig.cs @@ -11,74 +11,89 @@ namespace NadekoBot.Common.Configs; [Cloneable] public sealed partial class BotConfig : ICloneable { - [Comment(@"DO NOT CHANGE")] + [Comment("""DO NOT CHANGE""")] public int Version { get; set; } = 5; - [Comment(@"Most commands, when executed, have a small colored line -next to the response. The color depends whether the command -is completed, errored or in progress (pending) -Color settings below are for the color of those lines. -To get color's hex, you can go here https://htmlcolorcodes.com/ -and copy the hex code fo your selected color (marked as #)")] + [Comment(""" + Most commands, when executed, have a small colored line + next to the response. The color depends whether the command + is completed, errored or in progress (pending) + Color settings below are for the color of those lines. + To get color's hex, you can go here https://htmlcolorcodes.com/ + and copy the hex code fo your selected color (marked as #) + """)] public ColorConfig Color { get; set; } [Comment("Default bot language. It has to be in the list of supported languages (.langli)")] public CultureInfo DefaultLocale { get; set; } - [Comment(@"Style in which executed commands will show up in the console. -Allowed values: Simple, Normal, None")] + [Comment(""" + Style in which executed commands will show up in the console. + Allowed values: Simple, Normal, None + """)] public ConsoleOutputType ConsoleOutputType { get; set; } - [Comment(@"Whether the bot will check for new releases every hour")] + [Comment("""Whether the bot will check for new releases every hour""")] public bool CheckForUpdates { get; set; } = true; - [Comment(@"Do you want any messages sent by users in Bot's DM to be forwarded to the owner(s)?")] + [Comment("""Do you want any messages sent by users in Bot's DM to be forwarded to the owner(s)?""")] public bool ForwardMessages { get; set; } - [Comment( - @"Do you want the message to be forwarded only to the first owner specified in the list of owners (in creds.yml), -or all owners? (this might cause the bot to lag if there's a lot of owners specified)")] + [Comment(""" + Do you want the message to be forwarded only to the first owner specified in the list of owners (in creds.yml), + or all owners? (this might cause the bot to lag if there's a lot of owners specified) + """)] public bool ForwardToAllOwners { get; set; } - [Comment(@"Any messages sent by users in Bot's DM to be forwarded to the specified channel. -This option will only work when ForwardToAllOwners is set to false")] + [Comment(""" + Any messages sent by users in Bot's DM to be forwarded to the specified channel. + This option will only work when ForwardToAllOwners is set to false + """)] public ulong? ForwardToChannel { get; set; } - [Comment(@"When a user DMs the bot with a message which is not a command -they will receive this message. Leave empty for no response. The string which will be sent whenever someone DMs the bot. -Supports embeds. How it looks: https://puu.sh/B0BLV.png")] + [Comment(""" + When a user DMs the bot with a message which is not a command + they will receive this message. Leave empty for no response. The string which will be sent whenever someone DMs the bot. + Supports embeds. How it looks: https://puu.sh/B0BLV.png + """)] [YamlMember(ScalarStyle = ScalarStyle.Literal)] public string DmHelpText { get; set; } - [Comment(@"Only users who send a DM to the bot containing one of the specified words will get a DmHelpText response. -Case insensitive. -Leave empty to reply with DmHelpText to every DM.")] + [Comment(""" + Only users who send a DM to the bot containing one of the specified words will get a DmHelpText response. + Case insensitive. + Leave empty to reply with DmHelpText to every DM. + """)] public List DmHelpTextKeywords { get; set; } - [Comment(@"This is the response for the .h command")] + [Comment("""This is the response for the .h command""")] [YamlMember(ScalarStyle = ScalarStyle.Literal)] public string HelpText { get; set; } - [Comment(@"List of modules and commands completely blocked on the bot")] + [Comment("""List of modules and commands completely blocked on the bot""")] public BlockedConfig Blocked { get; set; } - [Comment(@"Which string will be used to recognize the commands")] + [Comment("""Which string will be used to recognize the commands""")] public string Prefix { get; set; } - [Comment(@"Toggles whether your bot will group greet/bye messages into a single message every 5 seconds. -1st user who joins will get greeted immediately -If more users join within the next 5 seconds, they will be greeted in groups of 5. -This will cause %user.mention% and other placeholders to be replaced with multiple users. -Keep in mind this might break some of your embeds - for example if you have %user.avatar% in the thumbnail, -it will become invalid, as it will resolve to a list of avatars of grouped users. -note: This setting is primarily used if you're afraid of raids, or you're running medium/large bots where some - servers might get hundreds of people join at once. This is used to prevent the bot from getting ratelimited, - and (slightly) reduce the greet spam in those servers.")] + [Comment(""" + Toggles whether your bot will group greet/bye messages into a single message every 5 seconds. + 1st user who joins will get greeted immediately + If more users join within the next 5 seconds, they will be greeted in groups of 5. + This will cause %user.mention% and other placeholders to be replaced with multiple users. + Keep in mind this might break some of your embeds - for example if you have %user.avatar% in the thumbnail, + it will become invalid, as it will resolve to a list of avatars of grouped users. + note: This setting is primarily used if you're afraid of raids, or you're running medium/large bots where some + servers might get hundreds of people join at once. This is used to prevent the bot from getting ratelimited, + and (slightly) reduce the greet spam in those servers. + """)] public bool GroupGreets { get; set; } - [Comment(@"Whether the bot will rotate through all specified statuses. -This setting can be changed via .ropl command. -See RotatingStatuses submodule in Administration.")] + [Comment(""" + Whether the bot will rotate through all specified statuses. + This setting can be changed via .ropl command. + See RotatingStatuses submodule in Administration. + """)] public bool RotateStatuses { get; set; } public BotConfig() @@ -89,32 +104,34 @@ See RotatingStatuses submodule in Administration.")] ConsoleOutputType = ConsoleOutputType.Normal; ForwardMessages = false; ForwardToAllOwners = false; - DmHelpText = @"{""description"": ""Type `%prefix%h` for help.""}"; - HelpText = @"{ - ""title"": ""To invite me to your server, use this link"", - ""description"": ""https://discordapp.com/oauth2/authorize?client_id={0}&scope=bot&permissions=66186303"", - ""color"": 53380, - ""thumbnail"": ""https://i.imgur.com/nKYyqMK.png"", - ""fields"": [ - { - ""name"": ""Useful help commands"", - ""value"": ""`%bot.prefix%modules` Lists all bot modules. -`%prefix%h CommandName` Shows some help about a specific command. -`%prefix%commands ModuleName` Lists all commands in a module."", - ""inline"": false - }, - { - ""name"": ""List of all Commands"", - ""value"": ""https://nadeko.bot/commands"", - ""inline"": false - }, - { - ""name"": ""Nadeko Support Server"", - ""value"": ""https://discord.nadeko.bot/ "", - ""inline"": true - } - ] -}"; + DmHelpText = """{"description": "Type `%prefix%h` for help."}"""; + HelpText = """ + { + "title": "To invite me to your server, use this link", + "description": "https://discordapp.com/oauth2/authorize?client_id={0}&scope=bot&permissions=66186303", + "color": 53380, + "thumbnail": "https://i.imgur.com/nKYyqMK.png", + "fields": [ + { + "name": "Useful help commands", + "value": "`%bot.prefix%modules` Lists all bot modules. + `%prefix%h CommandName` Shows some help about a specific command. + `%prefix%commands ModuleName` Lists all commands in a module.", + "inline": false + }, + { + "name": "List of all Commands", + "value": "https://nadeko.bot/commands", + "inline": false + }, + { + "name": "Nadeko Support Server", + "value": "https://discord.nadeko.bot/ ", + "inline": true + } + ] + } + """; var blocked = new BlockedConfig(); Blocked = blocked; Prefix = "."; @@ -160,13 +177,13 @@ public sealed partial class BlockedConfig [Cloneable] public partial class ColorConfig { - [Comment(@"Color used for embed responses when command successfully executes")] + [Comment("""Color used for embed responses when command successfully executes""")] public Rgba32 Ok { get; set; } - [Comment(@"Color used for embed responses when command has an error")] + [Comment("""Color used for embed responses when command has an error""")] public Rgba32 Error { get; set; } - [Comment(@"Color used for embed responses while command is doing work or is in progress")] + [Comment("""Color used for embed responses while command is doing work or is in progress""")] public Rgba32 Pending { get; set; } public ColorConfig() diff --git a/src/NadekoBot/Common/Creds.cs b/src/NadekoBot/Common/Creds.cs index 9b4a48661..584a2f47e 100644 --- a/src/NadekoBot/Common/Creds.cs +++ b/src/NadekoBot/Common/Creds.cs @@ -5,115 +5,141 @@ namespace NadekoBot.Common; public sealed class Creds : IBotCredentials { - [Comment(@"DO NOT CHANGE")] + [Comment("""DO NOT CHANGE""")] public int Version { get; set; } - [Comment(@"Bot token. Do not share with anyone ever -> https://discordapp.com/developers/applications/")] + [Comment("""Bot token. Do not share with anyone ever -> https://discordapp.com/developers/applications/""")] public string Token { get; set; } - [Comment(@"List of Ids of the users who have bot owner permissions -**DO NOT ADD PEOPLE YOU DON'T TRUST**")] + [Comment(""" + List of Ids of the users who have bot owner permissions + **DO NOT ADD PEOPLE YOU DON'T TRUST** + """)] public ICollection OwnerIds { get; set; } [Comment("Keep this on 'true' unless you're sure your bot shouldn't use privileged intents or you're waiting to be accepted")] public bool UsePrivilegedIntents { get; set; } - [Comment(@"The number of shards that the bot will be running on. -Leave at 1 if you don't know what you're doing. - -note: If you are planning to have more than one shard, then you must change botCache to 'redis'. - Also, in that case you should be using NadekoBot.Coordinator to start the bot, and it will correctly override this value.")] + [Comment(""" + The number of shards that the bot will be running on. + Leave at 1 if you don't know what you're doing. + + note: If you are planning to have more than one shard, then you must change botCache to 'redis'. + Also, in that case you should be using NadekoBot.Coordinator to start the bot, and it will correctly override this value. + """)] public int TotalShards { get; set; } [Comment( - @"Login to https://console.cloud.google.com, create a new project, go to APIs & Services -> Library -> YouTube Data API and enable it. -Then, go to APIs and Services -> Credentials and click Create credentials -> API key. -Used only for Youtube Data Api (at the moment).")] + """ + Login to https://console.cloud.google.com, create a new project, go to APIs & Services -> Library -> YouTube Data API and enable it. + Then, go to APIs and Services -> Credentials and click Create credentials -> API key. + Used only for Youtube Data Api (at the moment). + """)] public string GoogleApiKey { get; set; } [Comment( - @"Create a new custom search here https://programmablesearchengine.google.com/cse/create/new -Enable SafeSearch -Remove all Sites to Search -Enable Search the entire web -Copy the 'Search Engine ID' to the SearchId field - -Do all steps again but enable image search for the ImageSearchId")] + """ + Create a new custom search here https://programmablesearchengine.google.com/cse/create/new + Enable SafeSearch + Remove all Sites to Search + Enable Search the entire web + Copy the 'Search Engine ID' to the SearchId field + + Do all steps again but enable image search for the ImageSearchId + """)] public GoogleApiConfig Google { get; set; } - [Comment(@"Settings for voting system for discordbots. Meant for use on global Nadeko.")] + [Comment("""Settings for voting system for discordbots. Meant for use on global Nadeko.""")] public VotesSettings Votes { get; set; } - [Comment(@"Patreon auto reward system settings. -go to https://www.patreon.com/portal -> my clients -> create client")] + [Comment(""" + Patreon auto reward system settings. + go to https://www.patreon.com/portal -> my clients -> create client + """)] public PatreonSettings Patreon { get; set; } - [Comment(@"Api key for sending stats to DiscordBotList.")] + [Comment("""Api key for sending stats to DiscordBotList.""")] public string BotListToken { get; set; } - [Comment(@"Official cleverbot api key.")] + [Comment("""Official cleverbot api key.""")] public string CleverbotApiKey { get; set; } - [Comment(@"Which cache implementation should bot use. -'memory' - Cache will be in memory of the bot's process itself. Only use this on bots with a single shard. When the bot is restarted the cache is reset. -'redis' - Uses redis (which needs to be separately downloaded and installed). The cache will persist through bot restarts. You can configure connection string in creds.yml")] + [Comment(""" + Which cache implementation should bot use. + 'memory' - Cache will be in memory of the bot's process itself. Only use this on bots with a single shard. When the bot is restarted the cache is reset. + 'redis' - Uses redis (which needs to be separately downloaded and installed). The cache will persist through bot restarts. You can configure connection string in creds.yml + """)] public BotCacheImplemenation BotCache { get; set; } - [Comment(@"Redis connection string. Don't change if you don't know what you're doing. -Only used if botCache is set to 'redis'")] + [Comment(""" + Redis connection string. Don't change if you don't know what you're doing. + Only used if botCache is set to 'redis' + """)] public string RedisOptions { get; set; } - [Comment(@"Database options. Don't change if you don't know what you're doing. Leave null for default values")] + [Comment("""Database options. Don't change if you don't know what you're doing. Leave null for default values""")] public DbOptions Db { get; set; } - [Comment(@"Address and port of the coordinator endpoint. Leave empty for default. -Change only if you've changed the coordinator address or port.")] + [Comment(""" + Address and port of the coordinator endpoint. Leave empty for default. + Change only if you've changed the coordinator address or port. + """)] public string CoordinatorUrl { get; set; } [Comment( - @"Api key obtained on https://rapidapi.com (go to MyApps -> Add New App -> Enter Name -> Application key)")] + """Api key obtained on https://rapidapi.com (go to MyApps -> Add New App -> Enter Name -> Application key)""")] public string RapidApiKey { get; set; } - [Comment(@"https://locationiq.com api key (register and you will receive the token in the email). -Used only for .time command.")] + [Comment(""" + https://locationiq.com api key (register and you will receive the token in the email). + Used only for .time command. + """)] public string LocationIqApiKey { get; set; } - [Comment(@"https://timezonedb.com api key (register and you will receive the token in the email). -Used only for .time command")] + [Comment(""" + https://timezonedb.com api key (register and you will receive the token in the email). + Used only for .time command + """)] public string TimezoneDbApiKey { get; set; } - [Comment(@"https://pro.coinmarketcap.com/account/ api key. There is a free plan for personal use. -Used for cryptocurrency related commands.")] + [Comment(""" + https://pro.coinmarketcap.com/account/ api key. There is a free plan for personal use. + Used for cryptocurrency related commands. + """)] public string CoinmarketcapApiKey { get; set; } // [Comment(@"https://polygon.io/dashboard/api-keys api key. Free plan allows for 5 queries per minute. // Used for stocks related commands.")] // public string PolygonIoApiKey { get; set; } - [Comment(@"Api key used for Osu related commands. Obtain this key at https://osu.ppy.sh/p/api")] + [Comment("""Api key used for Osu related commands. Obtain this key at https://osu.ppy.sh/p/api""")] public string OsuApiKey { get; set; } - [Comment(@"Optional Trovo client id. -You should use this if Trovo stream notifications stopped working or you're getting ratelimit errors.")] + [Comment(""" + Optional Trovo client id. + You should use this if Trovo stream notifications stopped working or you're getting ratelimit errors. + """)] public string TrovoClientId { get; set; } - [Comment(@"Obtain by creating an application at https://dev.twitch.tv/console/apps")] + [Comment("""Obtain by creating an application at https://dev.twitch.tv/console/apps""")] public string TwitchClientId { get; set; } - [Comment(@"Obtain by creating an application at https://dev.twitch.tv/console/apps")] + [Comment("""Obtain by creating an application at https://dev.twitch.tv/console/apps""")] public string TwitchClientSecret { get; set; } - [Comment(@"Command and args which will be used to restart the bot. -Only used if bot is executed directly (NOT through the coordinator) -placeholders: - {0} -> shard id - {1} -> total shards -Linux default - cmd: dotnet - args: ""NadekoBot.dll -- {0}"" -Windows default - cmd: NadekoBot.exe - args: ""{0}""")] + [Comment(""" + Command and args which will be used to restart the bot. + Only used if bot is executed directly (NOT through the coordinator) + placeholders: + {0} -> shard id + {1} -> total shards + Linux default + cmd: dotnet + args: "NadekoBot.dll -- {0}" + Windows default + cmd: NadekoBot.exe + args: "{0}" + """)] public RestartConfig RestartCommand { get; set; } public Creds() @@ -145,15 +171,19 @@ Windows default public class DbOptions { - [Comment(@"Database type. ""sqlite"", ""mysql"" and ""postgresql"" are supported. -Default is ""sqlite""")] + [Comment(""" + Database type. "sqlite", "mysql" and "postgresql" are supported. + Default is "sqlite" + """)] public string Type { get; set; } - [Comment(@"Database connection string. -You MUST change this if you're not using ""sqlite"" type. -Default is ""Data Source=data/NadekoBot.db"" -Example for mysql: ""Server=localhost;Port=3306;Uid=root;Pwd=my_super_secret_mysql_password;Database=nadeko"" -Example for postgresql: ""Server=localhost;Port=5432;User Id=postgres;Password=my_super_secret_postgres_password;Database=nadeko;""")] + [Comment(""" + Database connection string. + You MUST change this if you're not using "sqlite" type. + Default is "Data Source=data/NadekoBot.db" + Example for mysql: "Server=localhost;Port=3306;Uid=root;Pwd=my_super_secret_mysql_password;Database=nadeko" + Example for postgresql: "Server=localhost;Port=5432;User Id=postgres;Password=my_super_secret_postgres_password;Database=nadeko;" + """)] public string ConnectionString { get; set; } } @@ -165,7 +195,7 @@ Example for postgresql: ""Server=localhost;Port=5432;User Id=postgres;Password=m public string ClientSecret { get; set; } [Comment( - @"Campaign ID of your patreon page. Go to your patreon page (make sure you're logged in) and type ""prompt('Campaign ID', window.patreon.bootstrap.creator.data.id);"" in the console. (ctrl + shift + i)")] + """Campaign ID of your patreon page. Go to your patreon page (make sure you're logged in) and type "prompt('Campaign ID', window.patreon.bootstrap.creator.data.id);" in the console. (ctrl + shift + i)""")] public string CampaignId { get; set; } public PatreonSettings( @@ -187,22 +217,30 @@ Example for postgresql: ""Server=localhost;Port=5432;User Id=postgres;Password=m public sealed record VotesSettings { - [Comment(@"top.gg votes service url -This is the url of your instance of the NadekoBot.Votes api -Example: https://votes.my.cool.bot.com")] + [Comment(""" + top.gg votes service url + This is the url of your instance of the NadekoBot.Votes api + Example: https://votes.my.cool.bot.com + """)] public string TopggServiceUrl { get; set; } - [Comment(@"Authorization header value sent to the TopGG service url with each request -This should be equivalent to the TopggKey in your NadekoBot.Votes api appsettings.json file")] + [Comment(""" + Authorization header value sent to the TopGG service url with each request + This should be equivalent to the TopggKey in your NadekoBot.Votes api appsettings.json file + """)] public string TopggKey { get; set; } - [Comment(@"discords.com votes service url -This is the url of your instance of the NadekoBot.Votes api -Example: https://votes.my.cool.bot.com")] + [Comment(""" + discords.com votes service url + This is the url of your instance of the NadekoBot.Votes api + Example: https://votes.my.cool.bot.com + """)] public string DiscordsServiceUrl { get; set; } - [Comment(@"Authorization header value sent to the Discords service url with each request -This should be equivalent to the DiscordsKey in your NadekoBot.Votes api appsettings.json file")] + [Comment(""" + Authorization header value sent to the Discords service url with each request + This should be equivalent to the DiscordsKey in your NadekoBot.Votes api appsettings.json file + """)] public string DiscordsKey { get; set; } public VotesSettings() diff --git a/src/NadekoBot/Common/Medusa/Config/MedusaConfig.cs b/src/NadekoBot/Common/Medusa/Config/MedusaConfig.cs index a9fdafd7f..2ac4e1aa8 100644 --- a/src/NadekoBot/Common/Medusa/Config/MedusaConfig.cs +++ b/src/NadekoBot/Common/Medusa/Config/MedusaConfig.cs @@ -7,10 +7,10 @@ namespace Nadeko.Medusa; [Cloneable] public sealed partial class MedusaConfig : ICloneable { - [Comment(@"DO NOT CHANGE")] + [Comment("""DO NOT CHANGE""")] public int Version { get; set; } = 1; - [Comment("List of medusae automatically loaded at startup")] + [Comment("""List of medusae automatically loaded at startup""")] public List? Loaded { get; set; } public MedusaConfig() diff --git a/src/NadekoBot/Migrations/MigrationQueries.cs b/src/NadekoBot/Migrations/MigrationQueries.cs index 2a11231e7..4d8570777 100644 --- a/src/NadekoBot/Migrations/MigrationQueries.cs +++ b/src/NadekoBot/Migrations/MigrationQueries.cs @@ -10,29 +10,35 @@ public static class MigrationQueries if (migrationBuilder.IsMySql()) { migrationBuilder.Sql( - @"INSERT IGNORE into reactionroles(guildid, channelid, messageid, emote, roleid, `group`, levelreq, dateadded) -select guildid, channelid, messageid, emotename, roleid, exclusive, 0, reactionrolemessage.dateadded -from reactionrole -left join reactionrolemessage on reactionrolemessage.id = reactionrole.reactionrolemessageid -left join guildconfigs on reactionrolemessage.guildconfigid = guildconfigs.id;"); + """ + INSERT IGNORE into reactionroles(guildid, channelid, messageid, emote, roleid, `group`, levelreq, dateadded) + select guildid, channelid, messageid, emotename, roleid, exclusive, 0, reactionrolemessage.dateadded + from reactionrole + left join reactionrolemessage on reactionrolemessage.id = reactionrole.reactionrolemessageid + left join guildconfigs on reactionrolemessage.guildconfigid = guildconfigs.id; + """); } else if (migrationBuilder.IsSqlite()) { migrationBuilder.Sql( - @"insert or ignore into reactionroles(guildid, channelid, messageid, emote, roleid, 'group', levelreq, dateadded) -select guildid, channelid, messageid, emotename, roleid, exclusive, 0, reactionrolemessage.dateadded -from reactionrole -left join reactionrolemessage on reactionrolemessage.id = reactionrole.reactionrolemessageid -left join guildconfigs on reactionrolemessage.guildconfigid = guildconfigs.id;"); + """ + insert or ignore into reactionroles(guildid, channelid, messageid, emote, roleid, 'group', levelreq, dateadded) + select guildid, channelid, messageid, emotename, roleid, exclusive, 0, reactionrolemessage.dateadded + from reactionrole + left join reactionrolemessage on reactionrolemessage.id = reactionrole.reactionrolemessageid + left join guildconfigs on reactionrolemessage.guildconfigid = guildconfigs.id; + """); } else if (migrationBuilder.IsNpgsql()) { - migrationBuilder.Sql(@"insert into reactionroles(guildid, channelid, messageid, emote, roleid, ""group"", levelreq, dateadded) - select guildid, channelid, messageid, emotename, roleid, exclusive::int, 0, reactionrolemessage.dateadded - from reactionrole - left join reactionrolemessage on reactionrolemessage.id = reactionrole.reactionrolemessageid - left join guildconfigs on reactionrolemessage.guildconfigid = guildconfigs.id - ON CONFLICT DO NOTHING;"); + migrationBuilder.Sql(""" + insert into reactionroles(guildid, channelid, messageid, emote, roleid, "group", levelreq, dateadded) + select guildid, channelid, messageid, emotename, roleid, exclusive::int, 0, reactionrolemessage.dateadded + from reactionrole + left join reactionrolemessage on reactionrolemessage.id = reactionrole.reactionrolemessageid + left join guildconfigs on reactionrolemessage.guildconfigid = guildconfigs.id + ON CONFLICT DO NOTHING; + """); } else { diff --git a/src/NadekoBot/Migrations/Sqlite/20211015232708_nsfw-blacklist-tags.cs b/src/NadekoBot/Migrations/Sqlite/20211015232708_nsfw-blacklist-tags.cs index 1cf2c246e..d29059444 100644 --- a/src/NadekoBot/Migrations/Sqlite/20211015232708_nsfw-blacklist-tags.cs +++ b/src/NadekoBot/Migrations/Sqlite/20211015232708_nsfw-blacklist-tags.cs @@ -28,14 +28,16 @@ namespace NadekoBot.Migrations table: "NsfwBlacklistedTags", column: "GuildId"); - migrationBuilder.Sql(@"INSERT INTO NsfwBlacklistedTags(Id, GuildId, Tag, DateAdded) -SELECT - Id, - (SELECT GuildId From GuildConfigs WHERE Id=GuildConfigId), - Tag, - DateAdded -FROM NsfwBlacklitedTag -WHERE GuildConfigId in (SELECT Id from GuildConfigs);"); + migrationBuilder.Sql(""" + INSERT INTO NsfwBlacklistedTags(Id, GuildId, Tag, DateAdded) + SELECT + Id, + (SELECT GuildId From GuildConfigs WHERE Id=GuildConfigId), + Tag, + DateAdded + FROM NsfwBlacklitedTag + WHERE GuildConfigId in (SELECT Id from GuildConfigs); + """); migrationBuilder.DropTable( name: "NsfwBlacklitedTag"); diff --git a/src/NadekoBot/Migrations/Sqlite/20220102102344_crs-rename-to-expressions-perm-rename.cs b/src/NadekoBot/Migrations/Sqlite/20220102102344_crs-rename-to-expressions-perm-rename.cs index cd73136d4..c989c8080 100644 --- a/src/NadekoBot/Migrations/Sqlite/20220102102344_crs-rename-to-expressions-perm-rename.cs +++ b/src/NadekoBot/Migrations/Sqlite/20220102102344_crs-rename-to-expressions-perm-rename.cs @@ -50,24 +50,30 @@ namespace NadekoBot.Migrations principalTable: "GuildConfigs", principalColumn: "Id"); - migrationBuilder.Sql(@"UPDATE Permissions -SET SecondaryTargetName='ACTUALEXPRESSIONS' -WHERE SecondaryTargetName='ActualCustomReactions' COLLATE NOCASE;"); + migrationBuilder.Sql(""" + UPDATE Permissions + SET SecondaryTargetName='ACTUALEXPRESSIONS' + WHERE SecondaryTargetName='ActualCustomReactions' COLLATE NOCASE; + """); - migrationBuilder.Sql(@"UPDATE Permissions -SET SecondaryTargetName='EXPRESSIONS' -WHERE SecondaryTargetName='CustomReactions' COLLATE NOCASE;"); + migrationBuilder.Sql(""" + UPDATE Permissions + SET SecondaryTargetName='EXPRESSIONS' + WHERE SecondaryTargetName='CustomReactions' COLLATE NOCASE; + """); - migrationBuilder.Sql(@"UPDATE Permissions -SET SecondaryTargetName= case lower(SecondaryTargetName) - WHEN 'editcustreact' THEN 'expredit' - WHEN 'delcustreact' THEN 'exprdel' - WHEN 'listcustreact' THEN 'exprlist' - WHEN 'addcustreact' THEN 'expradd' - WHEN 'showcustreact' THEN 'exprshow' -ELSE SecondaryTargetName -END -WHERE lower(SecondaryTargetName) in ('editcustreact', 'delcustreact', 'listcustreact', 'addcustreact', 'showcustreact');"); + migrationBuilder.Sql(""" + UPDATE Permissions + SET SecondaryTargetName= case lower(SecondaryTargetName) + WHEN 'editcustreact' THEN 'expredit' + WHEN 'delcustreact' THEN 'exprdel' + WHEN 'listcustreact' THEN 'exprlist' + WHEN 'addcustreact' THEN 'expradd' + WHEN 'showcustreact' THEN 'exprshow' + ELSE SecondaryTargetName + END + WHERE lower(SecondaryTargetName) in ('editcustreact', 'delcustreact', 'listcustreact', 'addcustreact', 'showcustreact'); + """); } protected override void Down(MigrationBuilder migrationBuilder) diff --git a/src/NadekoBot/Migrations/Sqlite/20220110105942_filter-settings-cleanup.cs b/src/NadekoBot/Migrations/Sqlite/20220110105942_filter-settings-cleanup.cs index 380f1e449..8d5ce784a 100644 --- a/src/NadekoBot/Migrations/Sqlite/20220110105942_filter-settings-cleanup.cs +++ b/src/NadekoBot/Migrations/Sqlite/20220110105942_filter-settings-cleanup.cs @@ -42,13 +42,15 @@ namespace NadekoBot.Migrations table: "FilterWordsChannelId", column: "GuildConfigId"); - migrationBuilder.Sql(@"INSERT INTO FilterWordsChannelId(Id, ChannelId, GuildConfigId, DateAdded) -SELECT Id, ChannelId, GuildConfigId1, DateAdded -FROM FilterChannelId -WHERE GuildConfigId1 is not null; --- Remove them after moving them to a different table -DELETE FROM FilterChannelId -WHERE GuildConfigId is null;"); + migrationBuilder.Sql(""" + INSERT INTO FilterWordsChannelId(Id, ChannelId, GuildConfigId, DateAdded) + SELECT Id, ChannelId, GuildConfigId1, DateAdded + FROM FilterChannelId + WHERE GuildConfigId1 is not null; + -- Remove them after moving them to a different table + DELETE FROM FilterChannelId + WHERE GuildConfigId is null; + """); migrationBuilder.DropColumn( name: "GuildConfigId1", diff --git a/src/NadekoBot/Migrations/Sqlite/20220409170828_clubs-refactor.cs b/src/NadekoBot/Migrations/Sqlite/20220409170828_clubs-refactor.cs index d4a43fbcf..a2ce93ff6 100644 --- a/src/NadekoBot/Migrations/Sqlite/20220409170828_clubs-refactor.cs +++ b/src/NadekoBot/Migrations/Sqlite/20220409170828_clubs-refactor.cs @@ -9,19 +9,21 @@ namespace NadekoBot.Migrations { protected override void Up(MigrationBuilder migrationBuilder) { - migrationBuilder.Sql(@"UPDATE Clubs -SET Name = Name || '#' || Discrim -WHERE Discrim <> 1; - -UPDATE Clubs as co -SET Name = - CASE (select count(*) from Clubs as ci where co.Name == ci.Name) = 1 - WHEN true - THEN Name - ELSE - Name || '#' || Discrim - END - WHERE Discrim = 1;"); + migrationBuilder.Sql(""" + UPDATE Clubs + SET Name = Name || '#' || Discrim + WHERE Discrim <> 1; + + UPDATE Clubs as co + SET Name = + CASE (select count(*) from Clubs as ci where co.Name == ci.Name) = 1 + WHEN true + THEN Name + ELSE + Name || '#' || Discrim + END + WHERE Discrim = 1; + """); migrationBuilder.DropForeignKey( name: "FK_Clubs_DiscordUser_OwnerId", diff --git a/src/NadekoBot/Modules/Expressions/NadekoExpressionsService.cs b/src/NadekoBot/Modules/Expressions/NadekoExpressionsService.cs index 70474eafe..06d8c6269 100644 --- a/src/NadekoBot/Modules/Expressions/NadekoExpressionsService.cs +++ b/src/NadekoBot/Modules/Expressions/NadekoExpressionsService.cs @@ -19,19 +19,21 @@ public sealed class NadekoExpressionsService : IExecOnMessage, IReadyExecutor private const string MENTION_PH = "%bot.mention%"; private const string PREPEND_EXPORT = - @"# Keys are triggers, Each key has a LIST of expressions in the following format: -# - res: Response string -# id: Alphanumeric id used for commands related to the expression. (Note, when using .exprsimport, a new id will be generated.) -# react: -# - -# at: Whether expression allows targets (see .h .exprat) -# ca: Whether expression expects trigger anywhere (see .h .exprca) -# dm: Whether expression DMs the response (see .h .exprdm) -# ad: Whether expression automatically deletes triggering message (see .h .exprad) + """ + # Keys are triggers, Each key has a LIST of expressions in the following format: + # - res: Response string + # id: Alphanumeric id used for commands related to the expression. (Note, when using .exprsimport, a new id will be generated.) + # react: + # - + # at: Whether expression allows targets (see .h .exprat) + # ca: Whether expression expects trigger anywhere (see .h .exprca) + # dm: Whether expression DMs the response (see .h .exprdm) + # ad: Whether expression automatically deletes triggering message (see .h .exprad) + -"; + """; private static readonly ISerializer _exportSerializer = new SerializerBuilder() .WithEventEmitter(args diff --git a/src/NadekoBot/Modules/Gambling/GamblingConfig.cs b/src/NadekoBot/Modules/Gambling/GamblingConfig.cs index 8acc32d82..cc217a930 100644 --- a/src/NadekoBot/Modules/Gambling/GamblingConfig.cs +++ b/src/NadekoBot/Modules/Gambling/GamblingConfig.cs @@ -10,50 +10,58 @@ namespace NadekoBot.Modules.Gambling.Common; [Cloneable] public sealed partial class GamblingConfig : ICloneable { - [Comment(@"DO NOT CHANGE")] + [Comment("""DO NOT CHANGE""")] public int Version { get; set; } = 2; - [Comment(@"Currency settings")] + [Comment("""Currency settings""")] public CurrencyConfig Currency { get; set; } - [Comment(@"Minimum amount users can bet (>=0)")] + [Comment("""Minimum amount users can bet (>=0)""")] public int MinBet { get; set; } = 0; - [Comment(@"Maximum amount users can bet -Set 0 for unlimited")] + [Comment(""" + Maximum amount users can bet + Set 0 for unlimited + """)] public int MaxBet { get; set; } = 0; - [Comment(@"Settings for betflip command")] + [Comment("""Settings for betflip command""")] public BetFlipConfig BetFlip { get; set; } - [Comment(@"Settings for betroll command")] + [Comment("""Settings for betroll command""")] public BetRollConfig BetRoll { get; set; } - [Comment(@"Automatic currency generation settings.")] + [Comment("""Automatic currency generation settings.""")] public GenerationConfig Generation { get; set; } - [Comment(@"Settings for timely command -(letting people claim X amount of currency every Y hours)")] + [Comment(""" + Settings for timely command + (letting people claim X amount of currency every Y hours) + """)] public TimelyConfig Timely { get; set; } - [Comment(@"How much will each user's owned currency decay over time.")] + [Comment("""How much will each user's owned currency decay over time.""")] public DecayConfig Decay { get; set; } - [Comment(@"Settings for LuckyLadder command")] + [Comment("""Settings for LuckyLadder command""")] public LuckyLadderSettings LuckyLadder { get; set; } - [Comment(@"Settings related to waifus")] + [Comment("""Settings related to waifus""")] public WaifuConfig Waifu { get; set; } - [Comment(@"Amount of currency selfhosters will get PER pledged dollar CENT. -1 = 100 currency per $. Used almost exclusively on public nadeko.")] + [Comment(""" + Amount of currency selfhosters will get PER pledged dollar CENT. + 1 = 100 currency per $. Used almost exclusively on public nadeko. + """)] public decimal PatreonCurrencyPerCent { get; set; } = 1; - [Comment(@"Currency reward per vote. -This will work only if you've set up VotesApi and correct credentials for topgg and/or discords voting")] + [Comment(""" + Currency reward per vote. + This will work only if you've set up VotesApi and correct credentials for topgg and/or discords voting + """)] public long VoteReward { get; set; } = 100; - [Comment(@"Slot config")] + [Comment("""Slot config""")] public SlotsConfig Slots { get; set; } public GamblingConfig() @@ -72,42 +80,50 @@ This will work only if you've set up VotesApi and correct credentials for topgg public class CurrencyConfig { - [Comment(@"What is the emoji/character which represents the currency")] + [Comment("""What is the emoji/character which represents the currency""")] public string Sign { get; set; } = "🌸"; - [Comment(@"What is the name of the currency")] + [Comment("""What is the name of the currency""")] public string Name { get; set; } = "Nadeko Flower"; - [Comment(@"For how long (in days) will the transactions be kept in the database (curtrs) -Set 0 to disable cleanup (keep transactions forever)")] + [Comment(""" + For how long (in days) will the transactions be kept in the database (curtrs) + Set 0 to disable cleanup (keep transactions forever) + """)] public int TransactionsLifetime { get; set; } = 0; } [Cloneable] public partial class TimelyConfig { - [Comment(@"How much currency will the users get every time they run .timely command -setting to 0 or less will disable this feature")] + [Comment(""" + How much currency will the users get every time they run .timely command + setting to 0 or less will disable this feature + """)] public int Amount { get; set; } = 0; - [Comment(@"How often (in hours) can users claim currency with .timely command -setting to 0 or less will disable this feature")] + [Comment(""" + How often (in hours) can users claim currency with .timely command + setting to 0 or less will disable this feature + """)] public int Cooldown { get; set; } = 24; } [Cloneable] public partial class BetFlipConfig { - [Comment(@"Bet multiplier if user guesses correctly")] + [Comment("""Bet multiplier if user guesses correctly""")] public decimal Multiplier { get; set; } = 1.95M; } [Cloneable] public partial class BetRollConfig { - [Comment(@"When betroll is played, user will roll a number 0-100. -This setting will describe which multiplier is used for when the roll is higher than the given number. -Doesn't have to be ordered.")] + [Comment(""" + When betroll is played, user will roll a number 0-100. + This setting will describe which multiplier is used for when the roll is higher than the given number. + Doesn't have to be ordered. + """)] public BetRollPair[] Pairs { get; set; } = Array.Empty(); public BetRollConfig() @@ -134,48 +150,56 @@ Doesn't have to be ordered.")] [Cloneable] public partial class GenerationConfig { - [Comment(@"when currency is generated, should it also have a random password -associated with it which users have to type after the .pick command -in order to get it")] + [Comment(""" + when currency is generated, should it also have a random password + associated with it which users have to type after the .pick command + in order to get it + """)] public bool HasPassword { get; set; } = true; - [Comment(@"Every message sent has a certain % chance to generate the currency -specify the percentage here (1 being 100%, 0 being 0% - for example -default is 0.02, which is 2%")] + [Comment(""" + Every message sent has a certain % chance to generate the currency + specify the percentage here (1 being 100%, 0 being 0% - for example + default is 0.02, which is 2% + """)] public decimal Chance { get; set; } = 0.02M; - [Comment(@"How many seconds have to pass for the next message to have a chance to spawn currency")] + [Comment("""How many seconds have to pass for the next message to have a chance to spawn currency""")] public int GenCooldown { get; set; } = 10; - [Comment(@"Minimum amount of currency that can spawn")] + [Comment("""Minimum amount of currency that can spawn""")] public int MinAmount { get; set; } = 1; - [Comment(@"Maximum amount of currency that can spawn. - Set to the same value as MinAmount to always spawn the same amount")] + [Comment(""" + Maximum amount of currency that can spawn. + Set to the same value as MinAmount to always spawn the same amount + """)] public int MaxAmount { get; set; } = 1; } [Cloneable] public partial class DecayConfig { - [Comment(@"Percentage of user's current currency which will be deducted every 24h. -0 - 1 (1 is 100%, 0.5 50%, 0 disabled)")] + [Comment(""" + Percentage of user's current currency which will be deducted every 24h. + 0 - 1 (1 is 100%, 0.5 50%, 0 disabled) + """)] public decimal Percent { get; set; } = 0; - [Comment(@"Maximum amount of user's currency that can decay at each interval. 0 for unlimited.")] + [Comment("""Maximum amount of user's currency that can decay at each interval. 0 for unlimited.""")] public int MaxDecay { get; set; } = 0; - [Comment(@"Only users who have more than this amount will have their currency decay.")] + [Comment("""Only users who have more than this amount will have their currency decay.""")] public int MinThreshold { get; set; } = 99; - [Comment(@"How often, in hours, does the decay run. Default is 24 hours")] + [Comment("""How often, in hours, does the decay run. Default is 24 hours""")] public int HourInterval { get; set; } = 24; } [Cloneable] public partial class LuckyLadderSettings { - [Comment(@"Self-Explanatory. Has to have 8 values, otherwise the command won't work.")] + [Comment("""Self-Explanatory. Has to have 8 values, otherwise the command won't work.""")] public decimal[] Multipliers { get; set; } public LuckyLadderSettings() @@ -185,17 +209,21 @@ public partial class LuckyLadderSettings [Cloneable] public sealed partial class WaifuConfig { - [Comment(@"Minimum price a waifu can have")] + [Comment("""Minimum price a waifu can have""")] public long MinPrice { get; set; } = 50; public MultipliersData Multipliers { get; set; } = new(); - [Comment(@"Settings for periodic waifu price decay. -Waifu price decays only if the waifu has no claimer.")] + [Comment(""" + Settings for periodic waifu price decay. + Waifu price decays only if the waifu has no claimer. + """)] public WaifuDecayConfig Decay { get; set; } = new(); - [Comment(@"List of items available for gifting. -If negative is true, gift will instead reduce waifu value.")] + [Comment(""" + List of items available for gifting. + If negative is true, gift will instead reduce waifu value. + """)] public List Items { get; set; } = new(); public WaifuConfig() @@ -241,16 +269,20 @@ If negative is true, gift will instead reduce waifu value.")] public class WaifuDecayConfig { - [Comment(@"Percentage (0 - 100) of the waifu value to reduce. -Set 0 to disable -For example if a waifu has a price of 500$, setting this value to 10 would reduce the waifu value by 10% (50$)")] + [Comment(""" + Percentage (0 - 100) of the waifu value to reduce. + Set 0 to disable + For example if a waifu has a price of 500$, setting this value to 10 would reduce the waifu value by 10% (50$) + """)] public int Percent { get; set; } = 0; - [Comment(@"How often to decay waifu values, in hours")] + [Comment("""How often to decay waifu values, in hours""")] public int HourInterval { get; set; } = 24; - [Comment(@"Minimum waifu price required for the decay to be applied. -For example if this value is set to 300, any waifu with the price 300 or less will not experience decay.")] + [Comment(""" + Minimum waifu price required for the decay to be applied. + For example if this value is set to 300, any waifu with the price 300 or less will not experience decay. + """)] public long MinPrice { get; set; } = 300; } } @@ -258,47 +290,61 @@ For example if this value is set to 300, any waifu with the price 300 or less wi [Cloneable] public sealed partial class MultipliersData { - [Comment(@"Multiplier for waifureset. Default 150. -Formula (at the time of writing this): -price = (waifu_price * 1.25f) + ((number_of_divorces + changes_of_heart + 2) * WaifuReset) rounded up")] + [Comment(""" + Multiplier for waifureset. Default 150. + Formula (at the time of writing this): + price = (waifu_price * 1.25f) + ((number_of_divorces + changes_of_heart + 2) * WaifuReset) rounded up + """)] public int WaifuReset { get; set; } = 150; - [Comment(@"The minimum amount of currency that you have to pay -in order to buy a waifu who doesn't have a crush on you. -Default is 1.1 -Example: If a waifu is worth 100, you will have to pay at least 100 * NormalClaim currency to claim her. -(100 * 1.1 = 110)")] + [Comment(""" + The minimum amount of currency that you have to pay + in order to buy a waifu who doesn't have a crush on you. + Default is 1.1 + Example: If a waifu is worth 100, you will have to pay at least 100 * NormalClaim currency to claim her. + (100 * 1.1 = 110) + """)] public decimal NormalClaim { get; set; } = 1.1m; - [Comment(@"The minimum amount of currency that you have to pay -in order to buy a waifu that has a crush on you. -Default is 0.88 -Example: If a waifu is worth 100, you will have to pay at least 100 * CrushClaim currency to claim her. -(100 * 0.88 = 88)")] + [Comment(""" + The minimum amount of currency that you have to pay + in order to buy a waifu that has a crush on you. + Default is 0.88 + Example: If a waifu is worth 100, you will have to pay at least 100 * CrushClaim currency to claim her. + (100 * 0.88 = 88) + """)] public decimal CrushClaim { get; set; } = 0.88M; - [Comment(@"When divorcing a waifu, her new value will be her current value multiplied by this number. -Default 0.75 (meaning will lose 25% of her value)")] + [Comment(""" + When divorcing a waifu, her new value will be her current value multiplied by this number. + Default 0.75 (meaning will lose 25% of her value) + """)] public decimal DivorceNewValue { get; set; } = 0.75M; - [Comment(@"All gift prices will be multiplied by this number. -Default 1 (meaning no effect)")] + [Comment(""" + All gift prices will be multiplied by this number. + Default 1 (meaning no effect) + """)] public decimal AllGiftPrices { get; set; } = 1.0M; - [Comment(@"What percentage of the value of the gift will a waifu gain when she's gifted. -Default 0.95 (meaning 95%) -Example: If a waifu is worth 1000, and she receives a gift worth 100, her new value will be 1095)")] + [Comment(""" + What percentage of the value of the gift will a waifu gain when she's gifted. + Default 0.95 (meaning 95%) + Example: If a waifu is worth 1000, and she receives a gift worth 100, her new value will be 1095) + """)] public decimal GiftEffect { get; set; } = 0.95M; - [Comment(@"What percentage of the value of the gift will a waifu lose when she's gifted a gift marked as 'negative'. -Default 0.5 (meaning 50%) -Example: If a waifu is worth 1000, and she receives a negative gift worth 100, her new value will be 950)")] + [Comment(""" + What percentage of the value of the gift will a waifu lose when she's gifted a gift marked as 'negative'. + Default 0.5 (meaning 50%) + Example: If a waifu is worth 1000, and she receives a negative gift worth 100, her new value will be 950) + """)] public decimal NegativeGiftEffect { get; set; } = 0.50M; } public sealed class SlotsConfig { - [Comment(@"Hex value of the color which the numbers on the slot image will have.")] + [Comment("""Hex value of the color which the numbers on the slot image will have.""")] public Rgba32 CurrencyFontColor { get; set; } = Color.Red; } diff --git a/src/NadekoBot/Modules/Gambling/GamblingService.cs b/src/NadekoBot/Modules/Gambling/GamblingService.cs index 9ee9c2e4b..d64761afa 100644 --- a/src/NadekoBot/Modules/Gambling/GamblingService.cs +++ b/src/NadekoBot/Modules/Gambling/GamblingService.cs @@ -96,9 +96,12 @@ public class GamblingService : INService, IReadyExecutor continue; } - Log.Information(@"Decaying users' currency - decay: {ConfigDecayPercent}% - | max: {MaxDecay} - | threshold: {DecayMinTreshold}", + Log.Information(""" + --- Decaying users' currency --- + | decay: {ConfigDecayPercent}% + | max: {MaxDecay} + | threshold: {DecayMinTreshold} + """, config.Decay.Percent * 100, maxDecay, config.Decay.MinThreshold); diff --git a/src/NadekoBot/Modules/Games/ChatterBot/ChatterbotService.cs b/src/NadekoBot/Modules/Games/ChatterBot/ChatterbotService.cs index b6bbfe508..64011e510 100644 --- a/src/NadekoBot/Modules/Games/ChatterBot/ChatterbotService.cs +++ b/src/NadekoBot/Modules/Games/ChatterBot/ChatterbotService.cs @@ -187,11 +187,13 @@ public class ChatterBotService : IExecOnMessage // , footer: counter > 0 ? counter.ToString() : null ); - Log.Information(@"CleverBot Executed -Server: {GuildName} [{GuildId}] -Channel: {ChannelName} [{ChannelId}] -UserId: {Author} [{AuthorId}] -Message: {Content}", + Log.Information(""" + CleverBot Executed + Server: {GuildName} [{GuildId}] + Channel: {ChannelName} [{ChannelId}] + UserId: {Author} [{AuthorId}] + Message: {Content} + """, guild.Name, guild.Id, usrMsg.Channel?.Name, diff --git a/src/NadekoBot/Modules/Games/GamesConfig.cs b/src/NadekoBot/Modules/Games/GamesConfig.cs index a597d934c..ac2912d73 100644 --- a/src/NadekoBot/Modules/Games/GamesConfig.cs +++ b/src/NadekoBot/Modules/Games/GamesConfig.cs @@ -110,8 +110,10 @@ public sealed partial class TriviaConfig [Comment("The amount of currency awarded to the winner of the trivia game.")] public long CurrencyReward { get; set; } - [Comment(@"Users won't be able to start trivia games which have -a smaller win requirement than the one specified by this setting.")] + [Comment(""" + Users won't be able to start trivia games which have + a smaller win requirement than the one specified by this setting. + """)] public int MinimumWinReq { get; set; } = 1; } diff --git a/src/NadekoBot/Modules/Games/Hangman/HangmanCommands.cs b/src/NadekoBot/Modules/Games/Hangman/HangmanCommands.cs index dc5fb56ef..f519ba6e4 100644 --- a/src/NadekoBot/Modules/Games/Hangman/HangmanCommands.cs +++ b/src/NadekoBot/Modules/Games/Hangman/HangmanCommands.cs @@ -14,13 +14,15 @@ public partial class Games => await SendConfirmAsync(GetText(strs.hangman_types(prefix)), _service.GetHangmanTypes().Join('\n')); private static string Draw(HangmanGame.State state) - => $@". ┌─────┐ -.┃...............┋ -.┃...............┋ -.┃{(state.Errors > 0 ? ".............😲" : "")} -.┃{(state.Errors > 1 ? "............./" : "")} {(state.Errors > 2 ? "|" : "")} {(state.Errors > 3 ? "\\" : "")} -.┃{(state.Errors > 4 ? "............../" : "")} {(state.Errors > 5 ? "\\" : "")} -/-\"; + => $""" + . ┌─────┐ + .┃...............┋ + .┃...............┋ + .┃{(state.Errors > 0 ? ".............😲" : "")} + .┃{(state.Errors > 1 ? "............./" : "")} {(state.Errors > 2 ? "|" : "")} {(state.Errors > 3 ? "\\" : "")} + .┃{(state.Errors > 4 ? "............../" : "")} {(state.Errors > 5 ? "\\" : "")} + /-\ + """; public static IEmbedBuilder GetEmbed(IEmbedBuilderService eb, HangmanGame.State state) { diff --git a/src/NadekoBot/Modules/Games/SpeedTyping/TypingGame.cs b/src/NadekoBot/Modules/Games/SpeedTyping/TypingGame.cs index 8f0af1957..a8bd85ce8 100644 --- a/src/NadekoBot/Modules/Games/SpeedTyping/TypingGame.cs +++ b/src/NadekoBot/Modules/Games/SpeedTyping/TypingGame.cs @@ -69,7 +69,7 @@ public class TypingGame try { await Channel.SendConfirmAsync(_eb, - $@":clock2: Next contest will last for {i} seconds. Type the bolded text as fast as you can."); + $":clock2: Next contest will last for {i} seconds. Type the bolded text as fast as you can."); var time = _options.StartTime; diff --git a/src/NadekoBot/Modules/Games/Trivia/TriviaQuestion.cs b/src/NadekoBot/Modules/Games/Trivia/TriviaQuestion.cs index f16456a7a..b6cb1fe13 100644 --- a/src/NadekoBot/Modules/Games/Trivia/TriviaQuestion.cs +++ b/src/NadekoBot/Modules/Games/Trivia/TriviaQuestion.cs @@ -78,13 +78,13 @@ public class TriviaQuestion private static string Clean(string str) { str = " " + str.ToLowerInvariant() + " "; - str = Regex.Replace(str, "\\s+", " "); - str = Regex.Replace(str, "[^\\w\\d\\s]", ""); + str = Regex.Replace(str, @"\s+", " "); + str = Regex.Replace(str, @"[^\w\d\s]", ""); //Here's where custom modification can be done - str = Regex.Replace(str, "\\s(a|an|the|of|in|for|to|as|at|be)\\s", " "); + str = Regex.Replace(str, @"\s(a|an|the|of|in|for|to|as|at|be)\s", " "); //End custom mod and cleanup whitespace - str = Regex.Replace(str, "^\\s+", ""); - str = Regex.Replace(str, "\\s+$", ""); + str = Regex.Replace(str, @"^\s+", ""); + str = Regex.Replace(str, @"\s+$", ""); //Trim the really long answers str = str.Length <= MAX_STRING_LENGTH ? str : str[..MAX_STRING_LENGTH]; return str; diff --git a/src/NadekoBot/Modules/Help/Help.cs b/src/NadekoBot/Modules/Help/Help.cs index 49bd46c7d..e45f849b9 100644 --- a/src/NadekoBot/Modules/Help/Help.cs +++ b/src/NadekoBot/Modules/Help/Help.cs @@ -490,13 +490,15 @@ public partial class Help : NadekoModule private Task SelfhostAction(SocketMessageComponent smc, object _) => smc.RespondConfirmAsync(_eb, - @"- In case you don't want or cannot Donate to NadekoBot project, but you -- NadekoBot is a completely free and fully [open source](https://gitlab.com/kwoth/nadekobot) project which means you can run your own ""selfhosted"" instance on your computer or server for free. - -*Keep in mind that running the bot on your computer means that the bot will be offline when you turn off your computer* - -- You can find the selfhosting guides by using the `.guide` command and clicking on the second link that pops up. -- If you decide to selfhost the bot, still consider [supporting the project](https://patreon.com/join/nadekobot) to keep the development going :)", + """ + - In case you don't want or cannot Donate to NadekoBot project, but you + - NadekoBot is a completely free and fully [open source](https://gitlab.com/kwoth/nadekobot) project which means you can run your own "selfhosted" instance on your computer or server for free. + + *Keep in mind that running the bot on your computer means that the bot will be offline when you turn off your computer* + + - You can find the selfhosting guides by using the `.guide` command and clicking on the second link that pops up. + - If you decide to selfhost the bot, still consider [supporting the project](https://patreon.com/join/nadekobot) to keep the development going :) + """, true); [Cmd] @@ -541,12 +543,14 @@ Nadeko will DM you the welcome instructions, and you may start using the patron- 🎉 **Enjoy!** 🎉 ") .AddField("Troubleshooting", - @" -*In case you didn't receive the rewards within 5 minutes:* -`1.` Make sure your DMs are open to everyone. Maybe your pledge was processed successfully but the bot was unable to DM you. Use the `.patron` command to check your status. -`2.` Make sure you've connected the CORRECT Discord account. Quite often users log in to different Discord accounts in their browser. You may also try disconnecting and reconnecting your account. -`3.` Make sure your payment has been processed and not declined by Patreon. -`4.` If any of the previous steps don't help, you can join the nadeko support server and ask for help in the #help channel"); + """ + + *In case you didn't receive the rewards within 5 minutes:* + `1.` Make sure your DMs are open to everyone. Maybe your pledge was processed successfully but the bot was unable to DM you. Use the `.patron` command to check your status. + `2.` Make sure you've connected the CORRECT Discord account. Quite often users log in to different Discord accounts in their browser. You may also try disconnecting and reconnecting your account. + `3.` Make sure your payment has been processed and not declined by Patreon. + `4.` If any of the previous steps don't help, you can join the nadeko support server and ask for help in the #help channel + """); try { diff --git a/src/NadekoBot/Modules/Music/_Common/Impl/MusicExtensions.cs b/src/NadekoBot/Modules/Music/_Common/Impl/MusicExtensions.cs index bfa8acc98..e06cc6251 100644 --- a/src/NadekoBot/Modules/Music/_Common/Impl/MusicExtensions.cs +++ b/src/NadekoBot/Modules/Music/_Common/Impl/MusicExtensions.cs @@ -39,9 +39,9 @@ public static class MusicExtensions if (trackInfo.Duration == TimeSpan.MaxValue) return "∞"; if (trackInfo.Duration.TotalHours >= 1) - return trackInfo.Duration.ToString(@"hh\:mm\:ss"); + return trackInfo.Duration.ToString("""hh\:mm\:ss"""); - return trackInfo.Duration.ToString(@"mm\:ss"); + return trackInfo.Duration.ToString("""mm\:ss"""); } public static ICachableTrackData ToCachedData(this ITrackInfo trackInfo, string id) diff --git a/src/NadekoBot/Modules/Music/_Common/Resolvers/RadioResolveStrategy.cs b/src/NadekoBot/Modules/Music/_Common/Resolvers/RadioResolveStrategy.cs index 3a9e10063..dc783e56b 100644 --- a/src/NadekoBot/Modules/Music/_Common/Resolvers/RadioResolveStrategy.cs +++ b/src/NadekoBot/Modules/Music/_Common/Resolvers/RadioResolveStrategy.cs @@ -5,10 +5,10 @@ namespace NadekoBot.Modules.Music.Resolvers; public class RadioResolver : IRadioResolver { - private readonly Regex _plsRegex = new("File1=(?.*?)\\n", RegexOptions.Compiled); - private readonly Regex _m3URegex = new("(?^[^#].*)", RegexOptions.Compiled | RegexOptions.Multiline); - private readonly Regex _asxRegex = new(".*?)\"", RegexOptions.Compiled); - private readonly Regex _xspfRegex = new("(?.*?)", RegexOptions.Compiled); + private readonly Regex _plsRegex = new(@"File1=(?.*?)\n", RegexOptions.Compiled); + private readonly Regex _m3URegex = new(@"(?^[^#].*)", RegexOptions.Compiled | RegexOptions.Multiline); + private readonly Regex _asxRegex = new(@".*?)""", RegexOptions.Compiled); + private readonly Regex _xspfRegex = new(@"(?.*?)", RegexOptions.Compiled); public async Task ResolveByQueryAsync(string query) { diff --git a/src/NadekoBot/Modules/Searches/Feeds/FeedCommands.cs b/src/NadekoBot/Modules/Searches/Feeds/FeedCommands.cs index 0a3ea9415..3afacad46 100644 --- a/src/NadekoBot/Modules/Searches/Feeds/FeedCommands.cs +++ b/src/NadekoBot/Modules/Searches/Feeds/FeedCommands.cs @@ -24,7 +24,7 @@ public partial class Searches var channelId = m.Groups["channelid"].Value; - return Feed("https://www.youtube.com/feeds/videos.xml?channel_id=" + channelId, channel, message); + return Feed($"https://www.youtube.com/feeds/videos.xml?channel_id={channelId}", channel, message); } [Cmd] diff --git a/src/NadekoBot/Modules/Searches/PathOfExileCommands.cs b/src/NadekoBot/Modules/Searches/PathOfExileCommands.cs index b53a09f99..d12acf3ac 100644 --- a/src/NadekoBot/Modules/Searches/PathOfExileCommands.cs +++ b/src/NadekoBot/Modules/Searches/PathOfExileCommands.cs @@ -304,7 +304,7 @@ public partial class Searches private static string ShortLeagueName(string str) { - var league = Regex.Replace(str, "Hardcore", "HC", RegexOptions.IgnoreCase); + var league = str.Replace("Hardcore", "HC", StringComparison.InvariantCultureIgnoreCase); return league; } diff --git a/src/NadekoBot/Modules/Searches/_Common/Config/SearchesConfig.cs b/src/NadekoBot/Modules/Searches/_Common/Config/SearchesConfig.cs index e05010a0a..a9b7165ff 100644 --- a/src/NadekoBot/Modules/Searches/_Common/Config/SearchesConfig.cs +++ b/src/NadekoBot/Modules/Searches/_Common/Config/SearchesConfig.cs @@ -9,51 +9,61 @@ public partial class SearchesConfig : ICloneable [Comment("DO NOT CHANGE")] public int Version { get; set; } = 0; - [Comment(@"Which engine should .search command -'google_scrape' - default. Scrapes the webpage for results. May break. Requires no api keys. -'google' - official google api. Requires googleApiKey and google.searchId set in creds.yml -'searx' - requires at least one searx instance specified in the 'searxInstances' property below")] + [Comment(""" + Which engine should .search command + 'google_scrape' - default. Scrapes the webpage for results. May break. Requires no api keys. + 'google' - official google api. Requires googleApiKey and google.searchId set in creds.yml + 'searx' - requires at least one searx instance specified in the 'searxInstances' property below + """)] public WebSearchEngine WebSearchEngine { get; set; } = WebSearchEngine.Google_Scrape; - [Comment(@"Which engine should .image command use -'google'- official google api. googleApiKey and google.imageSearchId set in creds.yml -'searx' requires at least one searx instance specified in the 'searxInstances' property below")] + [Comment(""" + Which engine should .image command use + 'google'- official google api. googleApiKey and google.imageSearchId set in creds.yml + 'searx' requires at least one searx instance specified in the 'searxInstances' property below + """)] public ImgSearchEngine ImgSearchEngine { get; set; } = ImgSearchEngine.Google; - [Comment(@"Which search provider will be used for the `.youtube` command. - -- `ytDataApiv3` - uses google's official youtube data api. Requires `GoogleApiKey` set in creds and youtube data api enabled in developers console - -- `ytdl` - default, uses youtube-dl. Requires `youtube-dl` to be installed and it's path added to env variables. Slow. - -- `ytdlp` - recommended easy, uses `yt-dlp`. Requires `yt-dlp` to be installed and it's path added to env variables - -- `invidious` - recommended advanced, uses invidious api. Requires at least one invidious instance specified in the `invidiousInstances` property")] + [Comment(""" + Which search provider will be used for the `.youtube` command. + + - `ytDataApiv3` - uses google's official youtube data api. Requires `GoogleApiKey` set in creds and youtube data api enabled in developers console + + - `ytdl` - default, uses youtube-dl. Requires `youtube-dl` to be installed and it's path added to env variables. Slow. + + - `ytdlp` - recommended easy, uses `yt-dlp`. Requires `yt-dlp` to be installed and it's path added to env variables + + - `invidious` - recommended advanced, uses invidious api. Requires at least one invidious instance specified in the `invidiousInstances` property + """)] public YoutubeSearcher YtProvider { get; set; } = YoutubeSearcher.Ytdl; - [Comment(@"Set the searx instance urls in case you want to use 'searx' for either img or web search. -Nadeko will use a random one for each request. -Use a fully qualified url. Example: `https://my-searx-instance.mydomain.com` -Instances specified must support 'format=json' query parameter. -- In case you're running your own searx instance, set - -search: - formats: - - json - -in 'searxng/settings.yml' on your server - -- If you're using a public instance, make sure that the instance you're using supports it (they usually don't)")] + [Comment(""" + Set the searx instance urls in case you want to use 'searx' for either img or web search. + Nadeko will use a random one for each request. + Use a fully qualified url. Example: `https://my-searx-instance.mydomain.com` + Instances specified must support 'format=json' query parameter. + - In case you're running your own searx instance, set + + search: + formats: + - json + + in 'searxng/settings.yml' on your server + + - If you're using a public instance, make sure that the instance you're using supports it (they usually don't) + """)] public List SearxInstances { get; set; } = new List(); - [Comment(@"Set the invidious instance urls in case you want to use 'invidious' for `.youtube` search -Nadeko will use a random one for each request. -These instances may be used for music queue functionality in the future. -Use a fully qualified url. Example: https://my-invidious-instance.mydomain.com - -Instances specified must have api available. -You check that by opening an api endpoint in your browser. For example: https://my-invidious-instance.mydomain.com/api/v1/trending")] + [Comment(""" + Set the invidious instance urls in case you want to use 'invidious' for `.youtube` search + Nadeko will use a random one for each request. + These instances may be used for music queue functionality in the future. + Use a fully qualified url. Example: https://my-invidious-instance.mydomain.com + + Instances specified must have api available. + You check that by opening an api endpoint in your browser. For example: https://my-invidious-instance.mydomain.com/api/v1/trending + """)] public List InvidiousInstances { get; set; } = new List(); } diff --git a/src/NadekoBot/Modules/Searches/_Common/StreamNotifications/Providers/TrovoProvider.cs b/src/NadekoBot/Modules/Searches/_Common/StreamNotifications/Providers/TrovoProvider.cs index f0ebdbb7a..df3fcd3fd 100644 --- a/src/NadekoBot/Modules/Searches/_Common/StreamNotifications/Providers/TrovoProvider.cs +++ b/src/NadekoBot/Modules/Searches/_Common/StreamNotifications/Providers/TrovoProvider.cs @@ -23,8 +23,10 @@ public class TrovoProvider : Provider if (string.IsNullOrWhiteSpace(creds.GetCreds().TrovoClientId)) { - Log.Warning(@"Trovo streams are using a default clientId. -If you are experiencing ratelimits, you should create your own application at: https://developer.trovo.live/"); + Log.Warning(""" + Trovo streams are using a default clientId. + If you are experiencing ratelimits, you should create your own application at: https://developer.trovo.live/ + """); } } diff --git a/src/NadekoBot/Modules/Utility/Info/InviteCommands.cs b/src/NadekoBot/Modules/Utility/Info/InviteCommands.cs index 775402e5c..d9633019b 100644 --- a/src/NadekoBot/Modules/Utility/Info/InviteCommands.cs +++ b/src/NadekoBot/Modules/Utility/Info/InviteCommands.cs @@ -52,7 +52,7 @@ public partial class Utility var expiryString = inv.MaxAge is null or 0 || inv.CreatedAt is null ? "∞" : (inv.CreatedAt.Value.AddSeconds(inv.MaxAge.Value).UtcDateTime - DateTime.UtcNow).ToString( - @"d\.hh\:mm\:ss"); + """d\.hh\:mm\:ss"""); var creator = inv.Inviter.ToString().TrimTo(25); var usesString = $"{inv.Uses} / {(inv.MaxUses == 0 ? "∞" : inv.MaxUses?.ToString())}"; diff --git a/src/NadekoBot/Modules/Utility/Patronage/PatronageService.cs b/src/NadekoBot/Modules/Utility/Patronage/PatronageService.cs index f4588896b..bbc3d301c 100644 --- a/src/NadekoBot/Modules/Utility/Patronage/PatronageService.cs +++ b/src/NadekoBot/Modules/Utility/Patronage/PatronageService.cs @@ -782,13 +782,15 @@ public sealed class PatronageService patron.ValidThru.AddDays(1).ToShortAndRelativeTimestampTag(), true) .AddField("Instructions", - @"*- Within the next **1-2 minutes** you will have all of the benefits of the Tier you've subscribed to.* -*- You can check your benefits on * -*- You can use the `.patron` command in this chat to check your current quota usage for the Patron-only commands* -*- **ALL** of the servers that you **own** will enjoy your Patron benefits.* -*- You can use any of the commands available in your tier on any server (assuming you have sufficient permissions to run those commands)* -*- Any user in any of your servers can use Patron-only commands, but they will spend **your quota**, which is why it's recommended to use Nadeko's command cooldown system (.h .cmdcd) or permission system to limit the command usage for your server members.* -*- Permission guide can be found here if you're not familiar with it: *", + """ + *- Within the next **1-2 minutes** you will have all of the benefits of the Tier you've subscribed to.* + *- You can check your benefits on * + *- You can use the `.patron` command in this chat to check your current quota usage for the Patron-only commands* + *- **ALL** of the servers that you **own** will enjoy your Patron benefits.* + *- You can use any of the commands available in your tier on any server (assuming you have sufficient permissions to run those commands)* + *- Any user in any of your servers can use Patron-only commands, but they will spend **your quota**, which is why it's recommended to use Nadeko's command cooldown system (.h .cmdcd) or permission system to limit the command usage for your server members.* + *- Permission guide can be found here if you're not familiar with it: * + """, isInline: false) .WithFooter($"platform id: {patron.UniquePlatformUserId}"); diff --git a/src/NadekoBot/Modules/Utility/Quote/QuoteCommands.cs b/src/NadekoBot/Modules/Utility/Quote/QuoteCommands.cs index 05edfba95..f2290a59b 100644 --- a/src/NadekoBot/Modules/Utility/Quote/QuoteCommands.cs +++ b/src/NadekoBot/Modules/Utility/Quote/QuoteCommands.cs @@ -13,12 +13,14 @@ public partial class Utility public partial class QuoteCommands : NadekoModule { private const string PREPEND_EXPORT = - @"# Keys are keywords, Each key has a LIST of quotes in the following format: -# - id: Alphanumeric id used for commands related to the quote. (Note, when using .quotesimport, a new id will be generated.) -# an: Author name -# aid: Author id -# txt: Quote text -"; + """ + # Keys are keywords, Each key has a LIST of quotes in the following format: + # - id: Alphanumeric id used for commands related to the quote. (Note, when using .quotesimport, a new id will be generated.) + # an: Author name + # aid: Author id + # txt: Quote text + + """; private static readonly ISerializer _exportSerializer = new SerializerBuilder() .WithEventEmitter(args diff --git a/src/NadekoBot/Modules/Xp/XpConfig.cs b/src/NadekoBot/Modules/Xp/XpConfig.cs index 3d63e44e7..54c0a78d3 100644 --- a/src/NadekoBot/Modules/Xp/XpConfig.cs +++ b/src/NadekoBot/Modules/Xp/XpConfig.cs @@ -9,58 +9,68 @@ namespace NadekoBot.Modules.Xp; [Cloneable] public sealed partial class XpConfig : ICloneable { - [Comment(@"DO NOT CHANGE")] + [Comment("""DO NOT CHANGE""")] public int Version { get; set; } = 5; - [Comment(@"How much XP will the users receive per message")] + [Comment("""How much XP will the users receive per message""")] public int XpPerMessage { get; set; } = 3; - [Comment(@"How often can the users receive XP in minutes")] + [Comment("""How often can the users receive XP in minutes""")] public int MessageXpCooldown { get; set; } = 5; - [Comment(@"Amount of xp users gain from posting an image")] + [Comment("""Amount of xp users gain from posting an image""")] public int XpFromImage { get; set; } = 0; - [Comment(@"Average amount of xp earned per minute in VC")] + [Comment("""Average amount of xp earned per minute in VC""")] public double VoiceXpPerMinute { get; set; } = 0; - [Comment(@"The maximum amount of minutes the bot will keep track of a user in a voice channel")] + [Comment("""The maximum amount of minutes the bot will keep track of a user in a voice channel""")] public int VoiceMaxMinutes { get; set; } = 720; - [Comment(@"The amount of currency users will receive for each point of global xp that they earn")] + [Comment("""The amount of currency users will receive for each point of global xp that they earn""")] public float CurrencyPerXp { get; set; } = 0; - [Comment(@"Xp Shop config")] + [Comment("""Xp Shop config""")] public ShopConfig Shop { get; set; } = new(); public sealed class ShopConfig { - [Comment(@"Whether the xp shop is enabled -True -> Users can access the xp shop using .xpshop command -False -> Users can't access the xp shop")] + [Comment(""" + Whether the xp shop is enabled + True -> Users can access the xp shop using .xpshop command + False -> Users can't access the xp shop + """)] public bool IsEnabled { get; set; } = false; - [Comment(@"Which patron tier do users need in order to use the .xpshop bgs command -Leave at 'None' if patron system is disabled or you don't want any restrictions")] + [Comment(""" + Which patron tier do users need in order to use the .xpshop bgs command + Leave at 'None' if patron system is disabled or you don't want any restrictions + """)] public PatronTier BgsTierRequirement { get; set; } = PatronTier.None; - [Comment(@"Which patron tier do users need in order to use the .xpshop frames command -Leave at 'None' if patron system is disabled or you don't want any restrictions")] + [Comment(""" + Which patron tier do users need in order to use the .xpshop frames command + Leave at 'None' if patron system is disabled or you don't want any restrictions + """)] public PatronTier FramesTierRequirement { get; set; } = PatronTier.None; - [Comment(@"Frames available for sale. Keys are unique IDs. -Do not change keys as they are not publicly visible. Only change properties (name, price, id) -Removing a key which previously existed means that all previous purchases will also be unusable. -To remove an item from the shop, but keep previous purchases, set the price to -1")] + [Comment(""" + Frames available for sale. Keys are unique IDs. + Do not change keys as they are not publicly visible. Only change properties (name, price, id) + Removing a key which previously existed means that all previous purchases will also be unusable. + To remove an item from the shop, but keep previous purchases, set the price to -1 + """)] public Dictionary? Frames { get; set; } = new() { {"default", new() {Name = "No frame", Price = 0, Url = string.Empty}} }; - [Comment(@"Backgrounds available for sale. Keys are unique IDs. -Do not change keys as they are not publicly visible. Only change properties (name, price, id) -Removing a key which previously existed means that all previous purchases will also be unusable. -To remove an item from the shop, but keep previous purchases, set the price to -1")] + [Comment(""" + Backgrounds available for sale. Keys are unique IDs. + Do not change keys as they are not publicly visible. Only change properties (name, price, id) + Removing a key which previously existed means that all previous purchases will also be unusable. + To remove an item from the shop, but keep previous purchases, set the price to -1 + """)] public Dictionary? Bgs { get; set; } = new() { {"default", new() {Name = "Default Background", Price = 0, Url = string.Empty}} @@ -69,19 +79,19 @@ To remove an item from the shop, but keep previous purchases, set the price to - public sealed class ShopItemInfo { - [Comment(@"Visible name of the item")] + [Comment("""Visible name of the item""")] public string Name { get; set; } - [Comment(@"Price of the item. Set to -1 if you no longer want to sell the item but want the users to be able to keep their old purchase")] + [Comment("""Price of the item. Set to -1 if you no longer want to sell the item but want the users to be able to keep their old purchase""")] public int Price { get; set; } - [Comment(@"Direct url to the .png image which will be applied to the user's XP card")] + [Comment("""Direct url to the .png image which will be applied to the user's XP card""")] public string Url { get; set; } - [Comment(@"Optional preview url which will show instead of the real URL in the shop ")] + [Comment("""Optional preview url which will show instead of the real URL in the shop """)] public string Preview { get; set; } - [Comment(@"Optional description of the item")] + [Comment("""Optional description of the item""")] public string Desc { get; set; } } } diff --git a/src/NadekoBot/Modules/Xp/XpRewards.cs b/src/NadekoBot/Modules/Xp/XpRewards.cs index f25056b43..36c152eb7 100644 --- a/src/NadekoBot/Modules/Xp/XpRewards.cs +++ b/src/NadekoBot/Modules/Xp/XpRewards.cs @@ -43,7 +43,7 @@ public partial class Xp .OrderBy(x => x.Level) .Select(x => { - var sign = !x.Remove ? @"✅ " : @"❌ "; + var sign = !x.Remove ? "✅ " : "❌ "; var str = ctx.Guild.GetRole(x.RoleId)?.ToString(); diff --git a/src/NadekoBot/Services/CommandHandler.cs b/src/NadekoBot/Services/CommandHandler.cs index 3cb047854..f7ac2a3d0 100644 --- a/src/NadekoBot/Services/CommandHandler.cs +++ b/src/NadekoBot/Services/CommandHandler.cs @@ -147,11 +147,13 @@ public class CommandHandler : INService, IReadyExecutor { if (_bss.Data.ConsoleOutputType == ConsoleOutputType.Normal) { - Log.Information(@"Command Executed after {ExecTime}s - User: {User} - Server: {Server} - Channel: {Channel} - Message: {Message}", + Log.Information(""" + Command Executed after {ExecTime}s + User: {User} + Server: {Server} + Channel: {Channel} + Message: {Message} + """, string.Join("/", execPoints.Select(x => (x * ONE_THOUSANDTH).ToString("F3"))), usrMsg.Author + " [" + usrMsg.Author.Id + "]", channel is null ? "PRIVATE" : channel.Guild.Name + " [" + channel.Guild.Id + "]", @@ -178,12 +180,14 @@ public class CommandHandler : INService, IReadyExecutor { if (_bss.Data.ConsoleOutputType == ConsoleOutputType.Normal) { - Log.Warning(@"Command Errored after {ExecTime}s - User: {User} - Server: {Guild} - Channel: {Channel} - Message: {Message} - Error: {ErrorMessage}", + Log.Warning(""" + Command Errored after {ExecTime}s + User: {User} + Server: {Guild} + Channel: {Channel} + Message: {Message} + Error: {ErrorMessage} + """, string.Join("/", execPoints.Select(x => (x * ONE_THOUSANDTH).ToString("F3"))), usrMsg.Author + " [" + usrMsg.Author.Id + "]", channel is null ? "DM" : channel.Guild.Name + " [" + channel.Guild.Id + "]", @@ -193,8 +197,10 @@ public class CommandHandler : INService, IReadyExecutor } else { - Log.Warning(@"Err | g:{GuildId} | c: {ChannelId} | u: {UserId} | msg: {Message} - Err: {ErrorMessage}", + Log.Warning(""" + Err | g:{GuildId} | c: {ChannelId} | u: {UserId} | msg: {Message} + Err: {ErrorMessage} + """, channel?.Guild.Id.ToString() ?? "-", channel?.Id.ToString() ?? "-", usrMsg.Author.Id, diff --git a/src/NadekoBot/Services/Impl/GoogleApiService.cs b/src/NadekoBot/Services/Impl/GoogleApiService.cs index dd7b06189..9c91bd503 100644 --- a/src/NadekoBot/Services/Impl/GoogleApiService.cs +++ b/src/NadekoBot/Services/Impl/GoogleApiService.cs @@ -13,7 +13,7 @@ namespace NadekoBot.Services; public sealed partial class GoogleApiService : IGoogleApiService, INService { private static readonly Regex - _plRegex = new("(?:youtu\\.be\\/|list=)(?[\\da-zA-Z\\-_]*)", RegexOptions.Compiled); + _plRegex = new(@"(?:youtu\.be\/|list=)(?[\da-zA-Z\-_]*)", RegexOptions.Compiled); private readonly YouTubeService _yt; diff --git a/src/NadekoBot/Services/Impl/GoogleApiService_SupportedLanguages.cs b/src/NadekoBot/Services/Impl/GoogleApiService_SupportedLanguages.cs index 6b1023bd3..365d4f5f4 100644 --- a/src/NadekoBot/Services/Impl/GoogleApiService_SupportedLanguages.cs +++ b/src/NadekoBot/Services/Impl/GoogleApiService_SupportedLanguages.cs @@ -2,140 +2,142 @@ public sealed partial class GoogleApiService { - private const string SUPPORTED = @"afrikaans af -albanian sq -amharic am -arabic ar -armenian hy -assamese as -aymara ay -azerbaijani az -bambara bm -basque eu -belarusian be -bengali bn -bhojpuri bho -bosnian bs -bulgarian bg -catalan ca -cebuano ceb -chinese zh-CN -chinese-trad zh-TW -corsican co -croatian hr -czech cs -danish da -dhivehi dv -dogri doi -dutch nl -english en -esperanto eo -estonian et -ewe ee -filipino fil -finnish fi -french fr -frisian fy -galician gl -georgian ka -german de -greek el -guarani gn -gujarati gu -haitian ht -hausa ha -hawaiian haw -hebrew he -hindi hi -hmong hmn -hungarian hu -icelandic is -igbo ig -ilocano ilo -indonesian id -irish ga -italian it -japanese ja -javanese jv -kannada kn -kazakh kk -khmer km -kinyarwanda rw -konkani gom -korean ko -krio kri -kurdish ku -kurdish-sor ckb -kyrgyz ky -lao lo -latin la -latvian lv -lingala ln -lithuanian lt -luganda lg -luxembourgish lb -macedonian mk -maithili mai -malagasy mg -malay ms -malayalam ml -maltese mt -maori mi -marathi mr -meiteilon mni-Mtei -mizo lus -mongolian mn -myanmar my -nepali ne -norwegian no -nyanja ny -odia or -oromo om -pashto ps -persian fa -polish pl -portuguese pt -punjabi pa -quechua qu -romanian ro -russian ru -samoan sm -sanskrit sa -scots gd -sepedi nso -serbian sr -sesotho st -shona sn -sindhi sd -sinhala si -slovak sk -slovenian sl -somali so -spanish es -sundanese su -swahili sw -swedish sv -tagalog tl -tajik tg -tamil ta -tatar tt -telugu te -thai th -tigrinya ti -tsonga ts -turkish tr -turkmen tk -twi ak -ukrainian uk -urdu ur -uyghur ug -uzbek uz -vietnamese vi -welsh cy -xhosa xh -yiddish yi -yoruba yo -zulu zu"; + private const string SUPPORTED = """ + afrikaans af + albanian sq + amharic am + arabic ar + armenian hy + assamese as + aymara ay + azerbaijani az + bambara bm + basque eu + belarusian be + bengali bn + bhojpuri bho + bosnian bs + bulgarian bg + catalan ca + cebuano ceb + chinese zh-CN + chinese-trad zh-TW + corsican co + croatian hr + czech cs + danish da + dhivehi dv + dogri doi + dutch nl + english en + esperanto eo + estonian et + ewe ee + filipino fil + finnish fi + french fr + frisian fy + galician gl + georgian ka + german de + greek el + guarani gn + gujarati gu + haitian ht + hausa ha + hawaiian haw + hebrew he + hindi hi + hmong hmn + hungarian hu + icelandic is + igbo ig + ilocano ilo + indonesian id + irish ga + italian it + japanese ja + javanese jv + kannada kn + kazakh kk + khmer km + kinyarwanda rw + konkani gom + korean ko + krio kri + kurdish ku + kurdish-sor ckb + kyrgyz ky + lao lo + latin la + latvian lv + lingala ln + lithuanian lt + luganda lg + luxembourgish lb + macedonian mk + maithili mai + malagasy mg + malay ms + malayalam ml + maltese mt + maori mi + marathi mr + meiteilon mni-Mtei + mizo lus + mongolian mn + myanmar my + nepali ne + norwegian no + nyanja ny + odia or + oromo om + pashto ps + persian fa + polish pl + portuguese pt + punjabi pa + quechua qu + romanian ro + russian ru + samoan sm + sanskrit sa + scots gd + sepedi nso + serbian sr + sesotho st + shona sn + sindhi sd + sinhala si + slovak sk + slovenian sl + somali so + spanish es + sundanese su + swahili sw + swedish sv + tagalog tl + tajik tg + tamil ta + tatar tt + telugu te + thai th + tigrinya ti + tsonga ts + turkish tr + turkmen tk + twi ak + ukrainian uk + urdu ur + uyghur ug + uzbek uz + vietnamese vi + welsh cy + xhosa xh + yiddish yi + yoruba yo + zulu zu + """; public IReadOnlyDictionary Languages { get; }