Compare commits

...

30 Commits
4.0.4 ... 4.0.5

Author SHA1 Message Date
Kwoth
59cea6ee38 Version upped to 4.0.5, updated changelog 2022-03-21 16:00:05 +01:00
Kwoth
cbcfa77a34 Updated discord.net to 3.4.1 2022-03-21 15:57:42 +01:00
Kwoth
cfb202cc95 A few more improvements and cleanup for the wallet 2022-03-21 15:55:57 +01:00
Kwoth
b7d1fd1b47 Many IDisposable fixes. GlobalNadeko won't have file watchers for creds. Wallet simplified 2022-03-21 15:33:18 +01:00
Kwoth
4cf3bdb53a Removed unneeded httpclient for searchimages and some minor cleanup 2022-03-21 13:33:43 +01:00
Kwoth
aab5bc9744 Added long delay during graceful coordinator restart, and some logging 2022-03-21 13:25:48 +01:00
Kwoth
1f14c9066e Added several missing using statements 2022-03-21 13:20:26 +01:00
Kwoth
9ade3c9537 Added some custom settings to GlobalNadeko configuration 2022-03-21 13:02:55 +01:00
Kwoth
1dc393d2b1 - Moved update loop to a separate method.
- Added optimize flag to GlobalNadeko configuration.
- Added some packages which will be needed soon
2022-03-21 12:01:07 +01:00
Kwoth
798b66db9b Fixed playing rotate nullref 2022-03-21 11:07:42 +01:00
Kwoth
57e65e5515 Coordinator fix 2022-03-21 01:52:32 +01:00
Kwoth
86e728b753 Converted many raw sql queries to their linq2db equivalents 2022-03-20 19:31:04 +01:00
Kwoth
fd032d3e91 Fixed addbulkasync method 2022-03-20 12:19:12 +01:00
Kwoth
eab16865cd Added a missing savechanges 2022-03-20 12:07:06 +01:00
Kwoth
a34a86bbfa Rewrote several raw queries to linqtodb 2022-03-20 01:14:24 +01:00
Kwoth
a016b3546f Added some missing tables to the context, no functional change 2022-03-20 01:09:22 +01:00
Kwoth
e09435da37 Moved some ICurrencyService methods to extensions to simplify use 2022-03-19 21:19:22 +01:00
Kwoth
416f3d604c Added a missing using 2022-03-19 17:01:03 +01:00
Kwoth
d9f371f994 Added logging to coordinator, downgraded framework to net5, downgraded grpc packages to 2.41 2022-03-19 11:19:08 +01:00
Kwoth
c1c22d0477 Merge branch 'v4' of https://gitlab.com/kwoth/nadekobot into v4 2022-03-18 23:50:55 +01:00
Kwoth
339f13d31a Updated gitlab-ci and docker-guide 2022-03-18 23:49:35 +01:00
Kwoth
4190f07d9c Merge branch 'eDavid-v4-patch-48565' into 'v4'
python3 folder is up to 3.9 now

See merge request Kwoth/nadekobot!241
2022-03-18 18:14:04 +00:00
David
a9bdf36c53 python3 folder is up to 3.9 now 2022-03-18 17:31:27 +00:00
Kwoth
1913cd4309 Merge branch 'hokutochen-v4-patch-69894' into 'v4'
wremoved "and when queue is empty" from .qap description.

See merge request Kwoth/nadekobot!238
2022-03-15 09:30:51 +00:00
Hokuto Chen
624439f684 wremoved "and when queue is empty" from .qap description. 2022-03-15 09:30:51 +00:00
Kwoth
3e14dc22cb Merge branch 'hokutochen-v4-patch-80313' into 'v4'
incorrect bot version (we are on 4.0.4)?

See merge request Kwoth/nadekobot!236
2022-03-15 00:00:17 +00:00
Kwoth
f4178aeacd Merge branch 'ban=patch' into 'v4'
Fix ban dms sending to mod. Closes #340

Closes #340

See merge request Kwoth/nadekobot!237
2022-03-14 23:59:55 +00:00
Alan Beatty
aaf3c9cfe9 Fix ban dms sending to mod. Closes #340 2022-03-14 11:44:15 -05:00
Kwoth
f6ee012b15 Removed some references to .cr commands from command strings 2022-03-13 06:57:22 +01:00
Hokuto Chen
363ef42923 incorrect bot version (we are on 4.0.4)? 2022-03-12 20:55:03 +00:00
54 changed files with 822 additions and 721 deletions

View File

@@ -97,29 +97,29 @@ upload-windows-updater-release:
- aws --endpoint-url $AWS_SERVICE_URL s3api put-object --bucket "$AWS_BUCKET_NAME" --key "dl/bot/$INSTALLER_FILE_NAME" --acl public-read --body "$INSTALLER_OUTPUT_DIR/$INSTALLER_FILE_NAME" - aws --endpoint-url $AWS_SERVICE_URL s3api put-object --bucket "$AWS_BUCKET_NAME" --key "dl/bot/$INSTALLER_FILE_NAME" --acl public-read --body "$INSTALLER_OUTPUT_DIR/$INSTALLER_FILE_NAME"
- aws --endpoint-url $AWS_SERVICE_URL s3api put-object --bucket "$AWS_BUCKET_NAME" --key "dl/bot/releases-v3.json" --acl public-read --body "releases-v3.json" - aws --endpoint-url $AWS_SERVICE_URL s3api put-object --bucket "$AWS_BUCKET_NAME" --key "dl/bot/releases-v3.json" --acl public-read --body "releases-v3.json"
# docker-build: docker-build:
# # Use the official docker image. # Use the official docker image.
# image: docker:latest image: docker:latest
# stage: build stage: build
# services: services:
# - docker:dind - docker:dind
# before_script: before_script:
# - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
# # Default branch leaves tag empty (= latest tag) # Default branch leaves tag empty (= latest tag)
# # All other branches are tagged with the escaped branch name (commit ref slug) # All other branches are tagged with the escaped branch name (commit ref slug)
# script: script:
# - | - |
# if [[ "$CI_COMMIT_BRANCH" == "$CI_DEFAULT_BRANCH" ]]; then if [[ "$CI_COMMIT_BRANCH" == "$CI_DEFAULT_BRANCH" ]]; then
# tag="" tag=""
# echo "Running on default branch '$CI_DEFAULT_BRANCH': tag = 'latest'" echo "Running on default branch '$CI_DEFAULT_BRANCH': tag = 'latest'"
# else else
# tag=":$CI_COMMIT_REF_SLUG" tag=":$CI_COMMIT_REF_SLUG"
# echo "Running on branch '$CI_COMMIT_BRANCH': tag = $tag" echo "Running on branch '$CI_COMMIT_BRANCH': tag = $tag"
# fi fi
# - docker build --pull -t "$CI_REGISTRY_IMAGE${tag}" . - docker build --pull -t "$CI_REGISTRY_IMAGE${tag}" .
# - docker push "$CI_REGISTRY_IMAGE${tag}" - docker push "$CI_REGISTRY_IMAGE${tag}"
# # Run this job in a branch where a Dockerfile exists # Run this job in a branch where a Dockerfile exists
# rules: rules:
# - if: $CI_COMMIT_BRANCH - if: $CI_COMMIT_BRANCH
# exists: exists:
# - Dockerfile - Dockerfile

View File

