mirror of
				https://gitlab.com/Kwoth/nadekobot.git
				synced 2025-11-04 00:34:26 -05:00 
			
		
		
		
	Implemented owner-only command .cacheusers to force users into the database, closes #425
				
					
				
			This commit is contained in:
		@@ -4,11 +4,60 @@ using LinqToDB.EntityFrameworkCore;
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
using NadekoBot.Db.Models;
 | 
			
		||||
using NadekoBot.Services.Database;
 | 
			
		||||
using System.Collections.Immutable;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Db;
 | 
			
		||||
 | 
			
		||||
public static class DiscordUserExtensions
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Adds the specified <paramref name="users"/> to the database. If a database user with placeholder name
 | 
			
		||||
    /// and discriminator is present in <paramref name="users"/>, their name and discriminator get updated accordingly.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="ctx">This database context.</param>
 | 
			
		||||
    /// <param name="users">The users to add or update in the database.</param>
 | 
			
		||||
    /// <returns>A tuple with the amount of new users added and old users updated.</returns>
 | 
			
		||||
    public static async Task<(long UsersAdded, long UsersUpdated)> RefreshUsersAsync(this NadekoContext ctx, List<IUser> users)
 | 
			
		||||
    {
 | 
			
		||||
        var presentDbUsers = await ctx.DiscordUser
 | 
			
		||||
            .Select(x => new { x.UserId, x.Username, x.Discriminator })
 | 
			
		||||
            .Where(x => users.Select(y => y.Id).Contains(x.UserId))
 | 
			
		||||
            .ToArrayAsyncEF();
 | 
			
		||||
 | 
			
		||||
        var usersToAdd = users
 | 
			
		||||
            .Where(x => !presentDbUsers.Select(x => x.UserId).Contains(x.Id))
 | 
			
		||||
            .Select(x => new DiscordUser()
 | 
			
		||||
            {
 | 
			
		||||
                UserId = x.Id,
 | 
			
		||||
                AvatarId = x.AvatarId,
 | 
			
		||||
                Username = x.Username,
 | 
			
		||||
                Discriminator = x.Discriminator
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        var added = (await ctx.BulkCopyAsync(usersToAdd)).RowsCopied;
 | 
			
		||||
        var toUpdateUserIds = presentDbUsers
 | 
			
		||||
            .Where(x => x.Username == "Unknown" && x.Discriminator == "????")
 | 
			
		||||
            .Select(x => x.UserId)
 | 
			
		||||
            .ToArray();
 | 
			
		||||
 | 
			
		||||
        foreach (var user in users.Where(x => toUpdateUserIds.Contains(x.Id)))
 | 
			
		||||
        {
 | 
			
		||||
            await ctx.DiscordUser
 | 
			
		||||
                .Where(x => x.UserId == user.Id)
 | 
			
		||||
                .UpdateAsync(x => new DiscordUser()
 | 
			
		||||
                {
 | 
			
		||||
                    Username = user.Username,
 | 
			
		||||
                    Discriminator = user.Discriminator,
 | 
			
		||||
 | 
			
		||||
                    // .award tends to set AvatarId and DateAdded to NULL, so account for that.
 | 
			
		||||
                    AvatarId = user.AvatarId,
 | 
			
		||||
                    DateAdded = x.DateAdded ?? DateTime.UtcNow
 | 
			
		||||
                });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return (added, toUpdateUserIds.Length);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static Task<DiscordUser> GetByUserIdAsync(
 | 
			
		||||
        this IQueryable<DiscordUser> set,
 | 
			
		||||
        ulong userId)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using Nadeko.Medusa;
 | 
			
		||||
using NadekoBot.Db;
 | 
			
		||||
using NadekoBot.Modules.Administration.Services;
 | 
			
		||||
using NadekoBot.Services.Database.Models;
 | 
			
		||||
 | 
			
		||||
@@ -22,19 +23,53 @@ public partial class Administration
 | 
			
		||||
        private readonly IBotStrings _strings;
 | 
			
		||||
        private readonly IMedusaLoaderService _medusaLoader;
 | 
			
		||||
        private readonly ICoordinator _coord;
 | 
			
		||||
        private readonly DbService _db;
 | 
			
		||||
 | 
			
		||||
        public SelfCommands(
 | 
			
		||||
            DiscordSocketClient client,
 | 
			
		||||
            DbService db,
 | 
			
		||||
            IBotStrings strings,
 | 
			
		||||
            ICoordinator coord,
 | 
			
		||||
            IMedusaLoaderService medusaLoader)
 | 
			
		||||
        {
 | 
			
		||||
            _client = client;
 | 
			
		||||
            _db = db;
 | 
			
		||||
            _strings = strings;
 | 
			
		||||
            _coord = coord;
 | 
			
		||||
            _medusaLoader = medusaLoader;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        [Cmd]
 | 
			
		||||
        [RequireContext(ContextType.Guild)]
 | 
			
		||||
        [OwnerOnly]
 | 
			
		||||
        public Task CacheUsers()
 | 
			
		||||
            => CacheUsers(ctx.Guild);
 | 
			
		||||
 | 
			
		||||
        [Cmd]
 | 
			
		||||
        [OwnerOnly]
 | 
			
		||||
        public async Task CacheUsers(IGuild guild)
 | 
			
		||||
        {
 | 
			
		||||
            var downloadUsersTask = guild.DownloadUsersAsync();
 | 
			
		||||
            var message = await ReplyPendingLocalizedAsync(strs.cache_users_pending);
 | 
			
		||||
            using var dbContext = _db.GetDbContext();
 | 
			
		||||
 | 
			
		||||
            await downloadUsersTask;
 | 
			
		||||
 | 
			
		||||
            var users = (await guild.GetUsersAsync(CacheMode.CacheOnly))
 | 
			
		||||
                .Cast<IUser>()
 | 
			
		||||
                .ToList();
 | 
			
		||||
 | 
			
		||||
            var (added, updated) = await dbContext.RefreshUsersAsync(users);
 | 
			
		||||
 | 
			
		||||
            await message.ModifyAsync(x =>
 | 
			
		||||
                x.Embed = _eb.Create()
 | 
			
		||||
                    .WithDescription(GetText(strs.cache_users_done(added, updated)))
 | 
			
		||||
                    .WithOkColor()
 | 
			
		||||
                    .Build()
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Cmd]
 | 
			
		||||
        [OwnerOnly]
 | 
			
		||||
        public async Task DoAs(IUser user, [Leftover] string message)
 | 
			
		||||
 
 | 
			
		||||
@@ -1390,4 +1390,6 @@ autopublish:
 | 
			
		||||
  - autopublish
 | 
			
		||||
doas:
 | 
			
		||||
  - doas
 | 
			
		||||
  - execas 
 | 
			
		||||
  - execas
 | 
			
		||||
cacheusers:
 | 
			
		||||
  - cacheusers
 | 
			
		||||
@@ -2363,3 +2363,8 @@ doas:
 | 
			
		||||
  desc: "Execute the command as if you were the target user. Requires bot ownership and server administrator permission."
 | 
			
		||||
  args:
 | 
			
		||||
    - "@Thief .give all @Admin"
 | 
			
		||||
cacheusers:
 | 
			
		||||
  desc: Caches users of a Discord server and saves them to the database.
 | 
			
		||||
  args:
 | 
			
		||||
    - ""
 | 
			
		||||
    - "serverId"
 | 
			
		||||
@@ -1058,5 +1058,7 @@
 | 
			
		||||
  "sticker_missing_name": "Please specify a name for the sticker.",
 | 
			
		||||
  "thread_deleted": "Thread Deleted",
 | 
			
		||||
  "thread_created": "Thread Created",
 | 
			
		||||
  "supported_languages": "Supported Languages"
 | 
			
		||||
  "supported_languages": "Supported Languages",
 | 
			
		||||
  "cache_users_pending": "Updating users, please wait...",
 | 
			
		||||
  "cache_users_done": "{0} users were added and {1} users were updated."
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user