Compare commits

..

19 Commits
4.2.2 ... 4.2.6

Author SHA1 Message Date
Kwoth
1716c69132 Upped version to 4.2.6 2022-06-22 09:56:42 +02:00
Kwoth
14bfcb54dc Patron sytem should be *disabled* on selfhosted bots by default. Commited true by mistake. 2022-06-22 09:55:09 +02:00
Kwoth
9f445c0866 Fixed a patron bug which didn't let patrons execute patron commands in non-patron servers 2022-06-18 10:29:33 +02:00
Kwoth
3343fd2f6e Allow docker build to fail 2022-06-18 02:13:43 +02:00
Kwoth
9103dd9fdb Upped version to 4.2.5 as ci didn't run for 4.2.4 2022-06-18 02:07:25 +02:00
Kwoth
1a8c9a6cba [skip ci] Version upped to 4.2.4, updated CHANGELOG.md 2022-06-17 22:57:03 +02:00
Kwoth
9d2f251923 Fixed crypto deserialization issue 2022-06-17 14:04:22 +02:00
Kwoth
3744dd287c Revert "Fixed .crypto - some extra fields which were causing deserialization issues"
This reverts commit f65ba100af.
2022-06-17 14:03:30 +02:00
Kwoth
f65ba100af Fixed .crypto - some extra fields which were causing deserialization issues 2022-06-17 14:02:38 +02:00
Kwoth
cc52605c90 [skip ci] Upped version to 4.2.3 2022-06-17 04:42:30 +02:00
Kwoth
3d3dc532dc Made .timely use timestamp tags and fixed a bug 2022-06-17 04:37:08 +02:00
Kwoth
6c58a6a72d Merge branch 'v4' of https://gitlab.com/kwoth/nadekobot into v4 2022-06-16 21:28:16 +02:00
Kwoth
cefd81d810 [skip ci] Use shared coinmarket key instead of public bot's 2022-06-16 21:27:51 +02:00
Kwoth
34c96c697a Merge branch 'hokutochen-v4-patch-06658' into 'v4'
updating docs and some code

See merge request Kwoth/nadekobot!250
2022-06-16 15:56:30 +00:00
Hokuto Chen
1cc5e0e1d8 updating docs and some code 2022-06-16 15:56:30 +00:00
Kwoth
deaedce6c7 Renamed some of the classes which still had 'Cr' instead of 'Expr' in them 2022-06-16 04:03:59 +02:00
Kwoth
91e4d9dffc permission commands should now work for global expressions too 2022-06-16 03:59:45 +02:00
Kwoth
a826f4245f Fixed .streamrole not updating in real time, closes #345 2022-06-16 03:37:19 +02:00
Kwoth
780eec62b3 [ci skip] undoed .gencmdlist path, no effect 2022-06-16 00:56:31 +02:00
27 changed files with 254 additions and 145 deletions

View File

@@ -113,6 +113,7 @@ docker-build:
# Use the official docker image.
image: docker:latest
stage: build
allow_failure: true
services:
- docker:dind
before_script:

View File