@@ -7,6 +7,14 @@ Experimental changelog. Mostly based on [keepachangelog](https://keepachangelog.
- More cool stuff coming soon - More cool stuff coming soon
## [4.0.5] - 21.03.2022
### Fixes
- Fixed several bugs in the currency code
- Fixed some potential memory leaks
- Fixed some response strings
## [4.0.4] - 04.03.2022 ## [4.0.4] - 04.03.2022
### Fixed ### Fixed

View File

@@ -25,7 +25,7 @@ RUN set -xe; \
useradd -m nadeko; \ useradd -m nadeko; \
apt-get update; \ apt-get update; \
apt-get install -y libopus0 libsodium23 libsqlite3-0 curl ffmpeg python3 python3-pip sudo; \ apt-get install -y libopus0 libsodium23 libsqlite3-0 curl ffmpeg python3 python3-pip sudo; \
update-alternatives --install /usr/bin/python python /usr/bin/python3.7 1; \ update-alternatives --install /usr/bin/python python /usr/bin/python3.9 1; \
echo 'Defaults>nadeko env_keep+="ASPNETCORE_* DOTNET_* NadekoBot_* shard_id total_shards TZ"' > /etc/sudoers.d/nadeko; \ echo 'Defaults>nadeko env_keep+="ASPNETCORE_* DOTNET_* NadekoBot_* shard_id total_shards TZ"' > /etc/sudoers.d/nadeko; \
pip3 install --upgrade youtube-dl; \ pip3 install --upgrade youtube-dl; \
apt-get remove -y python3-pip; \ apt-get remove -y python3-pip; \
@@ -39,4 +39,4 @@ ENV total_shards=1
VOLUME [ "app/data" ] VOLUME [ "app/data" ]
ENTRYPOINT [ "/usr/local/sbin/docker-entrypoint.sh" ] ENTRYPOINT [ "/usr/local/sbin/docker-entrypoint.sh" ]
CMD dotnet NadekoBot.dll "$shard_id" "$total_shards" CMD dotnet NadekoBot.dll "$shard_id" "$total_shards"

View File

@@ -1,18 +1,25 @@
# Setting up NadekoBot with Docker # Setting up NadekoBot with Docker
# DO NOT USE YET - WORK IN PROGRESS # WORK IN PROGRESS
### Docker Compose ### Installation
1. Create a `/srv/nadeko` folder
- `mkdir -p /srv/nadeko`
2. Create a `docker-compose.yml`
- nano `docker-compose.yml`
- copy the following contents into it:
##### docker-compose.yml
```yml ```yml
version: "3.7" version: "3.7"
services: services:
nadeko: nadeko:
image: registry.gitlab.com/veovis/nadekobot:v3-docker image: registry.gitlab.com/kwoth/nadekobot:latest
depends_on: depends_on:
- redis - redis
environment: environment:
TZ: Europe/Paris TZ: Europe/Paris
#NadekoBot_RedisOptions: redis,name=nadeko NadekoBot_RedisOptions: redis,name=nadeko
#NadekoBot_ShardRunCommand: dotnet #NadekoBot_ShardRunCommand: dotnet
#NadekoBot_ShardRunArguments: /app/NadekoBot.dll {0} {1} #NadekoBot_ShardRunArguments: /app/NadekoBot.dll {0} {1}
volumes: volumes:
@@ -27,6 +34,12 @@ services:
volumes: volumes:
- /srv/nadeko/redis-data:/data - /srv/nadeko/redis-data:/data
``` ```
3. Save your file and run docker compose
- `docker-compose up`
4. Edit creds in `/srv/nadeko/conf/creds.yml`
5. Run it again with
- `docker-compose up`
### Updating ### Updating
- `cd /srv/nadeko` - `cd /srv/nadeko`
- `docker-compose pull` - `docker-compose pull`

View File

@@ -15,6 +15,9 @@ namespace NadekoBot.Services
.MinimumLevel.Override("System", LogEventLevel.Information) .MinimumLevel.Override("System", LogEventLevel.Information)
.MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning) .MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning)
.Enrich.FromLogContext() .Enrich.FromLogContext()
.WriteTo.File("coord.log", LogEventLevel.Information,
rollOnFileSizeLimit: true,
fileSizeLimitBytes: 10_000_000)
.WriteTo.Console(LogEventLevel.Information, .WriteTo.Console(LogEventLevel.Information,
theme: GetTheme(), theme: GetTheme(),
outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] | #{LogSource} | {Message:lj}{NewLine}{Exception}") outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] | #{LogSource} | {Message:lj}{NewLine}{Exception}")

View File

@@ -9,9 +9,10 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Grpc.AspNetCore" Version="2.42.0" /> <PackageReference Include="Grpc.AspNetCore" Version="2.44.0" />
<PackageReference Include="Serilog" Version="2.10.0" /> <PackageReference Include="Serilog" Version="2.10.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" /> <PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" />
<PackageReference Include="Serilog.Sinks.File" Version="4.0.0" />
<PackageReference Include="YamlDotNet" Version="11.2.1" /> <PackageReference Include="YamlDotNet" Version="11.2.1" />
</ItemGroup> </ItemGroup>

View File

@@ -114,14 +114,6 @@ namespace NadekoBot.Coordinator
StartShard(shardId); StartShard(shardId);
break; break;
} }
if (status.Process is null or {HasExited: true})
{
Log.Warning("Shard {ShardId} is starting (process)...", shardId);
hadAction = true;
StartShard(shardId);
break;
}
if (DateTime.UtcNow - status.LastUpdate > if (DateTime.UtcNow - status.LastUpdate >
TimeSpan.FromSeconds(_config.UnresponsiveSec)) TimeSpan.FromSeconds(_config.UnresponsiveSec))
@@ -139,6 +131,24 @@ namespace NadekoBot.Coordinator
StartShard(shardId); StartShard(shardId);
break; break;
} }
try
{
if (status.Process is null or { HasExited: true })
{
Log.Warning("Shard {ShardId} is starting (process)...", shardId);
hadAction = true;
StartShard(shardId);
break;
}
}
catch (InvalidOperationException)
{
Log.Warning("Process for shard {ShardId} is bugged... ", shardId);
hadAction = true;
StartShard(shardId);
break;
}
} }
} }
@@ -161,17 +171,13 @@ namespace NadekoBot.Coordinator
var status = _shardStatuses[shardId]; var status = _shardStatuses[shardId];
try try
{ {
if (status.Process is { HasExited: false } p) status.Process?.Kill(true);
{ }
try catch
{ {
p.Kill(true); }
} try
catch {
{
}
}
status.Process?.Dispose(); status.Process?.Dispose();
} }
catch catch
@@ -280,10 +286,10 @@ namespace NadekoBot.Coordinator
for (var shardId = 0; shardId < _shardStatuses.Length; shardId++) for (var shardId = 0; shardId < _shardStatuses.Length; shardId++)
{ {
var status = _shardStatuses[shardId]; var status = _shardStatuses[shardId];
if (status.Process is { } p) if (status.Process is Process p)
{ {
p.Kill(); try{p.Kill();} catch {}
p.Dispose(); try{p.Dispose();} catch {}
_shardStatuses[shardId] = status with _shardStatuses[shardId] = status with
{ {
Process = null, Process = null,

View File

@@ -84,77 +84,6 @@ public static class DiscordUserExtensions
item.CurrencyAmount = 0; item.CurrencyAmount = 0;
} }
public static bool TryUpdateCurrencyState(
this NadekoContext ctx,
ulong userId,
string name,
string discrim,
string avatarId,
long amount,
bool allowNegative = false)
{
if (amount == 0)
return true;
// if remove - try to remove if he has more or equal than the amount
// and return number of rows > 0 (was there a change)
if (amount < 0 && !allowNegative)
{
var rows = ctx.Database.ExecuteSqlInterpolated($@"
UPDATE DiscordUser
SET CurrencyAmount=CurrencyAmount+{amount}
WHERE UserId={userId} AND CurrencyAmount>={-amount};");
return rows > 0;
}
// if remove and negative is allowed, just remove without any condition
if (amount < 0 && allowNegative)
{
var rows = ctx.Database.ExecuteSqlInterpolated($@"
UPDATE DiscordUser
SET CurrencyAmount=CurrencyAmount+{amount}
WHERE UserId={userId};");
return rows > 0;
}
// if add - create a new user with default values if it doesn't exist
// if it exists, sum current amount with the new one, if it doesn't
// he just has the new amount
var updatedUserData = !string.IsNullOrWhiteSpace(name);
name ??= "Unknown";
discrim ??= "????";
avatarId ??= "";
// just update the amount, there is no new user data
if (!updatedUserData)
{
ctx.Database.ExecuteSqlInterpolated($@"
UPDATE OR IGNORE DiscordUser
SET CurrencyAmount=CurrencyAmount+{amount}
WHERE UserId={userId};
INSERT OR IGNORE INTO DiscordUser (UserId, Username, Discriminator, AvatarId, CurrencyAmount, TotalXp)
VALUES ({userId}, {name}, {discrim}, {avatarId}, {amount}, 0);
");
}
else
{
ctx.Database.ExecuteSqlInterpolated($@"
UPDATE OR IGNORE DiscordUser
SET CurrencyAmount=CurrencyAmount+{amount},
Username={name},
Discriminator={discrim},
AvatarId={avatarId}
WHERE UserId={userId};
INSERT OR IGNORE INTO DiscordUser (UserId, Username, Discriminator, AvatarId, CurrencyAmount, TotalXp)
VALUES ({userId}, {name}, {discrim}, {avatarId}, {amount}, 0);
");
}
return true;
}
public static decimal GetTotalCurrency(this DbSet<DiscordUser> users) public static decimal GetTotalCurrency(this DbSet<DiscordUser> users)
=> users.Sum((Func<DiscordUser, decimal>)(x => x.CurrencyAmount)); => users.Sum((Func<DiscordUser, decimal>)(x => x.CurrencyAmount));

View File

@@ -73,6 +73,7 @@ public static class GuildConfigExtensions
{ {
GuildConfig config; GuildConfig config;
// todo linq2db
if (includes is null) if (includes is null)
config = ctx.GuildConfigs.IncludeEverything().FirstOrDefault(c => c.GuildId == guildId); config = ctx.GuildConfigs.IncludeEverything().FirstOrDefault(c => c.GuildId == guildId);
else else
@@ -100,6 +101,26 @@ public static class GuildConfigExtensions
} }
return config; return config;
// ctx.GuildConfigs
// .ToLinqToDBTable()
// .InsertOrUpdate(() => new()
// {
// GuildId = guildId,
// Permissions = Permissionv2.GetDefaultPermlist,
// WarningsInitialized = true,
// WarnPunishments = DefaultWarnPunishments
// },
// _ => new(),
// () => new()
// {
// GuildId = guildId
// });
//
// if(includes is null)
// return ctx.GuildConfigs
// .ToLinqToDBTable()
// .First(x => x.GuildId == guildId);
} }
public static LogSetting LogSettingsFor(this NadekoContext ctx, ulong guildId) public static LogSetting LogSettingsFor(this NadekoContext ctx, ulong guildId)

View File

@@ -1,4 +1,6 @@
#nullable disable #nullable disable
using LinqToDB;
using LinqToDB.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using NadekoBot.Db.Models; using NadekoBot.Db.Models;
using NadekoBot.Services.Database; using NadekoBot.Services.Database;
@@ -75,11 +77,22 @@ public static class WaifuExtensions
.Select(x => x.Waifu.UserId) .Select(x => x.Waifu.UserId)
.FirstOrDefault(); .FirstOrDefault();
public static WaifuInfoStats GetWaifuInfo(this NadekoContext ctx, ulong userId) public static async Task<WaifuInfoStats> GetWaifuInfoAsync(this NadekoContext ctx, ulong userId)
{ {
ctx.Database.ExecuteSqlInterpolated($@" await ctx.WaifuInfo
INSERT OR IGNORE INTO WaifuInfo (AffinityId, ClaimerId, Price, WaifuId) .ToLinqToDBTable()
VALUES ({null}, {null}, {1}, (SELECT Id FROM DiscordUser WHERE UserId={userId}));"); .InsertOrUpdateAsync(() => new()
{
AffinityId = null,
ClaimerId = null,
Price = 1,
WaifuId = ctx.DiscordUser.Where(x => x.UserId == userId).Select(x => x.Id).First()
},
_ => new(),
() => new()
{
WaifuId = ctx.DiscordUser.Where(x => x.UserId == userId).Select(x => x.Id).First()
});
var toReturn = ctx.WaifuInfo.AsQueryable() var toReturn = ctx.WaifuInfo.AsQueryable()
.Where(w => w.WaifuId .Where(w => w.WaifuId

View File

@@ -3,6 +3,7 @@ namespace NadekoBot.Services.Database.Models;
public class WaifuItem : DbEntity public class WaifuItem : DbEntity
{ {
public WaifuInfo WaifuInfo { get; set; }
public int? WaifuInfoId { get; set; } public int? WaifuInfoId { get; set; }
public string ItemEmoji { get; set; } public string ItemEmoji { get; set; }
public string Name { get; set; } public string Name { get; set; }

View File

@@ -37,9 +37,13 @@ public class NadekoContext : DbContext
public DbSet<NadekoExpression> Expressions { get; set; } public DbSet<NadekoExpression> Expressions { get; set; }
public DbSet<CurrencyTransaction> CurrencyTransactions { get; set; } public DbSet<CurrencyTransaction> CurrencyTransactions { get; set; }
public DbSet<WaifuUpdate> WaifuUpdates { get; set; } public DbSet<WaifuUpdate> WaifuUpdates { get; set; }
public DbSet<WaifuItem> WaifuItem { get; set; }
public DbSet<Warning> Warnings { get; set; } public DbSet<Warning> Warnings { get; set; }
public DbSet<UserXpStats> UserXpStats { get; set; } public DbSet<UserXpStats> UserXpStats { get; set; }
public DbSet<ClubInfo> Clubs { get; set; } public DbSet<ClubInfo> Clubs { get; set; }
public DbSet<ClubBans> ClubBans { get; set; }
public DbSet<ClubApplicants> ClubApplicants { get; set; }
//logging //logging
public DbSet<LogSetting> LogSettings { get; set; } public DbSet<LogSetting> LogSettings { get; set; }

View File

@@ -29,7 +29,7 @@ namespace NadekoBot.Migrations
b.HasIndex("UserId"); b.HasIndex("UserId");
b.ToTable("ClubApplicants"); b.ToTable("ClubApplicants", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Db.Models.ClubBans", b => modelBuilder.Entity("NadekoBot.Db.Models.ClubBans", b =>
@@ -44,7 +44,7 @@ namespace NadekoBot.Migrations
b.HasIndex("UserId"); b.HasIndex("UserId");
b.ToTable("ClubBans"); b.ToTable("ClubBans", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Db.Models.ClubInfo", b => modelBuilder.Entity("NadekoBot.Db.Models.ClubInfo", b =>
@@ -86,7 +86,7 @@ namespace NadekoBot.Migrations
b.HasIndex("OwnerId") b.HasIndex("OwnerId")
.IsUnique(); .IsUnique();
b.ToTable("Clubs"); b.ToTable("Clubs", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Db.Models.DiscordUser", b => modelBuilder.Entity("NadekoBot.Db.Models.DiscordUser", b =>
@@ -155,7 +155,7 @@ namespace NadekoBot.Migrations
b.HasIndex("UserId"); b.HasIndex("UserId");
b.ToTable("DiscordUser"); b.ToTable("DiscordUser", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Db.Models.FollowedStream", b => modelBuilder.Entity("NadekoBot.Db.Models.FollowedStream", b =>
@@ -189,7 +189,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId"); b.HasIndex("GuildConfigId");
b.ToTable("FollowedStream"); b.ToTable("FollowedStream", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiAltSetting", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiAltSetting", b =>
@@ -218,7 +218,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId") b.HasIndex("GuildConfigId")
.IsUnique(); .IsUnique();
b.ToTable("AntiAltSetting"); b.ToTable("AntiAltSetting", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiRaidSetting", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiRaidSetting", b =>
@@ -250,7 +250,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId") b.HasIndex("GuildConfigId")
.IsUnique(); .IsUnique();
b.ToTable("AntiRaidSetting"); b.ToTable("AntiRaidSetting", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiSpamIgnore", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiSpamIgnore", b =>
@@ -272,7 +272,7 @@ namespace NadekoBot.Migrations
b.HasIndex("AntiSpamSettingId"); b.HasIndex("AntiSpamSettingId");
b.ToTable("AntiSpamIgnore"); b.ToTable("AntiSpamIgnore", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiSpamSetting", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiSpamSetting", b =>
@@ -304,7 +304,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId") b.HasIndex("GuildConfigId")
.IsUnique(); .IsUnique();
b.ToTable("AntiSpamSetting"); b.ToTable("AntiSpamSetting", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.AutoCommand", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.AutoCommand", b =>
@@ -342,7 +342,7 @@ namespace NadekoBot.Migrations
b.HasKey("Id"); b.HasKey("Id");
b.ToTable("AutoCommands"); b.ToTable("AutoCommands", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.AutoTranslateChannel", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.AutoTranslateChannel", b =>
@@ -370,7 +370,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildId"); b.HasIndex("GuildId");
b.ToTable("AutoTranslateChannels"); b.ToTable("AutoTranslateChannels", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.AutoTranslateUser", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.AutoTranslateUser", b =>
@@ -398,7 +398,7 @@ namespace NadekoBot.Migrations
b.HasAlternateKey("ChannelId", "UserId"); b.HasAlternateKey("ChannelId", "UserId");
b.ToTable("AutoTranslateUsers"); b.ToTable("AutoTranslateUsers", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.BanTemplate", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.BanTemplate", b =>
@@ -421,7 +421,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildId") b.HasIndex("GuildId")
.IsUnique(); .IsUnique();
b.ToTable("BanTemplates"); b.ToTable("BanTemplates", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.BlacklistEntry", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.BlacklistEntry", b =>
@@ -441,7 +441,7 @@ namespace NadekoBot.Migrations
b.HasKey("Id"); b.HasKey("Id");
b.ToTable("Blacklist"); b.ToTable("Blacklist", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandAlias", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandAlias", b =>
@@ -466,7 +466,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId"); b.HasIndex("GuildConfigId");
b.ToTable("CommandAlias"); b.ToTable("CommandAlias", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandCooldown", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandCooldown", b =>
@@ -491,7 +491,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId"); b.HasIndex("GuildConfigId");
b.ToTable("CommandCooldown"); b.ToTable("CommandCooldown", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.CurrencyTransaction", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.CurrencyTransaction", b =>
@@ -529,7 +529,7 @@ namespace NadekoBot.Migrations
b.HasIndex("UserId"); b.HasIndex("UserId");
b.ToTable("CurrencyTransactions"); b.ToTable("CurrencyTransactions", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.DelMsgOnCmdChannel", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.DelMsgOnCmdChannel", b =>
@@ -554,7 +554,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId"); b.HasIndex("GuildConfigId");
b.ToTable("DelMsgOnCmdChannel"); b.ToTable("DelMsgOnCmdChannel", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.DiscordPermOverride", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.DiscordPermOverride", b =>
@@ -580,7 +580,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildId", "Command") b.HasIndex("GuildId", "Command")
.IsUnique(); .IsUnique();
b.ToTable("DiscordPermOverrides"); b.ToTable("DiscordPermOverrides", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.ExcludedItem", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.ExcludedItem", b =>
@@ -605,7 +605,7 @@ namespace NadekoBot.Migrations
b.HasIndex("XpSettingsId"); b.HasIndex("XpSettingsId");
b.ToTable("ExcludedItem"); b.ToTable("ExcludedItem", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.FeedSub", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.FeedSub", b =>
@@ -631,7 +631,7 @@ namespace NadekoBot.Migrations
b.HasAlternateKey("GuildConfigId", "Url"); b.HasAlternateKey("GuildConfigId", "Url");
b.ToTable("FeedSub"); b.ToTable("FeedSub", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterChannelId", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterChannelId", b =>
@@ -653,7 +653,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId"); b.HasIndex("GuildConfigId");
b.ToTable("FilterChannelId"); b.ToTable("FilterChannelId", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.FilteredWord", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.FilteredWord", b =>
@@ -675,7 +675,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId"); b.HasIndex("GuildConfigId");
b.ToTable("FilteredWord"); b.ToTable("FilteredWord", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterLinksChannelId", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterLinksChannelId", b =>
@@ -697,7 +697,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId"); b.HasIndex("GuildConfigId");
b.ToTable("FilterLinksChannelId"); b.ToTable("FilterLinksChannelId", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterWordsChannelId", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterWordsChannelId", b =>
@@ -719,7 +719,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId"); b.HasIndex("GuildConfigId");
b.ToTable("FilterWordsChannelId"); b.ToTable("FilterWordsChannelId", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.GCChannelId", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.GCChannelId", b =>
@@ -741,7 +741,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId"); b.HasIndex("GuildConfigId");
b.ToTable("GCChannelId"); b.ToTable("GCChannelId", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.GroupName", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.GroupName", b =>
@@ -767,7 +767,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId", "Number") b.HasIndex("GuildConfigId", "Number")
.IsUnique(); .IsUnique();
b.ToTable("GroupName"); b.ToTable("GroupName", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildConfig", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildConfig", b =>
@@ -891,7 +891,7 @@ namespace NadekoBot.Migrations
b.HasIndex("WarnExpireHours"); b.HasIndex("WarnExpireHours");
b.ToTable("GuildConfigs"); b.ToTable("GuildConfigs", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredLogItem", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredLogItem", b =>
@@ -917,7 +917,7 @@ namespace NadekoBot.Migrations
b.HasIndex("LogSettingId", "LogItemId", "ItemType") b.HasIndex("LogSettingId", "LogItemId", "ItemType")
.IsUnique(); .IsUnique();
b.ToTable("IgnoredLogChannels"); b.ToTable("IgnoredLogChannels", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredVoicePresenceChannel", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredVoicePresenceChannel", b =>
@@ -939,7 +939,7 @@ namespace NadekoBot.Migrations
b.HasIndex("LogSettingId"); b.HasIndex("LogSettingId");
b.ToTable("IgnoredVoicePresenceCHannels"); b.ToTable("IgnoredVoicePresenceCHannels", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.ImageOnlyChannel", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.ImageOnlyChannel", b =>
@@ -962,7 +962,7 @@ namespace NadekoBot.Migrations
b.HasIndex("ChannelId") b.HasIndex("ChannelId")
.IsUnique(); .IsUnique();
b.ToTable("ImageOnlyChannels"); b.ToTable("ImageOnlyChannels", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.LogSetting", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.LogSetting", b =>
@@ -1027,7 +1027,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildId") b.HasIndex("GuildId")
.IsUnique(); .IsUnique();
b.ToTable("LogSettings"); b.ToTable("LogSettings", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.MusicPlayerSettings", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.MusicPlayerSettings", b =>
@@ -1064,7 +1064,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildId") b.HasIndex("GuildId")
.IsUnique(); .IsUnique();
b.ToTable("MusicPlayerSettings"); b.ToTable("MusicPlayerSettings", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.MusicPlaylist", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.MusicPlaylist", b =>
@@ -1087,7 +1087,7 @@ namespace NadekoBot.Migrations
b.HasKey("Id"); b.HasKey("Id");
b.ToTable("MusicPlaylists"); b.ToTable("MusicPlaylists", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.MutedUserId", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.MutedUserId", b =>
@@ -1109,7 +1109,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId"); b.HasIndex("GuildConfigId");
b.ToTable("MutedUserId"); b.ToTable("MutedUserId", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.NadekoExpression", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.NadekoExpression", b =>
@@ -1147,7 +1147,7 @@ namespace NadekoBot.Migrations
b.HasKey("Id"); b.HasKey("Id");
b.ToTable("Expressions"); b.ToTable("Expressions", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.NsfwBlacklistedTag", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.NsfwBlacklistedTag", b =>
@@ -1169,7 +1169,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildId"); b.HasIndex("GuildId");
b.ToTable("NsfwBlacklistedTags"); b.ToTable("NsfwBlacklistedTags", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.Permissionv2", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.Permissionv2", b =>
@@ -1209,7 +1209,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId"); b.HasIndex("GuildConfigId");
b.ToTable("Permissions"); b.ToTable("Permissions", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.PlantedCurrency", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.PlantedCurrency", b =>
@@ -1246,7 +1246,7 @@ namespace NadekoBot.Migrations
b.HasIndex("MessageId") b.HasIndex("MessageId")
.IsUnique(); .IsUnique();
b.ToTable("PlantedCurrency"); b.ToTable("PlantedCurrency", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.PlaylistSong", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.PlaylistSong", b =>
@@ -1280,7 +1280,7 @@ namespace NadekoBot.Migrations
b.HasIndex("MusicPlaylistId"); b.HasIndex("MusicPlaylistId");
b.ToTable("PlaylistSong"); b.ToTable("PlaylistSong", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.Poll", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.Poll", b =>
@@ -1306,7 +1306,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildId") b.HasIndex("GuildId")
.IsUnique(); .IsUnique();
b.ToTable("Poll"); b.ToTable("Poll", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.PollAnswer", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.PollAnswer", b =>
@@ -1331,7 +1331,7 @@ namespace NadekoBot.Migrations
b.HasIndex("PollId"); b.HasIndex("PollId");
b.ToTable("PollAnswer"); b.ToTable("PollAnswer", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.PollVote", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.PollVote", b =>
@@ -1356,7 +1356,7 @@ namespace NadekoBot.Migrations
b.HasIndex("PollId"); b.HasIndex("PollId");
b.ToTable("PollVote"); b.ToTable("PollVote", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.Quote", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.Quote", b =>
@@ -1392,7 +1392,7 @@ namespace NadekoBot.Migrations
b.HasIndex("Keyword"); b.HasIndex("Keyword");
b.ToTable("Quotes"); b.ToTable("Quotes", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.ReactionRole", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.ReactionRole", b =>
@@ -1417,7 +1417,7 @@ namespace NadekoBot.Migrations
b.HasIndex("ReactionRoleMessageId"); b.HasIndex("ReactionRoleMessageId");
b.ToTable("ReactionRole"); b.ToTable("ReactionRole", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.ReactionRoleMessage", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.ReactionRoleMessage", b =>
@@ -1448,7 +1448,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId"); b.HasIndex("GuildConfigId");
b.ToTable("ReactionRoleMessage"); b.ToTable("ReactionRoleMessage", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.Reminder", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.Reminder", b =>
@@ -1482,7 +1482,7 @@ namespace NadekoBot.Migrations
b.HasIndex("When"); b.HasIndex("When");
b.ToTable("Reminders"); b.ToTable("Reminders", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.Repeater", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.Repeater", b =>
@@ -1517,7 +1517,7 @@ namespace NadekoBot.Migrations
b.HasKey("Id"); b.HasKey("Id");
b.ToTable("Repeaters"); b.ToTable("Repeaters", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.RewardedUser", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.RewardedUser", b =>
@@ -1546,7 +1546,7 @@ namespace NadekoBot.Migrations
b.HasIndex("PatreonUserId") b.HasIndex("PatreonUserId")
.IsUnique(); .IsUnique();
b.ToTable("RewardedUsers"); b.ToTable("RewardedUsers", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.RotatingPlayingStatus", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.RotatingPlayingStatus", b =>
@@ -1566,7 +1566,7 @@ namespace NadekoBot.Migrations
b.HasKey("Id"); b.HasKey("Id");
b.ToTable("RotatingStatus"); b.ToTable("RotatingStatus", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.SelfAssignedRole", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.SelfAssignedRole", b =>
@@ -1597,7 +1597,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildId", "RoleId") b.HasIndex("GuildId", "RoleId")
.IsUnique(); .IsUnique();
b.ToTable("SelfAssignableRoles"); b.ToTable("SelfAssignableRoles", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.ShopEntry", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.ShopEntry", b =>
@@ -1637,7 +1637,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId"); b.HasIndex("GuildConfigId");
b.ToTable("ShopEntry"); b.ToTable("ShopEntry", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.ShopEntryItem", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.ShopEntryItem", b =>
@@ -1659,7 +1659,7 @@ namespace NadekoBot.Migrations
b.HasIndex("ShopEntryId"); b.HasIndex("ShopEntryId");
b.ToTable("ShopEntryItem"); b.ToTable("ShopEntryItem", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.SlowmodeIgnoredRole", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.SlowmodeIgnoredRole", b =>
@@ -1681,7 +1681,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId"); b.HasIndex("GuildConfigId");
b.ToTable("SlowmodeIgnoredRole"); b.ToTable("SlowmodeIgnoredRole", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.SlowmodeIgnoredUser", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.SlowmodeIgnoredUser", b =>
@@ -1703,7 +1703,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId"); b.HasIndex("GuildConfigId");
b.ToTable("SlowmodeIgnoredUser"); b.ToTable("SlowmodeIgnoredUser", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.StreamRoleBlacklistedUser", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.StreamRoleBlacklistedUser", b =>
@@ -1728,7 +1728,7 @@ namespace NadekoBot.Migrations
b.HasIndex("StreamRoleSettingsId"); b.HasIndex("StreamRoleSettingsId");
b.ToTable("StreamRoleBlacklistedUser"); b.ToTable("StreamRoleBlacklistedUser", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.StreamRoleSettings", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.StreamRoleSettings", b =>
@@ -1760,7 +1760,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId") b.HasIndex("GuildConfigId")
.IsUnique(); .IsUnique();
b.ToTable("StreamRoleSettings"); b.ToTable("StreamRoleSettings", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.StreamRoleWhitelistedUser", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.StreamRoleWhitelistedUser", b =>
@@ -1785,7 +1785,7 @@ namespace NadekoBot.Migrations
b.HasIndex("StreamRoleSettingsId"); b.HasIndex("StreamRoleSettingsId");
b.ToTable("StreamRoleWhitelistedUser"); b.ToTable("StreamRoleWhitelistedUser", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.UnbanTimer", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.UnbanTimer", b =>
@@ -1810,7 +1810,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId"); b.HasIndex("GuildConfigId");
b.ToTable("UnbanTimer"); b.ToTable("UnbanTimer", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.UnmuteTimer", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.UnmuteTimer", b =>
@@ -1835,7 +1835,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId"); b.HasIndex("GuildConfigId");
b.ToTable("UnmuteTimer"); b.ToTable("UnmuteTimer", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.UnroleTimer", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.UnroleTimer", b =>
@@ -1863,7 +1863,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId"); b.HasIndex("GuildConfigId");
b.ToTable("UnroleTimer"); b.ToTable("UnroleTimer", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.UserXpStats", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.UserXpStats", b =>
@@ -1908,7 +1908,7 @@ namespace NadekoBot.Migrations
b.HasIndex("UserId", "GuildId") b.HasIndex("UserId", "GuildId")
.IsUnique(); .IsUnique();
b.ToTable("UserXpStats"); b.ToTable("UserXpStats", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.VcRoleInfo", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.VcRoleInfo", b =>
@@ -1933,7 +1933,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId"); b.HasIndex("GuildConfigId");
b.ToTable("VcRoleInfo"); b.ToTable("VcRoleInfo", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuInfo", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuInfo", b =>
@@ -1968,7 +1968,7 @@ namespace NadekoBot.Migrations
b.HasIndex("WaifuId") b.HasIndex("WaifuId")
.IsUnique(); .IsUnique();
b.ToTable("WaifuInfo"); b.ToTable("WaifuInfo", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuItem", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuItem", b =>
@@ -1993,7 +1993,7 @@ namespace NadekoBot.Migrations
b.HasIndex("WaifuInfoId"); b.HasIndex("WaifuInfoId");
b.ToTable("WaifuItem"); b.ToTable("WaifuItem", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuUpdate", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuUpdate", b =>
@@ -2025,7 +2025,7 @@ namespace NadekoBot.Migrations
b.HasIndex("UserId"); b.HasIndex("UserId");
b.ToTable("WaifuUpdates"); b.ToTable("WaifuUpdates", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.Warning", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.Warning", b =>
@@ -2068,7 +2068,7 @@ namespace NadekoBot.Migrations
b.HasIndex("UserId"); b.HasIndex("UserId");
b.ToTable("Warnings"); b.ToTable("Warnings", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.WarningPunishment", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.WarningPunishment", b =>
@@ -2099,7 +2099,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId"); b.HasIndex("GuildConfigId");
b.ToTable("WarningPunishment"); b.ToTable("WarningPunishment", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.XpCurrencyReward", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.XpCurrencyReward", b =>
@@ -2124,7 +2124,7 @@ namespace NadekoBot.Migrations
b.HasIndex("XpSettingsId"); b.HasIndex("XpSettingsId");
b.ToTable("XpCurrencyReward"); b.ToTable("XpCurrencyReward", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.XpRoleReward", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.XpRoleReward", b =>
@@ -2153,7 +2153,7 @@ namespace NadekoBot.Migrations
b.HasIndex("XpSettingsId", "Level") b.HasIndex("XpSettingsId", "Level")
.IsUnique(); .IsUnique();
b.ToTable("XpRoleReward"); b.ToTable("XpRoleReward", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.XpSettings", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.XpSettings", b =>
@@ -2176,7 +2176,7 @@ namespace NadekoBot.Migrations
b.HasIndex("GuildConfigId") b.HasIndex("GuildConfigId")
.IsUnique(); .IsUnique();
b.ToTable("XpSettings"); b.ToTable("XpSettings", (string)null);
}); });
modelBuilder.Entity("NadekoBot.Db.Models.ClubApplicants", b => modelBuilder.Entity("NadekoBot.Db.Models.ClubApplicants", b =>

View File

@@ -10,27 +10,26 @@ namespace NadekoBot.Modules.Administration
[OwnerOnly] [OwnerOnly]
public partial class DangerousCommands : NadekoModule<DangerousCommandsService> public partial class DangerousCommands : NadekoModule<DangerousCommandsService>
{ {
private async Task InternalExecSql(string sql, params object[] reps) private async Task ConfirmActionInternalAsync(string name, Func<Task> action)
{ {
sql = string.Format(sql, reps);
try try
{ {
var embed = _eb.Create() var embed = _eb.Create()
.WithTitle(GetText(strs.sql_confirm_exec)) .WithTitle(GetText(strs.sql_confirm_exec))
.WithDescription(Format.Code(sql)); .WithDescription(name);
if (!await PromptUserConfirmAsync(embed)) if (!await PromptUserConfirmAsync(embed))
return; return;
var res = await _service.ExecuteSql(sql); await action();
await SendConfirmAsync(res.ToString()); await ctx.OkAsync();
} }
catch (Exception ex) catch (Exception ex)
{ {
await SendErrorAsync(ex.ToString()); await SendErrorAsync(ex.ToString());
} }
} }
[Cmd] [Cmd]
[OwnerOnly] [OwnerOnly]
public partial Task SqlSelect([Leftover] string sql) public partial Task SqlSelect([Leftover] string sql)
@@ -57,38 +56,55 @@ namespace NadekoBot.Modules.Administration
[Cmd] [Cmd]
[OwnerOnly] [OwnerOnly]
public partial Task SqlExec([Leftover] string sql) public async partial Task SqlExec([Leftover] string sql)
=> InternalExecSql(sql); {
try
{
var embed = _eb.Create()
.WithTitle(GetText(strs.sql_confirm_exec))
.WithDescription(Format.Code(sql));
if (!await PromptUserConfirmAsync(embed))
return;
var res = await _service.ExecuteSql(sql);
await SendConfirmAsync(res.ToString());
}
catch (Exception ex)
{
await SendErrorAsync(ex.ToString());
}
}
[Cmd] [Cmd]
[OwnerOnly] [OwnerOnly]
public partial Task DeleteWaifus() public partial Task DeleteWaifus()
=> SqlExec(DangerousCommandsService.WAIFUS_DELETE_SQL); => ConfirmActionInternalAsync("Delete Waifus", () => _service.DeleteWaifus());
[Cmd] [Cmd]
[OwnerOnly] [OwnerOnly]
public partial Task DeleteWaifu(IUser user) public async partial Task DeleteWaifu(IUser user)
=> DeleteWaifu(user.Id); => await DeleteWaifu(user.Id);
[Cmd] [Cmd]
[OwnerOnly] [OwnerOnly]
public partial Task DeleteWaifu(ulong userId) public partial Task DeleteWaifu(ulong userId)
=> InternalExecSql(DangerousCommandsService.WAIFU_DELETE_SQL, userId); => ConfirmActionInternalAsync($"Delete Waifu {userId}", () => _service.DeleteWaifu(userId));
[Cmd] [Cmd]
[OwnerOnly] [OwnerOnly]
public partial Task DeleteCurrency() public partial Task DeleteCurrency()
=> SqlExec(DangerousCommandsService.CURRENCY_DELETE_SQL); => ConfirmActionInternalAsync("Delete Currency", () => _service.DeleteCurrency());
[Cmd] [Cmd]
[OwnerOnly] [OwnerOnly]
public partial Task DeletePlaylists() public partial Task DeletePlaylists()
=> SqlExec(DangerousCommandsService.MUSIC_PLAYLIST_DELETE_SQL); => ConfirmActionInternalAsync("Delete Playlists", () => _service.DeletePlaylists());
[Cmd] [Cmd]
[OwnerOnly] [OwnerOnly]
public partial Task DeleteXp() public partial Task DeleteXp()
=> SqlExec(DangerousCommandsService.XP_DELETE_SQL); => ConfirmActionInternalAsync("Delete Xp", () => _service.DeleteXp());
[Cmd] [Cmd]
[OwnerOnly] [OwnerOnly]
@@ -108,10 +124,6 @@ namespace NadekoBot.Modules.Administration
[OwnerOnly] [OwnerOnly]
public partial Task PurgeUser([Leftover] IUser user) public partial Task PurgeUser([Leftover] IUser user)
=> PurgeUser(user.Id); => PurgeUser(user.Id);
//[NadekoCommand, Usage, Description, Aliases]
//[OwnerOnly]
//public partial Task DeleteUnusedCrnQ() =>
// SqlExec(DangerousCommandsService.DeleteUnusedExpressionsAndQuotes);
} }
} }
} }

View File

@@ -2,46 +2,83 @@
using LinqToDB; using LinqToDB;
using LinqToDB.EntityFrameworkCore; using LinqToDB.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using NadekoBot.Db.Models;
using NadekoBot.Services.Database.Models; using NadekoBot.Services.Database.Models;
namespace NadekoBot.Modules.Administration.Services; namespace NadekoBot.Modules.Administration.Services;
public class DangerousCommandsService : INService public class DangerousCommandsService : INService
{ {
public const string WAIFUS_DELETE_SQL = @"DELETE FROM WaifuUpdates;
DELETE FROM WaifuItem;
DELETE FROM WaifuInfo;";
public const string WAIFU_DELETE_SQL =
@"DELETE FROM WaifuUpdates WHERE UserId=(SELECT Id FROM DiscordUser WHERE UserId={0});
DELETE FROM WaifuItem WHERE WaifuInfoId=(SELECT Id FROM WaifuInfo WHERE WaifuId=(SELECT Id FROM DiscordUser WHERE UserId={0}));
UPDATE WaifuInfo SET ClaimerId=NULL WHERE ClaimerId=(SELECT Id FROM DiscordUser WHERE UserId={0});
DELETE FROM WaifuInfo WHERE WaifuId=(SELECT Id FROM DiscordUser WHERE UserId={0});";
public const string CURRENCY_DELETE_SQL =
"UPDATE DiscordUser SET CurrencyAmount=0; DELETE FROM CurrencyTransactions; DELETE FROM PlantedCurrency;";
public const string MUSIC_PLAYLIST_DELETE_SQL = "DELETE FROM MusicPlaylists;";
public const string XP_DELETE_SQL = @"DELETE FROM UserXpStats;
UPDATE DiscordUser
SET ClubId=NULL,
IsClubAdmin=0,
TotalXp=0;
DELETE FROM ClubApplicants;
DELETE FROM ClubBans;
DELETE FROM Clubs;";
// public const string DeleteUnusedExpressionsAndQuotes = @"DELETE FROM Expressions
//WHERE UseCount=0 AND (DateAdded < date('now', '-7 day') OR DateAdded is null);
//DELETE FROM Quotes
//WHERE UseCount=0 AND (DateAdded < date('now', '-7 day') OR DateAdded is null);";
private readonly DbService _db; private readonly DbService _db;
public DangerousCommandsService(DbService db) public DangerousCommandsService(DbService db)
=> _db = db; => _db = db;
public async Task DeleteXp()
{
await using var ctx = _db.GetDbContext();
await ctx.DiscordUser.UpdateAsync(_ => new DiscordUser()
{
Club = null,
IsClubAdmin = false,
TotalXp = 0
});
await ctx.ClubApplicants.DeleteAsync();
await ctx.ClubBans.DeleteAsync();
await ctx.Clubs.DeleteAsync();
await ctx.SaveChangesAsync();
}
public async Task DeleteWaifus()
{
await using var ctx = _db.GetDbContext();
await ctx.WaifuUpdates.DeleteAsync();
await ctx.WaifuItem.DeleteAsync();
await ctx.WaifuInfo.DeleteAsync();
await ctx.SaveChangesAsync();
}
public async Task DeleteWaifu(ulong userId)
{
await using var ctx = _db.GetDbContext();
await ctx.WaifuUpdates
.Where(x => x.User.UserId == userId)
.DeleteAsync();
await ctx.WaifuItem
.Where(x => x.WaifuInfo.Waifu.UserId == userId)
.DeleteAsync();
await ctx.WaifuInfo
.Where(x => x.Claimer.UserId == userId)
.UpdateAsync(old => new WaifuInfo()
{
ClaimerId = null,
});
await ctx.WaifuInfo
.Where(x => x.Waifu.UserId == userId)
.DeleteAsync();
await ctx.SaveChangesAsync();
}
public async Task DeletePlaylists()
{
await using var ctx = _db.GetDbContext();
await ctx.MusicPlaylists.DeleteAsync();
await ctx.SaveChangesAsync();
}
public async Task DeleteCurrency()
{
await using var ctx = _db.GetDbContext();
await ctx.DiscordUser.UpdateAsync(_ => new DiscordUser()
{
CurrencyAmount = 0
});
await ctx.CurrencyTransactions.DeleteAsync();
await ctx.PlantedCurrency.DeleteAsync();
await ctx.SaveChangesAsync();
}
public async Task<int> ExecuteSql(string sql) public async Task<int> ExecuteSql(string sql)
{ {
int res; int res;

View File

@@ -30,7 +30,7 @@ public sealed class ImageOnlyChannelService : IEarlyBehavior
_client = client; _client = client;
_db = db; _db = db;
var uow = _db.GetDbContext(); using var uow = _db.GetDbContext();
_enabledOn = uow.ImageOnlyChannels.ToList() _enabledOn = uow.ImageOnlyChannels.ToList()
.GroupBy(x => x.GuildId) .GroupBy(x => x.GuildId)
.ToDictionary(x => x.Key, x => new ConcurrentHashSet<ulong>(x.Select(y => y.ChannelId))) .ToDictionary(x => x.Key, x => new ConcurrentHashSet<ulong>(x.Select(y => y.ChannelId)))

View File

@@ -11,6 +11,7 @@ public sealed class PlayingRotateService : INService, IReadyExecutor
private readonly SelfService _selfService; private readonly SelfService _selfService;
private readonly Replacer _rep; private readonly Replacer _rep;
private readonly DbService _db; private readonly DbService _db;
private readonly DiscordSocketClient _client;
public PlayingRotateService( public PlayingRotateService(
DiscordSocketClient client, DiscordSocketClient client,
@@ -22,6 +23,7 @@ public sealed class PlayingRotateService : INService, IReadyExecutor
_db = db; _db = db;
_bss = bss; _bss = bss;
_selfService = selfService; _selfService = selfService;
_client = client;
if (client.ShardId == 0) if (client.ShardId == 0)
_rep = new ReplacementBuilder().WithClient(client).WithProviders(phProviders).Build(); _rep = new ReplacementBuilder().WithClient(client).WithProviders(phProviders).Build();
@@ -29,6 +31,9 @@ public sealed class PlayingRotateService : INService, IReadyExecutor
public async Task OnReadyAsync() public async Task OnReadyAsync()
{ {
if (_client.ShardId != 0)
return;
using var timer = new PeriodicTimer(TimeSpan.FromMinutes(1)); using var timer = new PeriodicTimer(TimeSpan.FromMinutes(1));
var index = 0; var index = 0;
while (await timer.WaitForNextTickAsync()) while (await timer.WaitForNextTickAsync())

View File

@@ -478,7 +478,7 @@ public partial class Administration
var defaultMessage = GetText(strs.bandm(Format.Bold(ctx.Guild.Name), msg)); var defaultMessage = GetText(strs.bandm(Format.Bold(ctx.Guild.Name), msg));
var embed = _service.GetBanUserDmEmbed(Context, user, defaultMessage, msg, null); var embed = _service.GetBanUserDmEmbed(Context, user, defaultMessage, msg, null);
if (embed is not null) if (embed is not null)
await ctx.User.SendAsync(embed); await user.SendAsync(embed);
} }
catch catch
{ {

View File

@@ -1,4 +1,5 @@
#nullable disable #nullable disable
using LinqToDB;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using NadekoBot.Common.ModuleBehaviors; using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Common.TypeReaders.Models; using NadekoBot.Common.TypeReaders.Models;
@@ -224,16 +225,34 @@ public class UserPunishService : INService, IReadyExecutor
public async Task CheckAllWarnExpiresAsync() public async Task CheckAllWarnExpiresAsync()
{ {
await using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
var cleared = await uow.Database.ExecuteSqlRawAsync(@"UPDATE Warnings var cleared = await uow.Warnings
SET Forgiven = 1, .Where(x => uow.GuildConfigs
ForgivenBy = 'Expiry' .Any(y => y.GuildId == x.GuildId
WHERE GuildId in (SELECT GuildId FROM GuildConfigs WHERE WarnExpireHours > 0 AND WarnExpireAction = 0) && y.WarnExpireHours > 0
AND Forgiven = 0 && y.WarnExpireAction == WarnExpireAction.Clear)
AND DateAdded < datetime('now', (SELECT '-' || WarnExpireHours || ' hours' FROM GuildConfigs as gc WHERE gc.GuildId = Warnings.GuildId));"); && x.Forgiven == false
&& x.DateAdded
< DateTime.UtcNow.AddHours(-uow.GuildConfigs
.Where(y => x.GuildId == y.GuildId)
.Select(y => y.WarnExpireHours)
.First()))
.UpdateAsync(_ => new()
{
Forgiven = true,
ForgivenBy = "expiry"
});
var deleted = await uow.Database.ExecuteSqlRawAsync(@"DELETE FROM Warnings var deleted = await uow.Warnings
WHERE GuildId in (SELECT GuildId FROM GuildConfigs WHERE WarnExpireHours > 0 AND WarnExpireAction = 1) .Where(x => uow.GuildConfigs
AND DateAdded < datetime('now', (SELECT '-' || WarnExpireHours || ' hours' FROM GuildConfigs as gc WHERE gc.GuildId = Warnings.GuildId));"); .Any(y => y.GuildId == x.GuildId
&& y.WarnExpireHours > 0
&& y.WarnExpireAction == WarnExpireAction.Delete)
&& x.DateAdded
< DateTime.UtcNow.AddHours(-uow.GuildConfigs
.Where(y => x.GuildId == y.GuildId)
.Select(y => y.WarnExpireHours)
.First()))
.DeleteAsync();
if (cleared > 0 || deleted > 0) if (cleared > 0 || deleted > 0)
{ {
@@ -241,6 +260,8 @@ WHERE GuildId in (SELECT GuildId FROM GuildConfigs WHERE WarnExpireHours > 0 AND
cleared, cleared,
deleted); deleted);
} }
await uow.SaveChangesAsync();
} }
public async Task CheckWarnExpiresAsync(ulong guildId) public async Task CheckWarnExpiresAsync(ulong guildId)
@@ -251,21 +272,24 @@ WHERE GuildId in (SELECT GuildId FROM GuildConfigs WHERE WarnExpireHours > 0 AND
if (config.WarnExpireHours == 0) if (config.WarnExpireHours == 0)
return; return;
var hours = $"{-config.WarnExpireHours} hours";
if (config.WarnExpireAction == WarnExpireAction.Clear) if (config.WarnExpireAction == WarnExpireAction.Clear)
{ {
await uow.Database.ExecuteSqlInterpolatedAsync($@"UPDATE warnings await uow.Warnings
SET Forgiven = 1, .Where(x => x.GuildId == guildId
ForgivenBy = 'Expiry' && x.Forgiven == false
WHERE GuildId={guildId} && x.DateAdded < DateTime.UtcNow.AddHours(-config.WarnExpireHours))
AND Forgiven = 0 .UpdateAsync(_ => new()
AND DateAdded < datetime('now', {hours})"); {
Forgiven = true,
ForgivenBy = "expiry"
});
} }
else if (config.WarnExpireAction == WarnExpireAction.Delete) else if (config.WarnExpireAction == WarnExpireAction.Delete)
{ {
await uow.Database.ExecuteSqlInterpolatedAsync($@"DELETE FROM warnings await uow.Warnings
WHERE GuildId={guildId} .Where(x => x.GuildId == guildId
AND DateAdded < datetime('now', {hours})"); && x.DateAdded < DateTime.UtcNow.AddHours(-config.WarnExpireHours))
.DeleteAsync();
} }
await uow.SaveChangesAsync(); await uow.SaveChangesAsync();

View File

@@ -76,6 +76,7 @@ public partial class Gambling
(race.FinishedUsers[0].Bet * (race.Users.Count - 1)) + CurrencySign))); (race.FinishedUsers[0].Bet * (race.Users.Count - 1)) + CurrencySign)));
} }
ar.Dispose();
return SendConfirmAsync(GetText(strs.animal_race), return SendConfirmAsync(GetText(strs.animal_race),
GetText(strs.animal_race_won(Format.Bold(winner.Username), winner.Animal.Icon))); GetText(strs.animal_race_won(Format.Bold(winner.Username), winner.Animal.Icon)));
} }
@@ -127,6 +128,7 @@ public partial class Gambling
private Task Ar_OnStartingFailed(AnimalRace race) private Task Ar_OnStartingFailed(AnimalRace race)
{ {
_service.AnimalRaces.TryRemove(ctx.Guild.Id, out _); _service.AnimalRaces.TryRemove(ctx.Guild.Id, out _);
race.Dispose();
return ReplyErrorLocalizedAsync(strs.animal_race_failed); return ReplyErrorLocalizedAsync(strs.animal_race_failed);
} }

View File

@@ -65,8 +65,7 @@ public partial class Gambling : GamblingModule<GamblingService>
public async Task<string> GetBalanceStringAsync(ulong userId) public async Task<string> GetBalanceStringAsync(ulong userId)
{ {
var wallet = await _cs.GetWalletAsync(userId); var bal = await _cs.GetBalanceAsync(userId);
var bal = await wallet.GetBalance();
return N(bal); return N(bal);
} }

View File

@@ -60,7 +60,6 @@ public class GamblingService : INService, IReadyExecutor
await using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
await uow.CurrencyTransactions await uow.CurrencyTransactions
.DeleteAsync(ct => ct.DateAdded == null || now - ct.DateAdded < days); .DeleteAsync(ct => ct.DateAdded == null || now - ct.DateAdded < days);
await uow.SaveChangesAsync();
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -102,17 +101,16 @@ public class GamblingService : INService, IReadyExecutor
if (maxDecay == 0) if (maxDecay == 0)
maxDecay = int.MaxValue; maxDecay = int.MaxValue;
await uow.Database.ExecuteSqlInterpolatedAsync($@" var decay = (double)config.Decay.Percent;
UPDATE DiscordUser await uow.DiscordUser
SET CurrencyAmount= .Where(x => x.CurrencyAmount > config.Decay.MinThreshold && x.UserId != _client.CurrentUser.Id)
CASE WHEN .UpdateAsync(old => new()
{maxDecay} > ROUND(CurrencyAmount * {config.Decay.Percent} - 0.5) {
THEN CurrencyAmount =
CurrencyAmount - ROUND(CurrencyAmount * {config.Decay.Percent} - 0.5) maxDecay > Sql.Round((old.CurrencyAmount * decay) - 0.5)
ELSE ? (long)(old.CurrencyAmount - Sql.Round((old.CurrencyAmount * decay) - 0.5))
CurrencyAmount - {maxDecay} : old.CurrencyAmount - maxDecay
END });
WHERE CurrencyAmount > {config.Decay.MinThreshold} AND UserId!={_client.CurrentUser.Id};");
_cache.SetLastCurrencyDecay(); _cache.SetLastCurrencyDecay();
await uow.SaveChangesAsync(); await uow.SaveChangesAsync();

View File

@@ -35,7 +35,7 @@ public class VoteRewardService : INService, IReadyExecutor
if (_client.ShardId != 0) if (_client.ShardId != 0)
return; return;
var http = new HttpClient(new HttpClientHandler using var http = new HttpClient(new HttpClientHandler
{ {
AllowAutoRedirect = false, AllowAutoRedirect = false,
ServerCertificateCustomValidationCallback = delegate { return true; } ServerCertificateCustomValidationCallback = delegate { return true; }

View File

@@ -236,9 +236,9 @@ public partial class Gambling
public partial Task WaifuInfo(ulong targetId) public partial Task WaifuInfo(ulong targetId)
=> InternalWaifuInfo(targetId); => InternalWaifuInfo(targetId);
private Task InternalWaifuInfo(ulong targetId, string name = null) private async Task InternalWaifuInfo(ulong targetId, string name = null)
{ {
var wi = _service.GetFullWaifuInfoAsync(targetId); var wi = await _service.GetFullWaifuInfoAsync(targetId);
var affInfo = _service.GetAffinityTitle(wi.AffinityCount); var affInfo = _service.GetAffinityTitle(wi.AffinityCount);
var waifuItems = _service.GetWaifuItems().ToDictionary(x => x.ItemEmoji, x => x); var waifuItems = _service.GetWaifuItems().ToDictionary(x => x.ItemEmoji, x => x);
@@ -280,7 +280,7 @@ public partial class Gambling
true) true)
.AddField(GetText(strs.gifts), itemsStr, true); .AddField(GetText(strs.gifts), itemsStr, true);
return ctx.Channel.EmbedAsync(embed); await ctx.Channel.EmbedAsync(embed);
} }
[Cmd] [Cmd]

View File

@@ -385,10 +385,10 @@ public class WaifuService : INService, IReadyExecutor
return true; return true;
} }
public WaifuInfoStats GetFullWaifuInfoAsync(ulong targetId) public async Task<WaifuInfoStats> GetFullWaifuInfoAsync(ulong targetId)
{ {
using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
var wi = uow.GetWaifuInfo(targetId); var wi = await uow.GetWaifuInfoAsync(targetId);
if (wi is null) if (wi is null)
{ {
wi = new() wi = new()
@@ -409,12 +409,12 @@ public class WaifuService : INService, IReadyExecutor
return wi; return wi;
} }
public WaifuInfoStats GetFullWaifuInfoAsync(IGuildUser target) public async Task<WaifuInfoStats> GetFullWaifuInfoAsync(IGuildUser target)
{ {
using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
_ = uow.GetOrCreateUser(target); _ = uow.GetOrCreateUser(target);
return GetFullWaifuInfoAsync(target.Id); return await GetFullWaifuInfoAsync(target.Id);
} }
public string GetClaimTitle(int count) public string GetClaimTitle(int count)

View File

@@ -359,7 +359,7 @@ public partial class Help : NadekoModule<HelpService>
}; };
using var dlClient = new AmazonS3Client(accessKey, secretAcccessKey, config); using var dlClient = new AmazonS3Client(accessKey, secretAcccessKey, config);
var oldVersionObject = await dlClient.GetObjectAsync(new() using var oldVersionObject = await dlClient.GetObjectAsync(new()
{ {
BucketName = "nadeko-pictures", BucketName = "nadeko-pictures",
Key = "cmds/versions.json" Key = "cmds/versions.json"

View File

@@ -76,7 +76,7 @@ public sealed partial class YtLoader
var mem = GetScriptResponseSpan(response); var mem = GetScriptResponseSpan(response);
var root = JsonDocument.Parse(mem).RootElement; var root = JsonDocument.Parse(mem).RootElement;
var tracksJsonItems = root using var tracksJsonItems = root
.GetProperty("contents") .GetProperty("contents")
.GetProperty("twoColumnSearchResultsRenderer") .GetProperty("twoColumnSearchResultsRenderer")
.GetProperty("primaryContents") .GetProperty("primaryContents")

View File

@@ -7,12 +7,6 @@ using Newtonsoft.Json.Linq;
namespace NadekoBot.Modules.Nsfw; namespace NadekoBot.Modules.Nsfw;
public record TagRequest(
ulong GuildId,
bool ForceExplicit,
Booru SearchType,
params string[] Tags);
public record UrlReply public record UrlReply
{ {
public string Error { get; init; } public string Error { get; init; }
@@ -30,7 +24,6 @@ public class SearchImagesService : ISearchImagesService, INService
public ConcurrentDictionary<ulong, Timer> AutoBoobTimers { get; } = new(); public ConcurrentDictionary<ulong, Timer> AutoBoobTimers { get; } = new();
public ConcurrentDictionary<ulong, Timer> AutoButtTimers { get; } = new(); public ConcurrentDictionary<ulong, Timer> AutoButtTimers { get; } = new();
private readonly Random _rng; private readonly Random _rng;
private readonly HttpClient _http;
private readonly SearchImageCacher _cache; private readonly SearchImageCacher _cache;
private readonly IHttpClientFactory _httpFactory; private readonly IHttpClientFactory _httpFactory;
private readonly DbService _db; private readonly DbService _db;
@@ -39,14 +32,11 @@ public class SearchImagesService : ISearchImagesService, INService
public SearchImagesService( public SearchImagesService(
DbService db, DbService db,
IHttpClientFactory http,
SearchImageCacher cacher, SearchImageCacher cacher,
IHttpClientFactory httpFactory) IHttpClientFactory httpFactory)
{ {
_db = db; _db = db;
_rng = new NadekoRandom(); _rng = new NadekoRandom();
_http = http.CreateClient();
_http.AddFakeHeaders();
_cache = cacher; _cache = cacher;
_httpFactory = httpFactory; _httpFactory = httpFactory;
@@ -205,8 +195,10 @@ public class SearchImagesService : ISearchImagesService, INService
{ {
try try
{ {
using var http = _httpFactory.CreateClient();
http.AddFakeHeaders();
JToken obj; JToken obj;
obj = JArray.Parse(await _http.GetStringAsync($"http://api.oboobs.ru/boobs/{_rng.Next(0, 12000)}"))[0]; obj = JArray.Parse(await http.GetStringAsync($"http://api.oboobs.ru/boobs/{_rng.Next(0, 12000)}"))[0];
return new() return new()
{ {
Error = "", Error = "",
@@ -269,8 +261,10 @@ public class SearchImagesService : ISearchImagesService, INService
{ {
try try
{ {
using var http = _httpFactory.CreateClient();
http.AddFakeHeaders();
JToken obj; JToken obj;
obj = JArray.Parse(await _http.GetStringAsync($"http://api.obutts.ru/butts/{_rng.Next(0, 6100)}"))[0]; obj = JArray.Parse(await http.GetStringAsync($"http://api.obutts.ru/butts/{_rng.Next(0, 6100)}"))[0];
return new() return new()
{ {
Error = "", Error = "",

View File

@@ -98,15 +98,16 @@ public partial class Searches
return; return;
var fileName = $"{query}-sparkline.{imageData.Extension}"; var fileName = $"{query}-sparkline.{imageData.Extension}";
using var attachment = new FileAttachment(
imageData.FileData,
fileName
);
await message.ModifyAsync(mp => await message.ModifyAsync(mp =>
{ {
mp.Attachments = mp.Attachments =
new(new[] new(new[]
{ {
new FileAttachment( attachment
imageData.FileData,
fileName
)
}); });
mp.Embed = eb.WithImageUrl($"attachment://{fileName}").Build(); mp.Embed = eb.WithImageUrl($"attachment://{fileName}").Build();

View File

@@ -1,55 +1,55 @@
using System.Net.Http.Json; // using System.Net.Http.Json;
//
namespace NadekoBot.Modules.Searches; // namespace NadekoBot.Modules.Searches;
//
public sealed class PolygonApiClient : IDisposable // public sealed class PolygonApiClient : IDisposable
{ // {
private const string BASE_URL = "https://api.polygon.io/v3"; // private const string BASE_URL = "https://api.polygon.io/v3";
//
private readonly HttpClient _httpClient; // private readonly HttpClient _httpClient;
private readonly string _apiKey; // private readonly string _apiKey;
//
public PolygonApiClient(HttpClient httpClient, string apiKey) // public PolygonApiClient(HttpClient httpClient, string apiKey)
{ // {
_httpClient = httpClient; // _httpClient = httpClient;
_apiKey = apiKey; // _apiKey = apiKey;
} // }
//
public async Task<IReadOnlyCollection<PolygonTickerData>> TickersAsync(string? ticker = null, string? query = null) // public async Task<IReadOnlyCollection<PolygonTickerData>> TickersAsync(string? ticker = null, string? query = null)
{ // {
if (string.IsNullOrWhiteSpace(query)) // if (string.IsNullOrWhiteSpace(query))
query = null; // query = null;
//
if(query is not null) // if(query is not null)
query = Uri.EscapeDataString(query); // query = Uri.EscapeDataString(query);
//
var requestString = $"{BASE_URL}/reference/tickers" // var requestString = $"{BASE_URL}/reference/tickers"
+ "?type=CS" // + "?type=CS"
+ "&active=true" // + "&active=true"
+ "&order=asc" // + "&order=asc"
+ "&limit=1000" // + "&limit=1000"
+ $"&apiKey={_apiKey}"; // + $"&apiKey={_apiKey}";
//
if (!string.IsNullOrWhiteSpace(ticker)) // if (!string.IsNullOrWhiteSpace(ticker))
requestString += $"&ticker={ticker}"; // requestString += $"&ticker={ticker}";
//
if (!string.IsNullOrWhiteSpace(query)) // if (!string.IsNullOrWhiteSpace(query))
requestString += $"&search={query}"; // requestString += $"&search={query}";
//
//
var response = await _httpClient.GetFromJsonAsync<PolygonTickerResponse>(requestString); // var response = await _httpClient.GetFromJsonAsync<PolygonTickerResponse>(requestString);
//
if (response is null) // if (response is null)
return Array.Empty<PolygonTickerData>(); // return Array.Empty<PolygonTickerData>();
//
return response.Results; // return response.Results;
} // }
//
// public async Task<PolygonTickerDetailsV3> TickerDetailsV3Async(string ticker) // // public async Task<PolygonTickerDetailsV3> TickerDetailsV3Async(string ticker)
// { // // {
// return new(); // // return new();
// } // // }
//
public void Dispose() // public void Dispose()
=> _httpClient.Dispose(); // => _httpClient.Dispose();
} // }

View File

@@ -34,7 +34,7 @@ public partial class Searches
return; return;
using var http = _httpFactory.CreateClient("memelist"); using var http = _httpFactory.CreateClient("memelist");
var res = await http.GetAsync("https://api.memegen.link/templates/"); using var res = await http.GetAsync("https://api.memegen.link/templates/");
var rawJson = await res.Content.ReadAsStringAsync(); var rawJson = await res.Content.ReadAsStringAsync();

View File

@@ -109,7 +109,8 @@ public class SearchesService : INService
using (var avatarImg = Image.Load<Rgba32>(data)) using (var avatarImg = Image.Load<Rgba32>(data))
{ {
avatarImg.Mutate(x => x.Resize(85, 85).ApplyRoundedCorners(42)); avatarImg.Mutate(x => x.Resize(85, 85).ApplyRoundedCorners(42));
data = avatarImg.ToStream().ToArray(); await using var avStream = avatarImg.ToStream();
data = avStream.ToArray();
DrawAvatar(bg, avatarImg); DrawAvatar(bg, avatarImg);
} }
@@ -141,7 +142,8 @@ public class SearchesService : INService
bg.Mutate(x => x.DrawImage(flowers, new(0, 0), new GraphicsOptions())); bg.Mutate(x => x.DrawImage(flowers, new(0, 0), new GraphicsOptions()));
} }
return bg.ToStream().ToArray(); await using var stream = bg.ToStream();
return stream.ToArray();
} }
public Task<WeatherData> GetWeatherDataAsync(string query) public Task<WeatherData> GetWeatherDataAsync(string query)
@@ -532,7 +534,7 @@ public class SearchesService : INService
http.DefaultRequestHeaders.Clear(); http.DefaultRequestHeaders.Clear();
using var response = await http.SendAsync(msg); using var response = await http.SendAsync(msg);
var content = await response.Content.ReadAsStreamAsync(); await using var content = await response.Content.ReadAsStreamAsync();
using var document = await _googleParser.ParseDocumentAsync(content); using var document = await _googleParser.ParseDocumentAsync(content);
var elems = document.QuerySelectorAll("div.g > div > div"); var elems = document.QuerySelectorAll("div.g > div > div");

View File

@@ -3,6 +3,7 @@ using LinqToDB;
using LinqToDB.EntityFrameworkCore; using LinqToDB.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using NadekoBot.Common.ModuleBehaviors; using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Services.Database.Models;
using System.Net; using System.Net;
namespace NadekoBot.Modules.Searches; namespace NadekoBot.Modules.Searches;
@@ -31,12 +32,14 @@ public sealed class TranslateService : ITranslateService, ILateExecutor, IReadyE
public async Task OnReadyAsync() public async Task OnReadyAsync()
{ {
var ctx = _db.GetDbContext(); List<AutoTranslateChannel> cs;
await using (var ctx = _db.GetDbContext())
var guilds = _bot.AllGuildConfigs.Select(x => x.GuildId).ToList(); {
var cs = await ctx.AutoTranslateChannels.Include(x => x.Users) var guilds = _bot.AllGuildConfigs.Select(x => x.GuildId).ToList();
cs = await ctx.AutoTranslateChannels.Include(x => x.Users)
.Where(x => guilds.Contains(x.GuildId)) .Where(x => guilds.Contains(x.GuildId))
.ToListAsyncEF(); .ToListAsyncEF();
}
foreach (var c in cs) foreach (var c in cs)
{ {
@@ -103,7 +106,7 @@ public sealed class TranslateService : ITranslateService, ILateExecutor, IReadyE
public async Task<bool> ToggleAtl(ulong guildId, ulong channelId, bool autoDelete) public async Task<bool> ToggleAtl(ulong guildId, ulong channelId, bool autoDelete)
{ {
var ctx = _db.GetDbContext(); await using var ctx = _db.GetDbContext();
var old = await ctx.AutoTranslateChannels.ToLinqToDBTable() var old = await ctx.AutoTranslateChannels.ToLinqToDBTable()
.FirstOrDefaultAsyncLinqToDB(x => x.ChannelId == channelId); .FirstOrDefaultAsyncLinqToDB(x => x.ChannelId == channelId);
@@ -164,7 +167,7 @@ public sealed class TranslateService : ITranslateService, ILateExecutor, IReadyE
if (!_google.Languages.ContainsKey(from) || !_google.Languages.ContainsKey(to)) if (!_google.Languages.ContainsKey(from) || !_google.Languages.ContainsKey(to))
return null; return null;
var ctx = _db.GetDbContext(); await using var ctx = _db.GetDbContext();
var ch = await ctx.AutoTranslateChannels.GetByChannelId(channelId); var ch = await ctx.AutoTranslateChannels.GetByChannelId(channelId);
if (ch is null) if (ch is null)
@@ -206,14 +209,13 @@ public sealed class TranslateService : ITranslateService, ILateExecutor, IReadyE
public async Task<bool> UnregisterUser(ulong channelId, ulong userId) public async Task<bool> UnregisterUser(ulong channelId, ulong userId)
{ {
var ctx = _db.GetDbContext(); await using var ctx = _db.GetDbContext();
var rows = await ctx.AutoTranslateUsers.ToLinqToDBTable() var rows = await ctx.AutoTranslateUsers.ToLinqToDBTable()
.DeleteAsync(x => x.UserId == userId && x.Channel.ChannelId == channelId); .DeleteAsync(x => x.UserId == userId && x.Channel.ChannelId == channelId);
if (_users.TryGetValue(channelId, out var inner)) if (_users.TryGetValue(channelId, out var inner))
inner.TryRemove(userId, out _); inner.TryRemove(userId, out _);
await ctx.SaveChangesAsync();
return rows > 0; return rows > 0;
} }

View File

@@ -62,7 +62,7 @@ public class PicartoProvider : Provider
{ {
http.DefaultRequestHeaders.Accept.Add(new("application/json")); http.DefaultRequestHeaders.Accept.Add(new("application/json"));
// get id based on the username // get id based on the username
var res = await http.GetAsync($"https://api.picarto.tv/v1/channel/name/{login}"); using var res = await http.GetAsync($"https://api.picarto.tv/v1/channel/name/{login}");
if (!res.IsSuccessStatusCode) if (!res.IsSuccessStatusCode)
continue; continue;

View File

@@ -59,7 +59,7 @@ If you are experiencing ratelimits, you should create your own application at: h
// so there is no need for ratelimit checks atm // so there is no need for ratelimit checks atm
try try
{ {
var res = await http.PostAsJsonAsync( using var res = await http.PostAsJsonAsync(
$"https://open-api.trovo.live/openplatform/channels/id", $"https://open-api.trovo.live/openplatform/channels/id",
new TrovoRequestData() new TrovoRequestData()
{ {

View File

@@ -84,12 +84,13 @@ public class PatreonRewardsService : INService, IReadyExecutor
try try
{ {
using var http = _httpFactory.CreateClient(); using var http = _httpFactory.CreateClient();
var res = await http.PostAsync("https://www.patreon.com/api/oauth2/token" using var content = new StringContent(string.Empty);
using var res = await http.PostAsync("https://www.patreon.com/api/oauth2/token"
+ "?grant_type=refresh_token" + "?grant_type=refresh_token"
+ $"&refresh_token={creds.Patreon.RefreshToken}" + $"&refresh_token={creds.Patreon.RefreshToken}"
+ $"&client_id={creds.Patreon.ClientId}" + $"&client_id={creds.Patreon.ClientId}"
+ $"&client_secret={creds.Patreon.ClientSecret}", + $"&client_secret={creds.Patreon.ClientSecret}",
new StringContent(string.Empty)); content);
res.EnsureSuccessStatusCode(); res.EnsureSuccessStatusCode();

View File

@@ -78,8 +78,6 @@ public class RemindService : INService
.ToLinqToDBTable() .ToLinqToDBTable()
.Where(x => x.ServerId / 4194304 % (ulong)_creds.TotalShards == (ulong)_client.ShardId .Where(x => x.ServerId / 4194304 % (ulong)_creds.TotalShards == (ulong)_client.ShardId
&& x.When < now) && x.When < now)
// .FromSqlInterpolated(
// $"select * from reminders where ((serverid >> 22) % {_creds.TotalShards}) == {_client.ShardId} and \"when\" < {now};")
.ToListAsyncLinqToDB(); .ToListAsyncLinqToDB();
} }

View File

@@ -27,7 +27,7 @@ public sealed class RepeaterService : IReadyExecutor, INService
_creds = creds; _creds = creds;
_client = client; _client = client;
var uow = _db.GetDbContext(); using var uow = _db.GetDbContext();
var shardRepeaters = uow.Set<Repeater>() var shardRepeaters = uow.Set<Repeater>()
.Where(x => (int)(x.GuildId / Math.Pow(2, 22)) % _creds.TotalShards .Where(x => (int)(x.GuildId / Math.Pow(2, 22)) % _creds.TotalShards
== _client.ShardId) == _client.ShardId)

View File

@@ -336,7 +336,7 @@ public partial class Utility : NadekoModule
return; return;
using var http = _httpFactory.CreateClient(); using var http = _httpFactory.CreateClient();
var res = await http.GetAsync(url, HttpCompletionOption.ResponseHeadersRead); using var res = await http.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
if (!res.IsImage() || res.GetImageSize() is null or > 262_144) if (!res.IsImage() || res.GetImageSize() is null or > 262_144)
{ {
await ReplyErrorLocalizedAsync(strs.invalid_emoji_link); await ReplyErrorLocalizedAsync(strs.invalid_emoji_link);

View File

@@ -121,153 +121,155 @@ public class XpService : INService, IReadyExecutor
#endif #endif
} }
public Task OnReadyAsync() public async Task OnReadyAsync()
=> UpdateLoop();
private async Task UpdateLoop()
{ {
using var timer = new PeriodicTimer(5.Seconds()); using var timer = new PeriodicTimer(5.Seconds());
while (await timer.WaitForNextTickAsync()) while (await timer.WaitForNextTickAsync())
{ {
try await UpdateLoop();
}
}
private async Task UpdateLoop()
{
try
{
var toNotify =
new List<(IGuild Guild, IMessageChannel MessageChannel, IUser User, int Level,
XpNotificationLocation NotifyType, NotifOf NotifOf)>();
var roleRewards = new Dictionary<ulong, List<XpRoleReward>>();
var curRewards = new Dictionary<ulong, List<XpCurrencyReward>>();
var toAddTo = new List<UserCacheItem>();
while (_addMessageXp.TryDequeue(out var usr))
toAddTo.Add(usr);
var group = toAddTo.GroupBy(x => (GuildId: x.Guild.Id, x.User));
if (toAddTo.Count == 0)
return;
await using (var uow = _db.GetDbContext())
{ {
var toNotify = foreach (var item in group)
new List<(IGuild Guild, IMessageChannel MessageChannel, IUser User, int Level,
XpNotificationLocation NotifyType, NotifOf NotifOf)>();
var roleRewards = new Dictionary<ulong, List<XpRoleReward>>();
var curRewards = new Dictionary<ulong, List<XpCurrencyReward>>();
var toAddTo = new List<UserCacheItem>();
while (_addMessageXp.TryDequeue(out var usr))
toAddTo.Add(usr);
var group = toAddTo.GroupBy(x => (GuildId: x.Guild.Id, x.User));
if (toAddTo.Count == 0)
continue;
await using (var uow = _db.GetDbContext())
{ {
foreach (var item in group) var xp = item.Sum(x => x.XpAmount);
var usr = uow.GetOrCreateUserXpStats(item.Key.GuildId, item.Key.User.Id);
var du = uow.GetOrCreateUser(item.Key.User);
var globalXp = du.TotalXp;
var oldGlobalLevelData = new LevelStats(globalXp);
var newGlobalLevelData = new LevelStats(globalXp + xp);
var oldGuildLevelData = new LevelStats(usr.Xp + usr.AwardedXp);
usr.Xp += xp;
du.TotalXp += xp;
if (du.Club is not null)
du.Club.Xp += xp;
var newGuildLevelData = new LevelStats(usr.Xp + usr.AwardedXp);
if (oldGlobalLevelData.Level < newGlobalLevelData.Level)
{ {
var xp = item.Sum(x => x.XpAmount); du.LastLevelUp = DateTime.UtcNow;
var first = item.First();
var usr = uow.GetOrCreateUserXpStats(item.Key.GuildId, item.Key.User.Id); if (du.NotifyOnLevelUp != XpNotificationLocation.None)
var du = uow.GetOrCreateUser(item.Key.User);
var globalXp = du.TotalXp;
var oldGlobalLevelData = new LevelStats(globalXp);
var newGlobalLevelData = new LevelStats(globalXp + xp);
var oldGuildLevelData = new LevelStats(usr.Xp + usr.AwardedXp);
usr.Xp += xp;
du.TotalXp += xp;
if (du.Club is not null)
du.Club.Xp += xp;
var newGuildLevelData = new LevelStats(usr.Xp + usr.AwardedXp);
if (oldGlobalLevelData.Level < newGlobalLevelData.Level)
{ {
du.LastLevelUp = DateTime.UtcNow; toNotify.Add((first.Guild, first.Channel, first.User, newGlobalLevelData.Level,
var first = item.First(); du.NotifyOnLevelUp, NotifOf.Global));
if (du.NotifyOnLevelUp != XpNotificationLocation.None)
{
toNotify.Add((first.Guild, first.Channel, first.User, newGlobalLevelData.Level,
du.NotifyOnLevelUp, NotifOf.Global));
}
}
if (oldGuildLevelData.Level < newGuildLevelData.Level)
{
usr.LastLevelUp = DateTime.UtcNow;
//send level up notification
var first = item.First();
if (usr.NotifyOnLevelUp != XpNotificationLocation.None)
{
toNotify.Add((first.Guild, first.Channel, first.User, newGuildLevelData.Level,
usr.NotifyOnLevelUp, NotifOf.Server));
}
//give role
if (!roleRewards.TryGetValue(usr.GuildId, out var rrews))
{
rrews = uow.XpSettingsFor(usr.GuildId).RoleRewards.ToList();
roleRewards.Add(usr.GuildId, rrews);
}
if (!curRewards.TryGetValue(usr.GuildId, out var crews))
{
crews = uow.XpSettingsFor(usr.GuildId).CurrencyRewards.ToList();
curRewards.Add(usr.GuildId, crews);
}
//loop through levels since last level up, so if a high amount of xp is gained, reward are still applied.
for (var i = oldGuildLevelData.Level + 1; i <= newGuildLevelData.Level; i++)
{
var rrew = rrews.FirstOrDefault(x => x.Level == i);
if (rrew is not null)
{
var role = first.User.Guild.GetRole(rrew.RoleId);
if (role is not null)
{
if (rrew.Remove)
_ = first.User.RemoveRoleAsync(role);
else
_ = first.User.AddRoleAsync(role);
}
}
//get currency reward for this level
var crew = crews.FirstOrDefault(x => x.Level == i);
if (crew is not null)
//give the user the reward if it exists
await _cs.AddAsync(item.Key.User.Id, crew.Amount, new("xp", "level-up"));
}
} }
} }
uow.SaveChanges(); if (oldGuildLevelData.Level < newGuildLevelData.Level)
{
usr.LastLevelUp = DateTime.UtcNow;
//send level up notification
var first = item.First();
if (usr.NotifyOnLevelUp != XpNotificationLocation.None)
{
toNotify.Add((first.Guild, first.Channel, first.User, newGuildLevelData.Level,
usr.NotifyOnLevelUp, NotifOf.Server));
}
//give role
if (!roleRewards.TryGetValue(usr.GuildId, out var rrews))
{
rrews = uow.XpSettingsFor(usr.GuildId).RoleRewards.ToList();
roleRewards.Add(usr.GuildId, rrews);
}
if (!curRewards.TryGetValue(usr.GuildId, out var crews))
{
crews = uow.XpSettingsFor(usr.GuildId).CurrencyRewards.ToList();
curRewards.Add(usr.GuildId, crews);
}
//loop through levels since last level up, so if a high amount of xp is gained, reward are still applied.
for (var i = oldGuildLevelData.Level + 1; i <= newGuildLevelData.Level; i++)
{
var rrew = rrews.FirstOrDefault(x => x.Level == i);
if (rrew is not null)
{
var role = first.User.Guild.GetRole(rrew.RoleId);
if (role is not null)
{
if (rrew.Remove)
_ = first.User.RemoveRoleAsync(role);
else
_ = first.User.AddRoleAsync(role);
}
}
//get currency reward for this level
var crew = crews.FirstOrDefault(x => x.Level == i);
if (crew is not null)
//give the user the reward if it exists
await _cs.AddAsync(item.Key.User.Id, crew.Amount, new("xp", "level-up"));
}
}
} }
await toNotify.Select(async x => uow.SaveChanges();
{ }
if (x.NotifOf == NotifOf.Server)
{
if (x.NotifyType == XpNotificationLocation.Dm)
{
await x.User.SendConfirmAsync(_eb,
_strings.GetText(strs.level_up_dm(x.User.Mention,
Format.Bold(x.Level.ToString()),
Format.Bold(x.Guild.ToString() ?? "-")),
x.Guild.Id));
}
else if (x.MessageChannel is not null) // channel
{
await x.MessageChannel.SendConfirmAsync(_eb,
_strings.GetText(strs.level_up_channel(x.User.Mention,
Format.Bold(x.Level.ToString())),
x.Guild.Id));
}
}
else
{
IMessageChannel chan;
if (x.NotifyType == XpNotificationLocation.Dm)
chan = await x.User.CreateDMChannelAsync();
else // channel
chan = x.MessageChannel;
await chan.SendConfirmAsync(_eb, await toNotify.Select(async x =>
_strings.GetText(strs.level_up_global(x.User.Mention, {
if (x.NotifOf == NotifOf.Server)
{
if (x.NotifyType == XpNotificationLocation.Dm)
{
await x.User.SendConfirmAsync(_eb,
_strings.GetText(strs.level_up_dm(x.User.Mention,
Format.Bold(x.Level.ToString()),
Format.Bold(x.Guild.ToString() ?? "-")),
x.Guild.Id));
}
else if (x.MessageChannel is not null) // channel
{
await x.MessageChannel.SendConfirmAsync(_eb,
_strings.GetText(strs.level_up_channel(x.User.Mention,
Format.Bold(x.Level.ToString())), Format.Bold(x.Level.ToString())),
x.Guild.Id)); x.Guild.Id));
} }
}) }
.WhenAll(); else
} {
catch (Exception ex) IMessageChannel chan;
{ if (x.NotifyType == XpNotificationLocation.Dm)
Log.Error(ex, "Error In the XP update loop"); chan = await x.User.CreateDMChannelAsync();
} else // channel
chan = x.MessageChannel;
await chan.SendConfirmAsync(_eb,
_strings.GetText(strs.level_up_global(x.User.Mention,
Format.Bold(x.Level.ToString())),
x.Guild.Id));
}
})
.WhenAll();
}
catch (Exception ex)
{
Log.Error(ex, "Error In the XP update loop");
} }
} }

View File

@@ -23,24 +23,19 @@
<PackageReference Include="AWSSDK.S3" Version="3.7.7.21" /> <PackageReference Include="AWSSDK.S3" Version="3.7.7.21" />
<PackageReference Include="CodeHollow.FeedReader" Version="1.2.4" /> <PackageReference Include="CodeHollow.FeedReader" Version="1.2.4" />
<PackageReference Include="CommandLineParser" Version="2.8.0" /> <PackageReference Include="CommandLineParser" Version="2.8.0" />
<PackageReference Include="Discord.Net" Version="3.3.0" /> <PackageReference Include="Discord.Net" Version="3.4.1" />
<PackageReference Include="CoreCLR-NCalc" Version="2.2.92" /> <PackageReference Include="CoreCLR-NCalc" Version="2.2.92" />
<PackageReference Include="Google.Apis.Urlshortener.v1" Version="1.41.1.138" /> <PackageReference Include="Google.Apis.Urlshortener.v1" Version="1.41.1.138" />
<PackageReference Include="Google.Apis.YouTube.v3" Version="1.55.0.2449" /> <PackageReference Include="Google.Apis.YouTube.v3" Version="1.55.0.2449" />
<PackageReference Include="Google.Apis.Customsearch.v1" Version="1.49.0.2084" /> <PackageReference Include="Google.Apis.Customsearch.v1" Version="1.49.0.2084" />
<PackageReference Include="Google.Protobuf" Version="3.19.4" /> <PackageReference Include="Google.Protobuf" Version="3.19.4" />
<PackageReference Include="Grpc.Net.ClientFactory" Version="2.42.0" /> <PackageReference Include="Grpc.Net.ClientFactory" Version="2.41.0" />
<PackageReference Include="Grpc.Tools" Version="2.43.0"> <PackageReference Include="Grpc.Tools" Version="2.41.1">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Html2Markdown" Version="5.0.2.561" /> <PackageReference Include="Html2Markdown" Version="5.0.2.561" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="6.0.1" /> <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="6.0.0" />
@@ -59,10 +54,22 @@
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta0010" /> <PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta0010" />
<PackageReference Include="StackExchange.Redis" Version="2.2.88" /> <PackageReference Include="StackExchange.Redis" Version="2.2.88" />
<PackageReference Include="YamlDotNet" Version="11.2.1" /> <PackageReference Include="YamlDotNet" Version="11.2.1" />
<PackageReference Include="linq2db.EntityFrameworkCore" Version="6.6.1" />
<PackageReference Include="Humanizer" Version="2.14.1" /> <PackageReference Include="Humanizer" Version="2.14.1" />
<PackageReference Include="JetBrains.Annotations" Version="2021.3.0" /> <PackageReference Include="JetBrains.Annotations" Version="2021.3.0" />
<!-- Db-related packages -->
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="linq2db.EntityFrameworkCore" Version="6.6.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.2" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.3" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="6.0.1" />
<!-- Remove this when static abstract interface members support is released --> <!-- Remove this when static abstract interface members support is released -->
<PackageReference Include="System.Runtime.Experimental" Version="6.0.0" /> <PackageReference Include="System.Runtime.Experimental" Version="6.0.0" />
@@ -71,6 +78,12 @@
<!-- Used by stream notifications --> <!-- Used by stream notifications -->
<PackageReference Include="TwitchLib.Api" Version="3.4.1" /> <PackageReference Include="TwitchLib.Api" Version="3.4.1" />
<!-- Uncomment to check for disposable issues -->
<!-- <PackageReference Include="IDisposableAnalyzers" Version="4.0.2">-->
<!-- <PrivateAssets>all</PrivateAssets>-->
<!-- <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>-->
<!-- </PackageReference>-->
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -101,10 +114,15 @@
<Version Condition=" '$(VersionSuffix)' != '' ">$(VersionPrefix).$(VersionSuffix)</Version> <Version Condition=" '$(VersionSuffix)' != '' ">$(VersionPrefix).$(VersionSuffix)</Version>
<Version Condition=" '$(Version)' == '' ">$(VersionPrefix)</Version> <Version Condition=" '$(Version)' == '' ">$(VersionPrefix)</Version>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'GlobalNadeko' "> <PropertyGroup Condition=" '$(Configuration)' == 'GlobalNadeko' ">
<DefineConstants>$(DefineConstants);GLOBAL_NADEKO</DefineConstants> <!-- Define trace doesn't seem to affect the build at all so I had to remove $(DefineConstants)-->
<DefineTrace>false</DefineTrace>
<DefineConstants>GLOBAL_NADEKO</DefineConstants>
<NoWarn>$(NoWarn);CS1573;CS1591</NoWarn> <NoWarn>$(NoWarn);CS1573;CS1591</NoWarn>
<Optimize>true</Optimize>
<DebugType>portable</DebugType>
<DebugSymbols>false</DebugSymbols>
</PropertyGroup> </PropertyGroup>
<!-- TODO: Remove this when the conflict issue in System.Runtime.Experimental is fixed --> <!-- TODO: Remove this when the conflict issue in System.Runtime.Experimental is fixed -->

View File

@@ -14,7 +14,7 @@ public class CurrencyService : ICurrencyService, INService
public Task<IWallet> GetWalletAsync(ulong userId, CurrencyType type = CurrencyType.Default) public Task<IWallet> GetWalletAsync(ulong userId, CurrencyType type = CurrencyType.Default)
{ {
if (type == CurrencyType.Default) if (type == CurrencyType.Default)
return Task.FromResult<IWallet>(new DefaultWallet(userId, _db.GetDbContext())); return Task.FromResult<IWallet>(new DefaultWallet(userId, _db));
throw new ArgumentOutOfRangeException(nameof(type)); throw new ArgumentOutOfRangeException(nameof(type));
} }
@@ -27,14 +27,12 @@ public class CurrencyService : ICurrencyService, INService
{ {
if (type == CurrencyType.Default) if (type == CurrencyType.Default)
{ {
await using var ctx = _db.GetDbContext();
foreach (var userId in userIds) foreach (var userId in userIds)
{ {
var wallet = new DefaultWallet(userId, ctx); var wallet = await GetWalletAsync(userId);
await wallet.Add(amount, txData); await wallet.Add(amount, txData);
} }
await ctx.SaveChangesAsync();
return; return;
} }
@@ -58,6 +56,7 @@ public class CurrencyService : ICurrencyService, INService
? du.CurrencyAmount - amount ? du.CurrencyAmount - amount
: 0 : 0
}); });
await ctx.SaveChangesAsync();
return; return;
} }
@@ -69,7 +68,7 @@ public class CurrencyService : ICurrencyService, INService
long amount, long amount,
TxData txData) TxData txData)
{ {
await using var wallet = await GetWalletAsync(userId); var wallet = await GetWalletAsync(userId);
await wallet.Add(amount, txData); await wallet.Add(amount, txData);
} }
@@ -78,7 +77,7 @@ public class CurrencyService : ICurrencyService, INService
long amount, long amount,
TxData txData) TxData txData)
{ {
await using var wallet = await GetWalletAsync(user.Id); var wallet = await GetWalletAsync(user.Id);
await wallet.Add(amount, txData); await wallet.Add(amount, txData);
} }
@@ -87,7 +86,7 @@ public class CurrencyService : ICurrencyService, INService
long amount, long amount,
TxData txData) TxData txData)
{ {
await using var wallet = await GetWalletAsync(userId); var wallet = await GetWalletAsync(userId);
return await wallet.Take(amount, txData); return await wallet.Take(amount, txData);
} }
@@ -96,22 +95,7 @@ public class CurrencyService : ICurrencyService, INService
long amount, long amount,
TxData txData) TxData txData)
{ {
await using var wallet = await GetWalletAsync(user.Id); var wallet = await GetWalletAsync(user.Id);
return await wallet.Take(amount, txData); return await wallet.Take(amount, txData);
} }
public async Task<bool> TransferAsync(
ulong fromId,
ulong toId,
long amount,
string fromName,
string note)
{
await using var fromWallet = await GetWalletAsync(fromId);
await using var toWallet = await GetWalletAsync(toId);
var extra = new TxData("gift", fromName, note, fromId);
return await fromWallet.Transfer(amount, toWallet, extra);
}
} }

View File

@@ -0,0 +1,29 @@
using NadekoBot.Services.Currency;
namespace NadekoBot.Services;
public static class CurrencyServiceExtensions
{
public static async Task<long> GetBalanceAsync(this ICurrencyService cs, ulong userId)
{
var wallet = await cs.GetWalletAsync(userId);
return await wallet.GetBalance();
}
// todo transfer should be a transaction
public static async Task<bool> TransferAsync(
this ICurrencyService cs,
ulong fromId,
ulong toId,
long amount,
string fromName,
string note)
{
var fromWallet = await cs.GetWalletAsync(fromId);
var toWallet = await cs.GetWalletAsync(toId);
var extra = new TxData("gift", fromName, note, fromId);
return await fromWallet.Transfer(amount, toWallet, extra);
}
}

View File

@@ -7,48 +7,54 @@ namespace NadekoBot.Services.Currency;
public class DefaultWallet : IWallet public class DefaultWallet : IWallet
{ {
private readonly DbService _db;
public ulong UserId { get; } public ulong UserId { get; }
private readonly NadekoContext _ctx; public DefaultWallet(ulong userId, DbService db)
public DefaultWallet(ulong userId, NadekoContext ctx)
{ {
UserId = userId; UserId = userId;
_ctx = ctx; _db = db;
} }
public Task<long> GetBalance() public async Task<long> GetBalance()
=> _ctx.DiscordUser {
.ToLinqToDBTable() await using var ctx = _db.GetDbContext();
.Where(x => x.UserId == UserId) return await ctx.DiscordUser
.Select(x => x.CurrencyAmount) .ToLinqToDBTable()
.FirstOrDefaultAsync(); .Where(x => x.UserId == UserId)
.Select(x => x.CurrencyAmount)
.FirstOrDefaultAsync();
}
public async Task<bool> Take(long amount, TxData txData) public async Task<bool> Take(long amount, TxData txData)
{ {
if (amount < 0) if (amount < 0)
throw new ArgumentOutOfRangeException(nameof(amount), "Amount to take must be non negative."); throw new ArgumentOutOfRangeException(nameof(amount), "Amount to take must be non negative.");
var changed = await _ctx.DiscordUser await using var ctx = _db.GetDbContext();
.Where(x => x.UserId == UserId && x.CurrencyAmount >= amount)
.UpdateAsync(x => new() var changed = await ctx.DiscordUser
{ .Where(x => x.UserId == UserId && x.CurrencyAmount >= amount)
CurrencyAmount = x.CurrencyAmount - amount .UpdateAsync(x => new()
}); {
CurrencyAmount = x.CurrencyAmount - amount
});
if (changed == 0) if (changed == 0)
return false; return false;
await _ctx.CreateLinqToDbContext() await ctx
.InsertAsync(new CurrencyTransaction() .GetTable<CurrencyTransaction>()
{ .InsertAsync(() => new()
Amount = -amount, {
Note = txData.Note, Amount = -amount,
UserId = UserId, Note = txData.Note,
Type = txData.Type, UserId = UserId,
Extra = txData.Extra, Type = txData.Type,
OtherId = txData.OtherId Extra = txData.Extra,
}); OtherId = txData.OtherId,
DateAdded = DateTime.UtcNow
});
return true; return true;
} }
@@ -58,9 +64,11 @@ public class DefaultWallet : IWallet
if (amount <= 0) if (amount <= 0)
throw new ArgumentOutOfRangeException(nameof(amount), "Amount must be greater than 0."); throw new ArgumentOutOfRangeException(nameof(amount), "Amount must be greater than 0.");
await using (var tran = await _ctx.Database.BeginTransactionAsync()) await using var ctx = _db.GetDbContext();
await using (var tran = await ctx.Database.BeginTransactionAsync())
{ {
var changed = await _ctx.DiscordUser var changed = await ctx.DiscordUser
.Where(x => x.UserId == UserId) .Where(x => x.UserId == UserId)
.UpdateAsync(x => new() .UpdateAsync(x => new()
{ {
@@ -69,7 +77,7 @@ public class DefaultWallet : IWallet
if (changed == 0) if (changed == 0)
{ {
await _ctx.DiscordUser await ctx.DiscordUser
.ToLinqToDBTable() .ToLinqToDBTable()
.Value(x => x.UserId, UserId) .Value(x => x.UserId, UserId)
.Value(x => x.Username, "Unknown") .Value(x => x.Username, "Unknown")
@@ -81,29 +89,16 @@ public class DefaultWallet : IWallet
await tran.CommitAsync(); await tran.CommitAsync();
} }
var ct = new CurrencyTransaction() await ctx.GetTable<CurrencyTransaction>()
{ .InsertAsync(() => new()
Amount = amount, {
UserId = UserId, Amount = amount,
Note = txData.Note, UserId = UserId,
Type = txData.Type, Note = txData.Note,
Extra = txData.Extra, Type = txData.Type,
OtherId = txData.OtherId Extra = txData.Extra,
}; OtherId = txData.OtherId,
DateAdded = DateTime.UtcNow
await _ctx.CreateLinqToDbContext() });
.InsertAsync(ct);
}
public void Dispose()
{
_ctx.SaveChanges();
_ctx.Dispose();
}
public async ValueTask DisposeAsync()
{
await _ctx.SaveChangesAsync();
await _ctx.DisposeAsync();
} }
} }

View File

@@ -38,11 +38,4 @@ public interface ICurrencyService
IUser user, IUser user,
long amount, long amount,
TxData txData); TxData txData);
Task<bool> TransferAsync(
ulong from,
ulong to,
long amount,
string fromName,
string note);
} }

View File

@@ -1,6 +1,6 @@
namespace NadekoBot.Services.Currency; namespace NadekoBot.Services.Currency;
public interface IWallet : IDisposable, IAsyncDisposable public interface IWallet
{ {
public ulong UserId { get; } public ulong UserId { get; }

View File

@@ -32,10 +32,9 @@ public class DbService
using var context = new NadekoContext(_options); using var context = new NadekoContext(_options);
if (context.Database.GetPendingMigrations().Any()) if (context.Database.GetPendingMigrations().Any())
{ {
var mContext = new NadekoContext(_migrateOptions); using var mContext = new NadekoContext(_migrateOptions);
mContext.Database.Migrate(); mContext.Database.Migrate();
mContext.SaveChanges(); mContext.SaveChanges();
mContext.Dispose();
} }
context.Database.ExecuteSqlRaw("PRAGMA journal_mode=WAL"); context.Database.ExecuteSqlRaw("PRAGMA journal_mode=WAL");

View File

@@ -32,6 +32,7 @@ public sealed class BotCredsProvider : IBotCredsProvider
private readonly object _reloadLock = new(); private readonly object _reloadLock = new();
private readonly IDisposable _changeToken;
public BotCredsProvider(int? totalShards = null) public BotCredsProvider(int? totalShards = null)
{ {
@@ -52,9 +53,9 @@ public sealed class BotCredsProvider : IBotCredsProvider
_config = new ConfigurationBuilder().AddYamlFile(CredsPath, false, true) _config = new ConfigurationBuilder().AddYamlFile(CredsPath, false, true)
.AddEnvironmentVariables("NadekoBot_") .AddEnvironmentVariables("NadekoBot_")
.Build(); .Build();
#if !GLOBAL_NADEKO
ChangeToken.OnChange(() => _config.GetReloadToken(), Reload); _changeToken = ChangeToken.OnChange(() => _config.GetReloadToken(), Reload);
#endif
Reload(); Reload();
} }

View File

@@ -96,7 +96,8 @@ public class RemoteGrpcCoordinator : ICoordinator, IReadyExecutor
break; break;
} }
await Task.Delay(22500); Log.Information("Coordinator is restarting gracefully. Waiting...");
await Task.Delay(30_000);
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@@ -5,9 +5,9 @@ using System.Diagnostics;
namespace NadekoBot.Services; namespace NadekoBot.Services;
public class StatsService : IStatsService, IReadyExecutor, INService, IDisposable public sealed class StatsService : IStatsService, IReadyExecutor, INService, IDisposable
{ {
public const string BOT_VERSION = "4.0.3"; public const string BOT_VERSION = "4.0.5";
public string Author public string Author
=> "Kwoth#2452"; => "Kwoth#2452";
@@ -133,11 +133,16 @@ public class StatsService : IStatsService, IReadyExecutor, INService, IDisposabl
}; };
} }
public async Task OnReadyAsync() private void InitializeChannelCount()
{ {
var guilds = _client.Guilds; var guilds = _client.Guilds;
textChannels = guilds.Sum(g => g.Channels.Count(cx => cx is ITextChannel)); textChannels = guilds.Sum(static g => g.Channels.Count(static cx => cx is ITextChannel));
voiceChannels = guilds.Sum(g => g.Channels.Count(cx => cx is IVoiceChannel)); voiceChannels = guilds.Sum(static g => g.Channels.Count(static cx => cx is IVoiceChannel));
}
public async Task OnReadyAsync()
{
InitializeChannelCount();
using var timer = new PeriodicTimer(TimeSpan.FromHours(1)); using var timer = new PeriodicTimer(TimeSpan.FromHours(1));
do do
@@ -189,4 +194,4 @@ public class StatsService : IStatsService, IReadyExecutor, INService, IDisposabl
_currentProcess.Dispose(); _currentProcess.Dispose();
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
} }

View File

@@ -1,87 +1,87 @@
// Copyright (c) .NET Foundation. All rights reserved. // // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// https://github.com/aspnet/Common/blob/dev/shared/Microsoft.Extensions.Process.Sources/ProcessHelper.cs // // https://github.com/aspnet/Common/blob/dev/shared/Microsoft.Extensions.Process.Sources/ProcessHelper.cs
//
using System.Diagnostics; // using System.Diagnostics;
using System.Runtime.InteropServices; // using System.Runtime.InteropServices;
//
namespace NadekoBot.Extensions; // namespace NadekoBot.Extensions;
//
public static class ProcessExtensions // public static class ProcessExtensions
{ // {
private static readonly bool _isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); // private static readonly bool _isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
private static readonly TimeSpan _defaultTimeout = TimeSpan.FromSeconds(10); // private static readonly TimeSpan _defaultTimeout = TimeSpan.FromSeconds(10);
//
public static void KillTree(this Process process) // public static void KillTree(this Process process)
=> process.KillTree(_defaultTimeout); // => process.KillTree(_defaultTimeout);
//
public static void KillTree(this Process process, TimeSpan timeout) // public static void KillTree(this Process process, TimeSpan timeout)
{ // {
if (_isWindows) // if (_isWindows)
RunProcessAndWaitForExit("taskkill", $"/T /F /PID {process.Id}", timeout, out _); // RunProcessAndWaitForExit("taskkill", $"/T /F /PID {process.Id}", timeout, out _);
else // else
{ // {
var children = new HashSet<int>(); // var children = new HashSet<int>();
GetAllChildIdsUnix(process.Id, children, timeout); // GetAllChildIdsUnix(process.Id, children, timeout);
foreach (var childId in children) // foreach (var childId in children)
KillProcessUnix(childId, timeout); // KillProcessUnix(childId, timeout);
//
KillProcessUnix(process.Id, timeout); // KillProcessUnix(process.Id, timeout);
} // }
} // }
//
private static void GetAllChildIdsUnix(int parentId, ISet<int> children, TimeSpan timeout) // private static void GetAllChildIdsUnix(int parentId, ISet<int> children, TimeSpan timeout)
{ // {
var exitCode = RunProcessAndWaitForExit("pgrep", $"-P {parentId}", timeout, out var stdout); // var exitCode = RunProcessAndWaitForExit("pgrep", $"-P {parentId}", timeout, out var stdout);
//
if (exitCode == 0 && !string.IsNullOrEmpty(stdout)) // if (exitCode == 0 && !string.IsNullOrEmpty(stdout))
{ // {
using var reader = new StringReader(stdout); // using var reader = new StringReader(stdout);
while (true) // while (true)
{ // {
var text = reader.ReadLine(); // var text = reader.ReadLine();
if (text is null) // if (text is null)
return; // return;
//
if (int.TryParse(text, out var id)) // if (int.TryParse(text, out var id))
{ // {
children.Add(id); // children.Add(id);
// Recursively get the children // // Recursively get the children
GetAllChildIdsUnix(id, children, timeout); // GetAllChildIdsUnix(id, children, timeout);
} // }
} // }
} // }
} // }
//
private static void KillProcessUnix(int processId, TimeSpan timeout) // private static void KillProcessUnix(int processId, TimeSpan timeout)
=> RunProcessAndWaitForExit("kill", $"-TERM {processId}", timeout, out _); // => RunProcessAndWaitForExit("kill", $"-TERM {processId}", timeout, out _);
//
private static int RunProcessAndWaitForExit( // private static int RunProcessAndWaitForExit(
string fileName, // string fileName,
string arguments, // string arguments,
TimeSpan timeout, // TimeSpan timeout,
out string? stdout) // out string? stdout)
{ // {
stdout = null; // stdout = null;
//
var startInfo = new ProcessStartInfo // var startInfo = new ProcessStartInfo
{ // {
FileName = fileName, // FileName = fileName,
Arguments = arguments, // Arguments = arguments,
RedirectStandardOutput = true, // RedirectStandardOutput = true,
UseShellExecute = false // UseShellExecute = false
}; // };
//
var process = Process.Start(startInfo); // using var process = Process.Start(startInfo);
//
if (process is null) // if (process is null)
return -1; // return -1;
//
if (process.WaitForExit((int)timeout.TotalMilliseconds)) // if (process.WaitForExit((int)timeout.TotalMilliseconds))
stdout = process.StandardOutput.ReadToEnd(); // stdout = process.StandardOutput.ReadToEnd();
else // else
process.Kill(); // process.Kill();
//
return process.ExitCode; // return process.ExitCode;
} // }
} // }

View File

@@ -21,7 +21,7 @@ public static class Rgba32Extensions
var xOffset = 0; var xOffset = 0;
for (var i = 0; i < imgArray.Count; i++) for (var i = 0; i < imgArray.Count; i++)
{ {
var frame = imgArray[i].Frames.CloneFrame(frameNumber % imgArray[i].Frames.Count); using var frame = imgArray[i].Frames.CloneFrame(frameNumber % imgArray[i].Frames.Count);
var offset = xOffset; var offset = xOffset;
imgFrame.Mutate(x => x.DrawImage(frame, new(offset, 0), new GraphicsOptions())); imgFrame.Mutate(x => x.DrawImage(frame, new(offset, 0), new GraphicsOptions()));
xOffset += imgArray[i].Bounds().Width; xOffset += imgArray[i].Bounds().Width;

View File

@@ -89,7 +89,7 @@ public static class StringExtensions
public static async Task<Stream> ToStream(this string str) public static async Task<Stream> ToStream(this string str)
{ {
var ms = new MemoryStream(); var ms = new MemoryStream();
var sw = new StreamWriter(ms); await using var sw = new StreamWriter(ms);
await sw.WriteAsync(str); await sw.WriteAsync(str);
await sw.FlushAsync(); await sw.FlushAsync();
ms.Position = 0; ms.Position = 0;

View File

@@ -202,9 +202,9 @@ exprlist:
Running the command in DM will list global custom reactions, while running it in a server will list server custom reactions. Running the command in DM will list global custom reactions, while running it in a server will list server custom reactions.
Shows enabled settings, followed by id, followed by the trigger. Shows enabled settings, followed by id, followed by the trigger.
**Settings:** **Settings:**
• 🗯️ Triggered if trigger matches any word (`{0}h {0}crca`) • 🗯️ Triggered if trigger matches any word (`{0}h {0}exca`)
• ✉️ Response will be DMed (`{0}h {0}crdm`) • ✉️ Response will be DMed (`{0}h {0}exdm`)
• ❌ Trigger will be deleted (`{0}h {0}crad`) • ❌ Trigger will be deleted (`{0}h {0}exad`)
args: args:
- "1" - "1"
- "all" - "all"
@@ -889,7 +889,7 @@ deleteplaylist:
args: args:
- "5" - "5"
queueautoplay: queueautoplay:
desc: "Toggles autoplay - When the song is finished, automatically queue a related Youtube song. (Works only for Youtube songs and when queue is empty)" desc: "Toggles autoplay - When the song is finished, automatically queue a related Youtube song. (Works only for Youtube songs)"
args: args:
- "" - ""
streamadd: streamadd:
@@ -2154,4 +2154,4 @@ showembed:
deleteemptyservers: deleteemptyservers:
desc: "Deletes all servers in which the bot is the only member." desc: "Deletes all servers in which the bot is the only member."
args: args:
- "" - ""