wip reimplementation of the voicexp
This commit is contained in:
parent
5d9326b65e
commit
dbc312dd9d
2 changed files with 61 additions and 131 deletions
src/EllieBot
|
@ -154,7 +154,7 @@
|
||||||
<PropertyGroup Condition=" '$(Configuration)' == 'GlobalEllie' ">
|
<PropertyGroup Condition=" '$(Configuration)' == 'GlobalEllie' ">
|
||||||
<!-- Define trace doesn't seem to affect the build at all so I had to remove $(DefineConstants)-->
|
<!-- Define trace doesn't seem to affect the build at all so I had to remove $(DefineConstants)-->
|
||||||
<DefineTrace>false</DefineTrace>
|
<DefineTrace>false</DefineTrace>
|
||||||
<DefineConstants>GLOBAL_NADEKO</DefineConstants>
|
<DefineConstants>GLOBAL_ELLIE</DefineConstants>
|
||||||
<NoWarn>$(NoWarn);CS1573;CS1591</NoWarn>
|
<NoWarn>$(NoWarn);CS1573;CS1591</NoWarn>
|
||||||
<Optimize>true</Optimize>
|
<Optimize>true</Optimize>
|
||||||
<DebugType>portable</DebugType>
|
<DebugType>portable</DebugType>
|
||||||
|
|
|
@ -140,6 +140,22 @@ public class XpService : IEService, IReadyExecutor, IExecNoCommand
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
async Task VoiceUpdateTimer()
|
||||||
|
{
|
||||||
|
using var timer = new PeriodicTimer(TimeSpan.FromMinutes(1));
|
||||||
|
while (await timer.WaitForNextTickAsync())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await UpdateVoiceXp();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error(ex, "Error updating voice xp");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async Task UpdateTimer()
|
async Task UpdateTimer()
|
||||||
{
|
{
|
||||||
// todo a bigger loop that runs once every XpTimer
|
// todo a bigger loop that runs once every XpTimer
|
||||||
|
@ -164,17 +180,46 @@ public class XpService : IEService, IReadyExecutor, IExecNoCommand
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly ConcurrentHashSet<IGuildUser> _usersBatch = new();
|
private readonly ConcurrentHashSet<IGuildUser> _usersBatch = new();
|
||||||
|
|
||||||
|
private readonly ConcurrentHashSet<IGuildUser> _voiceXPBatch = new();
|
||||||
|
|
||||||
|
private async Task UpdateVoiceXp()
|
||||||
|
{
|
||||||
|
var xpAmount = (int)_xpConfig.Data.VoiceXpPerMinute;
|
||||||
|
var oldBatch = _voiceXPBatch.ToArray();
|
||||||
|
_voiceXPBatch.Clear();
|
||||||
|
var validUsers = new HashSet<IGuildUser>();
|
||||||
|
|
||||||
|
var guilds = _client.Guilds;
|
||||||
|
|
||||||
|
foreach (var g in guilds)
|
||||||
|
foreach (var vc in g.VoiceChannels)
|
||||||
|
foreach (var u in vc.ConnectedUsers)
|
||||||
|
if (!u.IsMuted && !u.IsDeafened
|
||||||
|
&& vc.ConnectedUsers.Count(x => !x.IsBot) > 1)
|
||||||
|
{
|
||||||
|
if (oldBatch.Contains(u))
|
||||||
|
validUsers.Add(u);
|
||||||
|
|
||||||
|
_voiceXPBatch.Add(u);
|
||||||
|
}
|
||||||
|
|
||||||
|
await UpdateXpInternalAsync(validUsers.DistinctBy(x => x.Id).ToArray(), xpAmount);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task UpdateXp()
|
private async Task UpdateXp()
|
||||||
{
|
{
|
||||||
var xpAmount = _xpConfig.Data.XpPerMessage;
|
var xpAmount = _xpConfig.Data.XpPerMessage;
|
||||||
var currentBatch = _usersBatch.ToArray();
|
var currentBatch = _usersBatch.ToArray();
|
||||||
_usersBatch.Clear();
|
_usersBatch.Clear();
|
||||||
|
|
||||||
|
await UpdateXpInternalAsync(currentBatch, xpAmount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task UpdateXpInternalAsync(IGuildUser[] currentBatch, int xpAmount)
|
||||||
|
{
|
||||||
if (currentBatch.Length == 0)
|
if (currentBatch.Length == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var ids = currentBatch.Select(x => x.Id).ToArray();
|
|
||||||
|
|
||||||
await using var ctx = _db.GetDbContext();
|
await using var ctx = _db.GetDbContext();
|
||||||
await using var lctx = ctx.CreateLinqToDBConnection();
|
await using var lctx = ctx.CreateLinqToDBConnection();
|
||||||
|
|
||||||
|
@ -492,7 +537,7 @@ public class XpService : IEService, IReadyExecutor, IExecNoCommand
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
uow.SaveChanges();
|
await uow.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IReadOnlyCollection<UserXpStats>> GetGuildUserXps(ulong guildId, int page)
|
public async Task<IReadOnlyCollection<UserXpStats>> GetGuildUserXps(ulong guildId, int page)
|
||||||
|
@ -552,29 +597,16 @@ public class XpService : IEService, IReadyExecutor, IExecNoCommand
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task Client_OnUserVoiceStateUpdated(SocketUser socketUser, SocketVoiceState before, SocketVoiceState after)
|
private async Task Client_OnUserVoiceStateUpdated(SocketUser socketUser, SocketVoiceState before,
|
||||||
|
SocketVoiceState after)
|
||||||
{
|
{
|
||||||
if (socketUser is not SocketGuildUser user || user.IsBot)
|
if (socketUser is not SocketGuildUser user || user.IsBot)
|
||||||
return Task.CompletedTask;
|
return;
|
||||||
|
|
||||||
_ = Task.Run(async () =>
|
if (after.VoiceChannel is not null)
|
||||||
{
|
{
|
||||||
if (before.VoiceChannel is not null)
|
await ScanChannelForVoiceXp(after.VoiceChannel);
|
||||||
await ScanChannelForVoiceXp(before.VoiceChannel);
|
}
|
||||||
|
|
||||||
if (after.VoiceChannel is not null && after.VoiceChannel != before.VoiceChannel)
|
|
||||||
{
|
|
||||||
await ScanChannelForVoiceXp(after.VoiceChannel);
|
|
||||||
}
|
|
||||||
else if (after.VoiceChannel is null && before.VoiceChannel is not null)
|
|
||||||
{
|
|
||||||
// In this case, the user left the channel and the previous for loops didn't catch
|
|
||||||
// it because it wasn't in any new channel. So we need to get rid of it.
|
|
||||||
await UserLeftVoiceChannel(user, before.VoiceChannel);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ScanChannelForVoiceXp(SocketVoiceChannel channel)
|
private async Task ScanChannelForVoiceXp(SocketVoiceChannel channel)
|
||||||
|
@ -582,27 +614,13 @@ public class XpService : IEService, IReadyExecutor, IExecNoCommand
|
||||||
if (ShouldTrackVoiceChannel(channel))
|
if (ShouldTrackVoiceChannel(channel))
|
||||||
{
|
{
|
||||||
foreach (var user in channel.ConnectedUsers)
|
foreach (var user in channel.ConnectedUsers)
|
||||||
await ScanUserForVoiceXp(user, channel);
|
{
|
||||||
}
|
if (UserParticipatingInVoiceChannel(user) && ShouldTrackXp(user, channel))
|
||||||
else
|
await UserJoinedVoiceChannel(user);
|
||||||
{
|
}
|
||||||
foreach (var user in channel.ConnectedUsers)
|
|
||||||
await UserLeftVoiceChannel(user, channel);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Assumes that the channel itself is valid and adding xp.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="user"></param>
|
|
||||||
/// <param name="channel"></param>
|
|
||||||
private async Task ScanUserForVoiceXp(SocketGuildUser user, SocketVoiceChannel channel)
|
|
||||||
{
|
|
||||||
if (UserParticipatingInVoiceChannel(user) && ShouldTrackXp(user, channel))
|
|
||||||
await UserJoinedVoiceChannel(user);
|
|
||||||
else
|
|
||||||
await UserLeftVoiceChannel(user, channel);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool ShouldTrackVoiceChannel(SocketVoiceChannel channel)
|
private bool ShouldTrackVoiceChannel(SocketVoiceChannel channel)
|
||||||
=> channel.ConnectedUsers.Where(UserParticipatingInVoiceChannel).Take(2).Count() >= 2;
|
=> channel.ConnectedUsers.Where(UserParticipatingInVoiceChannel).Take(2).Count() >= 2;
|
||||||
|
@ -619,84 +637,10 @@ public class XpService : IEService, IReadyExecutor, IExecNoCommand
|
||||||
|
|
||||||
await _c.AddAsync(GetVoiceXpKey(user.Id),
|
await _c.AddAsync(GetVoiceXpKey(user.Id),
|
||||||
value,
|
value,
|
||||||
TimeSpan.FromMinutes(_xpConfig.Data.VoiceMaxMinutes),
|
TimeSpan.FromMinutes(1),
|
||||||
overwrite: false);
|
overwrite: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// private void UserJoinedVoiceChannel(SocketGuildUser user)
|
|
||||||
// {
|
|
||||||
// var key = $"{_creds.RedisKey()}_user_xp_vc_join_{user.Id}";
|
|
||||||
// var value = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
|
||||||
//
|
|
||||||
// _cache.Redis.GetDatabase()
|
|
||||||
// .StringSet(key,
|
|
||||||
// value,
|
|
||||||
// TimeSpan.FromMinutes(_xpConfig.Data.VoiceMaxMinutes),
|
|
||||||
// when: When.NotExists);
|
|
||||||
// }
|
|
||||||
|
|
||||||
private async Task UserLeftVoiceChannel(SocketGuildUser user, SocketVoiceChannel channel)
|
|
||||||
{
|
|
||||||
var key = GetVoiceXpKey(user.Id);
|
|
||||||
var result = await _c.GetAsync(key);
|
|
||||||
if (!await _c.RemoveAsync(key))
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Allow for if this function gets called multiple times when a user leaves a channel.
|
|
||||||
if (!result.TryGetValue(out var unixTime))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var dateStart = DateTimeOffset.FromUnixTimeSeconds(unixTime);
|
|
||||||
var dateEnd = DateTimeOffset.UtcNow;
|
|
||||||
var minutes = (dateEnd - dateStart).TotalMinutes;
|
|
||||||
var xp = _xpConfig.Data.VoiceXpPerMinute * minutes;
|
|
||||||
var actualXp = (int)Math.Floor(xp);
|
|
||||||
|
|
||||||
if (actualXp > 0)
|
|
||||||
{
|
|
||||||
Log.Information("Adding {Amount} voice xp to {User}", actualXp, user.ToString());
|
|
||||||
await _xpGainQueue.Writer.WriteAsync(new()
|
|
||||||
{
|
|
||||||
Guild = channel.Guild,
|
|
||||||
User = user,
|
|
||||||
XpAmount = actualXp,
|
|
||||||
Channel = channel
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* private void UserLeftVoiceChannel(SocketGuildUser user, SocketVoiceChannel channel)
|
|
||||||
{
|
|
||||||
var key = $"{_creds.RedisKey()}_user_xp_vc_join_{user.Id}";
|
|
||||||
var value = _cache.Redis.GetDatabase().StringGet(key);
|
|
||||||
_cache.Redis.GetDatabase().KeyDelete(key);
|
|
||||||
|
|
||||||
// Allow for if this function gets called multiple times when a user leaves a channel.
|
|
||||||
if (value.IsNull)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!value.TryParse(out long startUnixTime))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var dateStart = DateTimeOffset.FromUnixTimeSeconds(startUnixTime);
|
|
||||||
var dateEnd = DateTimeOffset.UtcNow;
|
|
||||||
var minutes = (dateEnd - dateStart).TotalMinutes;
|
|
||||||
var xp = _xpConfig.Data.VoiceXpPerMinute * minutes;
|
|
||||||
var actualXp = (int)Math.Floor(xp);
|
|
||||||
|
|
||||||
if (actualXp > 0)
|
|
||||||
{
|
|
||||||
_addMessageXp.Enqueue(new()
|
|
||||||
{
|
|
||||||
Guild = channel.Guild,
|
|
||||||
User = user,
|
|
||||||
XpAmount = actualXp
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
private bool ShouldTrackXp(SocketGuildUser user, IMessageChannel channel)
|
private bool ShouldTrackXp(SocketGuildUser user, IMessageChannel channel)
|
||||||
{
|
{
|
||||||
var channelId = channel.Id;
|
var channelId = channel.Id;
|
||||||
|
@ -744,20 +688,6 @@ public class XpService : IEService, IReadyExecutor, IExecNoCommand
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
// public void AddXpDirectly(IGuildUser user, IMessageChannel channel, int amount)
|
|
||||||
// {
|
|
||||||
// if (amount <= 0)
|
|
||||||
// throw new ArgumentOutOfRangeException(nameof(amount));
|
|
||||||
//
|
|
||||||
// _xpGainQueue.Writer.WriteAsync(new()
|
|
||||||
// {
|
|
||||||
// Guild = user.Guild,
|
|
||||||
// Channel = channel,
|
|
||||||
// User = user,
|
|
||||||
// XpAmount = amount
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
public async Task<int> AddXpToUsersAsync(ulong guildId, long amount, params ulong[] userIds)
|
public async Task<int> AddXpToUsersAsync(ulong guildId, long amount, params ulong[] userIds)
|
||||||
{
|
{
|
||||||
await using var ctx = _db.GetDbContext();
|
await using var ctx = _db.GetDbContext();
|
||||||
|
|
Loading…
Add table
Reference in a new issue