@@ -3,6 +3,26 @@
Experimental changelog. Mostly based on [keepachangelog](https://keepachangelog.com/en/1.0.0/) except date format. a-c-f-r-o
## [4.2.6] - 22.06.2022
### Fixed
- Patron system should now properly by disabled on selfhosts by default.
## [4.2.5] - 18.06.2022
### Fixed
- Fixed `.crypto`, you will still need coinmarketcapApiKey in `creds.yml` in order to make it run consistently as the key is shared
## [4.2.3] - 17.06.2022
### Fixed
- Fixed `.timely` nullref bug and made it nicer
- Fixed `.streamrole` not updating in real time!
- Disabling specific Global Expressions should now work with `.sc` (and other permission commands)
## [4.2.2] - 15.06.2022
### Fixed

View File

@@ -3,19 +3,19 @@
### Important
- For modifying **global** expressions, the ones which will work across all the servers your bot is connected to, you **must** be a Bot Owner.
You must also use the commands for adding, deleting and listing these reactions in a direct message with the bot.
You must also use the commands for adding, deleting and listing these expressions in a direct message with the bot.
- For modifying **local** expressions, the ones which will only work on the server that they are added on, it is required to have the **Administrator** permission.
You must also use the commands for adding, deleting and listing these reactions in the server you want the expressions to work on.
You must also use the commands for adding, deleting and listing these expressions in the server you want the expressions to work on.
### Commands and Their Use
| Command Name | Description | Example |
| :----------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------- |
| `.exadd` | Add an expression with a trigger and a response. Running this command in a server requries the Administrator permission. Running this command in DM is Bot Owner only, and adds a new global expression. | `.exadd "hello" Hi there, %user%!` |
| `exl` | Lists a page of global or server expression(15 reactions / expressions per page). Running this command in a DM will list the global expression, while running it in a server will list that server's expression. | `.exl 1` |
| `.exa` | Add an expression with a trigger and a response. Running this command in a server requries the Administrator permission. Running this command in DM is Bot Owner only, and adds a new global expression. | `.exadd "hello" Hi there, %user%!` |
| `exl` | Lists a page of global or server expression(15 expressions per page). Running this command in a DM will list the global expression, while running it in a server will list that server's expression. | `.exl 1` |
| `.exd` | Deletes an expression based on the provided index. Running this command in a server requires the Administrator permission. Running this command in DM is Bot Owner only, and will delete a global expression. | `.exd 5` |
#### Now that we know the commands let's take a look at an example of adding a command with `.acr`,
#### Now that we know the commands let's take a look at an example of adding a command with `.exa`,
`.exadd "Nice Weather" It sure is, %user%!`
@@ -35,7 +35,7 @@ Now, if that command was ran in a server, anyone on that server can make the bot
If you want to disable a global expression which you do not like, and you do not want to remove it, or you are not the bot owner, you can do so by adding a new expression with the same trigger on your server, and set the response to `-`.
For example:
`.acr /o/ -`
`.exa /o/ -`
Now if you try to trigger `/o/`, it won't print anything even if there is a global expression with the same name.

View File

@@ -17,7 +17,7 @@ It is recommended that you use **Ubuntu 20.04**, as there have been nearly no pr
##### Compatible operating systems:
- Ubuntu: 16.04, 18.04, 20.04, 21.04, 21.10
- Ubuntu: 16.04, 18.04, 20.04, 21.04, 21.10 22.04
- Mint: 19, 20
- Debian: 9, 10
- CentOS: 7
@@ -63,9 +63,20 @@ Open Terminal (if you're on an installation with a window manager) and navigate
4. Run the bot (type `3` and press enter)
5. 🎉
## **⚠ IF YOU ARE FOLLOWING THE GUIDE ABOVE, IGNORE THIS SECTION ⚠**
## Linux Release
**⚠ IF YOU ARE FOLLOWING THE GUIDE ABOVE, IGNORE THIS SECTION ⚠**
###### Prerequisites
1. Nadeko requires redis to function
- ubuntu installation command: `sudo apt-get install redis-server`
2. Playing music requires `ffmpeg`, `libopus`, `libsodium` and `youtube-dl` (which in turn requires python3)
- ubuntu installation command: `sudo apt-get install ffmpeg libopus0 opus-tools libopus-dev libsodium-dev -y`
3. Make sure your python is version 3+ with `python --version`
- if it's not, you can install python 3 and make it the default with: `sudo apt-get install python3.8 python-is-python3`
*You can use nadeko bash script [prerequisites installer](https://gitlab.com/Kwoth/nadeko-bash-installer/-/blob/v4/n-prereq.sh) as a reference*
##### Installation Instructions
@@ -92,19 +103,6 @@ Open Terminal (if you're on an installation with a window manager) and navigate
##### Release Update Instructions
###### Prerequisites
1. Nadeko requires redis to function
- ubuntu installation command: `sudo apt-get install redis-server`
2. Playing music requires `ffmpeg`, `libopus`, `libsodium` and `youtube-dl` (which in turn requires python3)
- ubuntu installation command: `sudo apt-get install ffmpeg libopus0 opus-tools libopus-dev libsodium-dev -y`
3. Make sure your python is version 3+ with `python --version`
- if it's not, you can install python 3 and make it the default with: `sudo apt-get install python3.8 python-is-python3`
*You can use nadeko bash script [prerequisites installer](https://gitlab.com/Kwoth/nadeko-bash-installer/-/blob/v4/n-prereq.sh) as a reference*
###### Installation
1. Stop the bot
2. Download the latest release from <https://gitlab.com/Kwoth/nadekobot/-/releases>
- Look for the file called "x.x.x-linux-x64-build.tar" (where `X.X.X` is a version, for example 3.0.4) and download it

View File

@@ -63,9 +63,9 @@ You can still install them manually:
- [ffmpeg-32bit] | [ffmpeg-64bit] - Download the **appropriate version** for your system (32 bit if you're running a 32 bit OS, or 64 if you're running a 64bit OS). Unzip it, and move `ffmpeg.exe` to a path that's in your PATH environment variable. If you don't know what that is, then just move the `ffmpeg.exe` file to NadekoBot/system
- [youtube-dl] - Click to download the file. Then put `youtube-dl.exe` in a path that's in your PATH environment variable. If you don't know what that is, then just move the `youtube-dl.exe` file to NadekoBot/system
### Windows From Source
## **⚠ IF YOU ARE FOLLOWING THE GUIDE ABOVE, IGNORE THIS SECTION ⚠**
⚠ IF YOU ARE FOLLOWING THE GUIDE ABOVE, IGNORE THIS SECTION ⚠
### Windows From Source
##### Prerequisites

View File

@@ -77,8 +77,8 @@ Say you want to only enable NSFW commands for a specific role, just do the follo
If you don't want server or global Expressions, just block the module that controls their usage:
1. `.sm Expressions disable`
- Disables the ActualCustomReactions module from being used
1. `.sm ActualExpressions disable`
- Disables the ActualExpression module from being used
**Note**: The `Expressions` module controls the usage of Expressions. The `Expressions` module controls commands related to Expressions (such as `.acr`, `.lcr`, `.crca`, etc).

View File

@@ -92,4 +92,3 @@ nav:
- medusa/snek-lifecycle.md
- Contribution Guide: contribution-guide.md
- Donate: donate.md
- License: license.md

View File

@@ -0,0 +1,62 @@
using System.Threading.Channels;
namespace NadekoBot.Common;
public sealed class QueueRunner
{
private readonly Channel<Func<Task>> _channel;
private readonly int _delayMs;
public QueueRunner(int delayMs = 0, int maxCapacity = -1)
{
if (delayMs < 0)
throw new ArgumentOutOfRangeException(nameof(delayMs));
_delayMs = delayMs;
_channel = maxCapacity switch
{
0 or < -1 => throw new ArgumentOutOfRangeException(nameof(maxCapacity)),
-1 => Channel.CreateUnbounded<Func<Task>>(new UnboundedChannelOptions()
{
SingleReader = true,
SingleWriter = false,
AllowSynchronousContinuations = true,
}),
_ => Channel.CreateBounded<Func<Task>>(new BoundedChannelOptions(maxCapacity)
{
Capacity = maxCapacity,
FullMode = BoundedChannelFullMode.DropOldest,
SingleReader = true,
SingleWriter = false,
AllowSynchronousContinuations = true
})
};
}
public async Task RunAsync(CancellationToken cancel = default)
{
while (true)
{
var func = await _channel.Reader.ReadAsync(cancel);
try
{
await func();
}
catch (Exception ex)
{
Log.Warning(ex, "Exception executing a staggered func: {ErrorMessage}", ex.Message);
}
finally
{
if (_delayMs != 0)
{
await Task.Delay(_delayMs, cancel);
}
}
}
}
public ValueTask Enqueue(Func<Task> action)
=> _channel.Writer.WriteAsync(action);
}

View File

@@ -31,38 +31,38 @@ public sealed class CommandTypeReader : NadekoTypeReader<CommandInfo>
}
}
public sealed class CommandOrCrTypeReader : NadekoTypeReader<CommandOrCrInfo>
public sealed class CommandOrExprTypeReader : NadekoTypeReader<CommandOrExprInfo>
{
private readonly CommandService _cmds;
private readonly CommandHandler _commandHandler;
private readonly NadekoExpressionsService _exprs;
public CommandOrCrTypeReader(CommandService cmds, NadekoExpressionsService exprs, CommandHandler commandHandler)
public CommandOrExprTypeReader(CommandService cmds, NadekoExpressionsService exprs, CommandHandler commandHandler)
{
_cmds = cmds;
_exprs = exprs;
_commandHandler = commandHandler;
}
public override async ValueTask<TypeReaderResult<CommandOrCrInfo>> ReadAsync(ICommandContext ctx, string input)
public override async ValueTask<TypeReaderResult<CommandOrExprInfo>> ReadAsync(ICommandContext ctx, string input)
{
input = input.ToUpperInvariant();
if (_exprs.ExpressionExists(ctx.Guild?.Id, input))
return TypeReaderResult.FromSuccess(new CommandOrCrInfo(input, CommandOrCrInfo.Type.Custom));
if (_exprs.ExpressionExists(ctx.Guild?.Id, input) || _exprs.ExpressionExists(null, input))
return TypeReaderResult.FromSuccess(new CommandOrExprInfo(input, CommandOrExprInfo.Type.Custom));
var cmd = await new CommandTypeReader(_commandHandler, _cmds).ReadAsync(ctx, input);
if (cmd.IsSuccess)
{
return TypeReaderResult.FromSuccess(new CommandOrCrInfo(((CommandInfo)cmd.Values.First().Value).Name,
CommandOrCrInfo.Type.Normal));
return TypeReaderResult.FromSuccess(new CommandOrExprInfo(((CommandInfo)cmd.Values.First().Value).Name,
CommandOrExprInfo.Type.Normal));
}
return TypeReaderResult.FromError<CommandOrCrInfo>(CommandError.ParseFailed, "No such command or cr found.");
return TypeReaderResult.FromError<CommandOrExprInfo>(CommandError.ParseFailed, "No such command or expression found.");
}
}
public class CommandOrCrInfo
public class CommandOrExprInfo
{
public enum Type
{
@@ -76,7 +76,7 @@ public class CommandOrCrInfo
public bool IsCustom
=> CmdType == Type.Custom;
public CommandOrCrInfo(string input, Type type)
public CommandOrExprInfo(string input, Type type)
{
Name = input;
CmdType = type;

View File

@@ -14,7 +14,7 @@ public partial class Administration
[Cmd]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
public async partial Task DiscordPermOverride(CommandOrCrInfo cmd, params GuildPerm[] perms)
public async partial Task DiscordPermOverride(CommandOrExprInfo cmd, params GuildPerm[] perms)
{
if (perms is null || perms.Length == 0)
{

View File

@@ -189,7 +189,7 @@ public sealed class NadekoExpressionsService : IExecOnMessage, IReadyExecutor
continue;
}
// if CA is disabled, and CR has AllowTarget, then the
// if CA is disabled, and expr has AllowTarget, then the
// content has to start with the trigger followed by a space
if (expr.AllowTarget
&& content.StartsWith(trigger, StringComparison.OrdinalIgnoreCase)

View File

@@ -126,13 +126,15 @@ public partial class Gambling : GamblingModule<GamblingService>
if (_cache.AddTimelyClaim(ctx.User.Id, period) is { } rem)
{
await ReplyErrorLocalizedAsync(strs.timely_already_claimed(rem.ToString(@"dd\d\ hh\h\ mm\m\ ss\s")));
var now = DateTime.UtcNow;
var relativeTag = TimestampTag.FromDateTime(now.Add(rem), TimestampTagStyles.Relative);
await ReplyErrorLocalizedAsync(strs.timely_already_claimed(relativeTag));
return;
}
var result = await _ps.TryGetFeatureLimitAsync(_timelyKey, ctx.User.Id, 0);
val = (int)(val * (1 + (result.Quota * 0.01f)));
val = (int)(val * (1 + (result.Quota! * 0.01f)));
await _cs.AddAsync(ctx.User.Id, val, new("timely", "claim"));

View File

@@ -145,7 +145,7 @@ public partial class Help : NadekoModule<HelpService>
return "❓";
case "administration":
return "🛠️";
case "customreactions":
case "expressions":
return "🗣️";
case "searches":
return "🔍";
@@ -402,7 +402,7 @@ public partial class Help : NadekoModule<HelpService>
ContentType = "application/json",
ContentBody = uploadData,
// either use a path provided in the argument or the default one for public nadeko, other/cmds.json
Key = $"cmds/v4/{StatsService.BOT_VERSION}.json",
Key = $"cmds/{StatsService.BOT_VERSION}.json",
CannedACL = S3CannedACL.PublicRead
});
}
@@ -414,7 +414,7 @@ public partial class Help : NadekoModule<HelpService>
using var oldVersionObject = await dlClient.GetObjectAsync(new()
{
BucketName = "nadeko-pictures",
Key = "cmds/v4/versions.json"
Key = "cmds/versions.json"
});
await using var ms = new MemoryStream();
@@ -445,7 +445,7 @@ public partial class Help : NadekoModule<HelpService>
ContentType = "application/json",
ContentBody = versionListString,
// either use a path provided in the argument or the default one for public nadeko, other/cmds.json
Key = "cmds/v4/versions.json",
Key = "cmds/versions.json",
CannedACL = S3CannedACL.PublicRead
});
}

View File

@@ -79,7 +79,7 @@ public partial class Permissions
[Cmd]
[RequireContext(ContextType.Guild)]
[Priority(1)]
public partial Task CmdCooldown(CommandOrCrInfo command, int secs)
public partial Task CmdCooldown(CommandOrExprInfo command, int secs)
=> CmdCooldownInternal(command.Name, secs);
[Cmd]

View File

@@ -60,7 +60,7 @@ public partial class Permissions
[Cmd]
[OwnerOnly]
public async partial Task GlobalCommand(CommandOrCrInfo cmd)
public async partial Task GlobalCommand(CommandOrExprInfo cmd)
{
var commandName = cmd.Name.ToLowerInvariant();
var added = _service.ToggleCommand(commandName);

View File

@@ -204,7 +204,7 @@ public partial class Permissions : NadekoModule<PermissionService>
[Cmd]
[RequireContext(ContextType.Guild)]
public async partial Task SrvrCmd(CommandOrCrInfo command, PermissionAction action)
public async partial Task SrvrCmd(CommandOrExprInfo command, PermissionAction action)
{
await _service.AddPermissions(ctx.Guild.Id,
new Permissionv2
@@ -245,7 +245,7 @@ public partial class Permissions : NadekoModule<PermissionService>
[Cmd]
[RequireContext(ContextType.Guild)]
public async partial Task UsrCmd(CommandOrCrInfo command, PermissionAction action, [Leftover] IGuildUser user)
public async partial Task UsrCmd(CommandOrExprInfo command, PermissionAction action, [Leftover] IGuildUser user)
{
await _service.AddPermissions(ctx.Guild.Id,
new Permissionv2
@@ -302,7 +302,7 @@ public partial class Permissions : NadekoModule<PermissionService>
[Cmd]
[RequireContext(ContextType.Guild)]
public async partial Task RoleCmd(CommandOrCrInfo command, PermissionAction action, [Leftover] IRole role)
public async partial Task RoleCmd(CommandOrExprInfo command, PermissionAction action, [Leftover] IRole role)
{
if (role == role.Guild.EveryoneRole)
return;
@@ -366,7 +366,7 @@ public partial class Permissions : NadekoModule<PermissionService>
[Cmd]
[RequireContext(ContextType.Guild)]
public async partial Task ChnlCmd(CommandOrCrInfo command, PermissionAction action, [Leftover] ITextChannel chnl)
public async partial Task ChnlCmd(CommandOrExprInfo command, PermissionAction action, [Leftover] ITextChannel chnl)
{
await _service.AddPermissions(ctx.Guild.Id,
new Permissionv2

View File

@@ -16,11 +16,11 @@ public class CmcQuote
[JsonPropertyName("volume_24h")]
public double Volume24h { get; set; }
[JsonPropertyName("volume_change_24h")]
public double VolumeChange24h { get; set; }
[JsonPropertyName("percent_change_1h")]
public double PercentChange1h { get; set; }
// [JsonPropertyName("volume_change_24h")]
// public double VolumeChange24h { get; set; }
//
// [JsonPropertyName("percent_change_1h")]
// public double PercentChange1h { get; set; }
[JsonPropertyName("percent_change_24h")]
public double PercentChange24h { get; set; }
@@ -33,12 +33,6 @@ public class CmcQuote
[JsonPropertyName("market_cap_dominance")]
public double MarketCapDominance { get; set; }
[JsonPropertyName("fully_diluted_market_cap")]
public double FullyDilutedMarketCap { get; set; }
[JsonPropertyName("last_updated")]
public DateTime LastUpdated { get; set; }
}
public class CmcResponseData
@@ -58,9 +52,6 @@ public class CmcResponseData
[JsonPropertyName("cmc_rank")]
public int CmcRank { get; set; }
[JsonPropertyName("num_market_pairs")]
public int NumMarketPairs { get; set; }
[JsonPropertyName("circulating_supply")]
public double? CirculatingSupply { get; set; }
@@ -70,15 +61,6 @@ public class CmcResponseData
[JsonPropertyName("max_supply")]
public double? MaxSupply { get; set; }
[JsonPropertyName("last_updated")]
public DateTime LastUpdated { get; set; }
[JsonPropertyName("date_added")]
public DateTime DateAdded { get; set; }
[JsonPropertyName("tags")]
public List<string> Tags { get; set; }
[JsonPropertyName("quote")]
public Dictionary<string, CmcQuote> Quote { get; set; }
}

View File

@@ -7,7 +7,7 @@ namespace NadekoBot.Modules.Utility.Patronage;
public partial class PatronConfigData : ICloneable<PatronConfigData>
{
[Comment("DO NOT CHANGE")]
public int Version { get; set; } = 1;
public int Version { get; set; } = 2;
[Comment("Whether the patronage feature is enabled")]
public bool IsEnabled { get; set; }

View File

@@ -18,5 +18,19 @@ public class PatronageConfig : ConfigServiceBase<PatronConfigData>
x => x.IsEnabled,
bool.TryParse,
ConfigPrinters.ToString);
Migrate();
}
private void Migrate()
{
ModifyConfig(c =>
{
if (c.Version == 1)
{
c.Version = 2;
c.IsEnabled = false;
}
});
}
}

View File

@@ -3,6 +3,7 @@ using LinqToDB.EntityFrameworkCore;
using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Db.Models;
using OneOf;
using OneOf.Types;
using StackExchange.Redis;
using CommandInfo = Discord.Commands.CommandInfo;
@@ -500,8 +501,8 @@ public sealed class PatronageService
if (!confData.IsEnabled)
return default;
if (_creds.IsOwner(userId))
return default;
// if (_creds.IsOwner(userId))
// return default;
// get user tier
var patron = await GetPatronAsync(userId);
@@ -558,7 +559,9 @@ public sealed class PatronageService
data.TryGetValue(QuotaPer.PerMonth, out var monthly) ? monthly : null
);
return quotaCheckResult.Match(_ => default, x => x);
return quotaCheckResult.Match<OneOf<Success, InsufficientTier, QuotaLimit>>(
_ => new Success(),
x => x);
}
private bool TryGetTierDataOrLower<T>(
@@ -697,7 +700,7 @@ public sealed class PatronageService
return new()
{
Name = key.PrettyName,
Quota = default,
Quota = defaultValue,
IsPatronLimit = false
};

View File

@@ -1,17 +1,20 @@
#nullable disable
using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Db;
using NadekoBot.Modules.Utility.Common;
using NadekoBot.Modules.Utility.Common.Exceptions;
using NadekoBot.Services.Database.Models;
using System.Diagnostics;
using System.Net;
namespace NadekoBot.Modules.Utility.Services;
public class StreamRoleService : INService
public class StreamRoleService : IReadyExecutor, INService
{
private readonly DbService _db;
private readonly DiscordSocketClient _client;
private readonly ConcurrentDictionary<ulong, StreamRoleSettings> _guildSettings;
private readonly QueueRunner _queueRunner;
public StreamRoleService(DiscordSocketClient client, DbService db, Bot bot)
{
@@ -22,33 +25,35 @@ public class StreamRoleService : INService
.Where(x => x.Value is { Enabled: true })
.ToConcurrent();
_client.GuildMemberUpdated += Client_GuildMemberUpdated;
_client.PresenceUpdated += OnPresenceUpdate;
_queueRunner = new QueueRunner();
}
private Task OnPresenceUpdate(SocketUser user, SocketPresence oldPresence, SocketPresence newPresence)
{
_ = Task.Run(async () =>
{
try
if (oldPresence.Activities.Count != newPresence.Activities.Count)
{
await client.Guilds.Select(g => RescanUsers(g)).WhenAll();
}
catch
{
// ignored
}
});
}
var guildUsers = _client.Guilds
.Select(x => x.GetUser(user.Id));
private Task Client_GuildMemberUpdated(Cacheable<SocketGuildUser, ulong> cacheable, SocketGuildUser after)
foreach (var guildUser in guildUsers)
{
_ = Task.Run(async () =>
{
//if user wasn't streaming or didn't have a game status at all
if (_guildSettings.TryGetValue(after.Guild.Id, out var setting))
await RescanUser(after, setting);
if (_guildSettings.TryGetValue(guildUser.Guild.Id, out var s))
await RescanUser(guildUser, s);
}
}
});
return Task.CompletedTask;
}
public Task OnReadyAsync()
=> Task.WhenAll(_client.Guilds.Select(RescanUsers).WhenAll(), _queueRunner.RunAsync());
/// <summary>
/// Adds or removes a user from a blacklist or a whitelist in the specified guild.
/// </summary>
@@ -135,7 +140,7 @@ public class StreamRoleService : INService
streamRoleSettings.Keyword = keyword;
UpdateCache(guild.Id, streamRoleSettings);
uow.SaveChanges();
await uow.SaveChangesAsync();
}
await RescanUsers(guild);
@@ -191,8 +196,7 @@ public class StreamRoleService : INService
foreach (var usr in await fromRole.GetMembersAsync())
{
if (usr is { } x)
await RescanUser(x, setting, addRole);
await RescanUser(usr, setting, addRole);
}
}
@@ -216,7 +220,10 @@ public class StreamRoleService : INService
await RescanUsers(guild);
}
private async Task RescanUser(IGuildUser user, StreamRoleSettings setting, IRole addRole = null)
private async ValueTask RescanUser(IGuildUser user, StreamRoleSettings setting, IRole addRole = null)
=> await _queueRunner.Enqueue(() => RescanUserInternal(user, setting, addRole));
private async Task RescanUserInternal(IGuildUser user, StreamRoleSettings setting, IRole addRole = null)
{
if (user.IsBot)
return;
@@ -231,6 +238,8 @@ public class StreamRoleService : INService
&& setting.Enabled
&& setting.Blacklist.All(x => x.UserId != user.Id)
&& user.RoleIds.Contains(setting.FromRoleId))
{
await _queueRunner.Enqueue(async () =>
{
try
{
@@ -261,30 +270,47 @@ public class StreamRoleService : INService
{
Log.Warning(ex, "Failed adding stream role");
}
});
}
else
{
//check if user is in the addrole
if (user.RoleIds.Contains(setting.AddRoleId))
{
await _queueRunner.Enqueue(async () =>
{
try
{
addRole ??= user.Guild.GetRole(setting.AddRoleId);
if (addRole is null)
throw new StreamRoleNotFoundException();
{
await StopStreamRole(user.Guild);
Log.Warning(
"Addrole doesn't exist in {GuildId} server. Forcibly disabling stream role feature",
user.Guild.Id);
return;
}
// need to check again in case queuer is taking too long to execute
if (user.RoleIds.Contains(setting.AddRoleId))
{
await user.RemoveRoleAsync(addRole);
}
Log.Information("Removed stream role from the user {User} in {Server} server",
user.ToString(),
user.Guild.ToString());
}
catch (HttpException ex) when (ex.HttpCode == HttpStatusCode.Forbidden)
catch (HttpException ex)
{
if (ex.HttpCode == HttpStatusCode.Forbidden)
{
await StopStreamRole(user.Guild);
Log.Warning(ex, "Error removing stream role(s). Forcibly disabling stream role feature");
throw new StreamRolePermissionException();
}
}
});
}
}
}

View File

@@ -103,8 +103,10 @@ public sealed class BotCredsProvider : IBotCredsProvider
if (string.IsNullOrWhiteSpace(_creds.RedisOptions))
_creds.RedisOptions = "127.0.0.1,syncTimeout=3000";
if (string.IsNullOrWhiteSpace(_creds.CoinmarketcapApiKey))
_creds.CoinmarketcapApiKey = "e79ec505-0913-439d-ae07-069e296a6079";
// replace the old generated key with the shared key
if (string.IsNullOrWhiteSpace(_creds.CoinmarketcapApiKey)
|| _creds.CoinmarketcapApiKey.StartsWith("e79ec505-0913"))
_creds.CoinmarketcapApiKey = "3077537c-7dfb-4d97-9a60-56fc9a9f5035";
_creds.TotalShards = _totalShards ?? _creds.TotalShards;
}

View File

@@ -7,7 +7,7 @@ namespace NadekoBot.Services;
public sealed class StatsService : IStatsService, IReadyExecutor, INService
{
public const string BOT_VERSION = "4.2.2";
public const string BOT_VERSION = "4.2.6";
public string Author
=> "Kwoth#2452";

View File

@@ -221,7 +221,7 @@ public static class Extensions
public static void Lap(this Stopwatch sw, string checkpoint)
{
Log.Information("Checkpoint {CheckPoint}: {Time}", checkpoint, sw.Elapsed.TotalMilliseconds);
Log.Information("Checkpoint {CheckPoint}: {Time}ms", checkpoint, sw.Elapsed.TotalMilliseconds);
sw.Restart();
}
}

View File

@@ -1,7 +1,7 @@
# DO NOT CHANGE
version: 1
version: 2
# Whether the patronage feature is enabled
isEnabled: true
isEnabled: false
# List of patron only features and relevant quota data
quotas:
# Dictionary of feature names with their respective limits. Set to null for unlimited

View File

@@ -878,7 +878,7 @@
"autodc_enable": "I will disconnect from the voice channel when there are no more tracks to play.",
"autodc_disable": "I will no longer disconnect from the voice channel when there are no more tracks to play.",
"timely_none": "Bot owner didn't specify a timely reward.",
"timely_already_claimed": "You've already claimed your timely reward. You can get it again in {0}.",
"timely_already_claimed": "You've already claimed your timely reward. You can get it again {0}.",
"timely": "You've claimed your {0}. You can claim again in {1}h",
"timely_set": "Users will be able to claim {0} every {1}h",
"timely_set_none": "Users will not be able to claim any timely currency.",