public async Task <Dictionary <KeyValuePair <string, ulong>, DiscordSnapshotInfo> > GetLatestSnapshots(ulong ownerId, DateTime?filter = null) { Dictionary <KeyValuePair <string, ulong>, DiscordSnapshotInfo> snapshots = new Dictionary <KeyValuePair <string, ulong>, DiscordSnapshotInfo>(); string ownerPath = Path.Combine(Core.Configuration.SnapshotsFolder, $"{ownerId}"); if (!Directory.Exists(ownerPath)) { return(snapshots); } foreach (var guildsDirectory in Directory.GetDirectories(ownerPath)) { string snapshotHistoryPath = Path.Combine(guildsDirectory, "history.json"); if (!File.Exists(snapshotHistoryPath)) { return(snapshots); } DiscordSnapshotHistory history = JsonConvert.DeserializeObject <DiscordSnapshotHistory>( await File.ReadAllTextAsync(snapshotHistoryPath)); if (filter != null) { history.History.RemoveAll(k => k.Value > filter); } if (history.History.Count == 0) { continue; } history.History.Sort((c, k) => { if (c.Value < k.Value) { return(1); } if (c.Value > k.Value) { return(-1); } return(0); }); string latestSnapshotPath = Path.Combine(guildsDirectory, $"{history.History.First().Key}.json"); if (!File.Exists(latestSnapshotPath)) { continue; } DiscordSnapshotInfo info = JsonConvert.DeserializeObject <DiscordSnapshotInfo>(await File.ReadAllTextAsync(latestSnapshotPath)); snapshots.Add(new KeyValuePair <string, ulong>(info.GuildName, info.GuildId), info); } return(snapshots); }
public async Task TakeSnapshot(CommandContext ctx) { if (ctx.Guild == null) { await ctx.RespondAsync($"**This command has to be ran inside a Discord server!**"); return; } if (ctx.Guild.Owner.Id != ctx.User.Id) { await ctx.RespondAsync($"**You must be the owner of this server to run this command!**"); return; } if (!Core.Configuration.AllowedUsers.Contains(ctx.User.Id)) { await ctx.RespondAsync($"**You're not allowed to use this bot!**"); return; } DiscordSnapshotInfo snapshot = await Core.DiscordBot.PerformSnapshot(ctx.Guild); string successText = $"Done! Snapshot Id: **{snapshot.SnapshotId}**\n"; successText += $"If this backup is important to you, you should write it down somewhere so that you do not forget it.\n"; successText += "At all times, you can always go back to this snapshot using the command you know and the ID of the important backup you just did.\n"; successText += "Automatic backups will keep taking place, but if you think that this is a stable backup, please write it down somewhere."; await ctx.RespondAsync(successText); }
public async Task <DiscordSnapshotInfo> PushSnapshot(DiscordSnapshotInfo snapshotInfo) { snapshotInfo.SnapshotId = Guid.NewGuid(); string snapshotDir = Path.Combine(Core.Configuration.SnapshotsFolder, $"{snapshotInfo.OwnerId}", $"{snapshotInfo.GuildId}"); string snapshotPath = Path.Combine(snapshotDir, $"{snapshotInfo.SnapshotId.ToString()}.json"); string snapshotHistoryPath = Path.Combine(snapshotDir, "history.json"); string snapshotData = JsonConvert.SerializeObject(snapshotInfo, Formatting.Indented); Directory.CreateDirectory(Path.GetDirectoryName(snapshotPath)); await File.WriteAllTextAsync(snapshotPath, snapshotData); DiscordSnapshotHistory history; if (!File.Exists(snapshotHistoryPath)) { history = new DiscordSnapshotHistory() { History = new List <KeyValuePair <Guid, DateTime> >() }; } else { string historyText = await File.ReadAllTextAsync(snapshotHistoryPath); history = JsonConvert.DeserializeObject <DiscordSnapshotHistory>(historyText); } history.History.Add(new KeyValuePair <Guid, DateTime>(snapshotInfo.SnapshotId, snapshotInfo.TakenAt)); await File.WriteAllTextAsync(snapshotHistoryPath, JsonConvert.SerializeObject(history, Formatting.Indented)); return(snapshotInfo); }
public async Task <DiscordSnapshotInfo> PerformSnapshot(DiscordGuild guild) { if (guildDisabledSnapshots.Contains(guild.Id)) { return(null); } Console.WriteLine($"[Snapshot] Performing guild {guild.Name} snapshot", Color.DarkCyan); DiscordSnapshotInfo snapshotInfo = new DiscordSnapshotInfo(); snapshotInfo.RolesSnapshot = new List <DiscordRoleSnapshotInfo>(); snapshotInfo.ChannelsSnapshot = new List <DiscordChannelSnapshotInfo>(); snapshotInfo.GuildName = guild.Name; snapshotInfo.GuildId = guild.Id; var roles = guild.Roles; Dictionary <ulong, Guid> rolesMapping = new Dictionary <ulong, Guid>(); foreach (var role in roles) { Console.WriteLine($"[Snapshot] [{guild.Name}] Performing role {role.Name} snapshot", Color.DarkCyan); DiscordRoleSnapshotInfo roleSnapshot = new DiscordRoleSnapshotInfo(); roleSnapshot.InternalRoleId = Guid.NewGuid(); roleSnapshot.RoleName = role.Name; roleSnapshot.RoleColor = role.Color.Value; roleSnapshot.IsSeparated = role.IsHoisted; roleSnapshot.IsMentionable = role.IsMentionable; roleSnapshot.Hierarchy = role.Position; roleSnapshot.Permissions = (ulong)role.Permissions; roleSnapshot.IsEveryone = role.Name == "@everyone"; rolesMapping.Add(role.Id, roleSnapshot.InternalRoleId); snapshotInfo.RolesSnapshot.Add(roleSnapshot); } var channels = await guild.GetChannelsAsync(); Dictionary <ulong, Guid> channelMapping = new Dictionary <ulong, Guid>(); foreach (var channel in channels) { channelMapping.Add(channel.Id, Guid.NewGuid()); } foreach (var channel in channels) { Console.WriteLine($"[Snapshot] [{guild.Name}] Performing channel {channel.Name} snapshot", Color.DarkCyan); DiscordChannelSnapshotInfo channelSnapshot = new DiscordChannelSnapshotInfo(); channelSnapshot.InternalChannelId = channelMapping[channel.Id]; channelSnapshot.Topic = channel.Topic; channelSnapshot.ChannelName = channel.Name; channelSnapshot.ChannelPosition = channel.Position; channelSnapshot.IsCategory = channel.Type == ChannelType.Category; channelSnapshot.IsText = channel.Type == ChannelType.Text; channelSnapshot.IsVoice = channel.Type == ChannelType.Voice; if (channel.ParentId.HasValue) { channelSnapshot.ParentInternalChannelId = channelMapping[channel.ParentId.Value]; } else { channelSnapshot.ParentInternalChannelId = null; } channelSnapshot.ChannelRolesSnapshot = new List <DiscordChannelRoleSnapshotInfo>(); foreach (var overwrite in channel.PermissionOverwrites) { if (overwrite.Type != "role") { continue; } DiscordChannelRoleSnapshotInfo channelRoleSnapshot = new DiscordChannelRoleSnapshotInfo(); channelRoleSnapshot.InternalRoleId = rolesMapping[overwrite.Id]; channelRoleSnapshot.Allow = (ulong)overwrite.Allow; channelRoleSnapshot.Deny = (ulong)overwrite.Deny; channelSnapshot.ChannelRolesSnapshot.Add(channelRoleSnapshot); } snapshotInfo.ChannelsSnapshot.Add(channelSnapshot); } snapshotInfo.OwnerId = guild.Owner.Id; snapshotInfo.GuildId = guild.Id; snapshotInfo.TakenAt = DateTime.Now; Console.WriteLine($"[Snapshot] [{guild.Name}] Saving snapshot...", Color.Orange); snapshotInfo = await Core.SnapManager.PushSnapshot(snapshotInfo); Console.WriteLine($"[Snapshot] [{guild.Name}] Snapshot taken!", Color.Green); return(snapshotInfo); }
public async Task RestoreSnapshot(CommandContext ctx, [Description("The snapshot id to restore")] string snapshotId) { if (ctx.Guild == null) { await ctx.RespondAsync($"**This command has to be ran inside a Discord server!**"); return; } if (ctx.Guild.Owner.Id != ctx.User.Id) { await ctx.RespondAsync($"**You must be the owner of this server to run this command!**"); return; } if (!Core.Configuration.AllowedUsers.Contains(ctx.User.Id)) { await ctx.RespondAsync($"**You're not allowed to use this bot!**"); return; } if (!Guid.TryParse(snapshotId, out Guid snapshotIdGuid)) { await ctx.RespondAsync($"**Invalid snapshot id format!**"); return; } DiscordSnapshotInfo snapshotInfo = await Core.SnapManager.LoadSnapshotInfo(ctx.User.Id, snapshotIdGuid); if (snapshotInfo == null) { await ctx.RespondAsync($"**The specified snapshot was not found!**"); return; } var guild = await ctx.Client.GetGuildAsync(ctx.Guild.Id); Core.DiscordBot.ToggleAutoSnapshot(guild.Id, false); foreach (var channel in await guild.GetChannelsAsync()) { await channel.DeleteAsync(); } var roles = guild.Roles.ToList(); foreach (var role in roles) { try { await guild.DeleteRoleAsync(role); } catch (Exception) { } } int rolesOffset = guild.Roles.Count; Dictionary <Guid, DiscordRole> rolesMapping = new Dictionary <Guid, DiscordRole>(); foreach (var role in snapshotInfo.RolesSnapshot) { if (role.IsEveryone) { rolesMapping.Add(role.InternalRoleId, guild.EveryoneRole); continue; } var createdRole = await guild.CreateRoleAsync(role.RoleName, (Permissions)role.Permissions, new DiscordColor(role.RoleColor), role.IsSeparated, role.IsMentionable); rolesMapping.Add(role.InternalRoleId, createdRole); } foreach (var role in snapshotInfo.RolesSnapshot) { var guildRole = rolesMapping[role.InternalRoleId]; if (role.IsEveryone) { await guild.UpdateRoleAsync(guildRole, permissions : (Permissions)role.Permissions, color : new DiscordColor(role.RoleColor), hoist : role.IsSeparated, mentionable : role.IsMentionable); } try { await guild.UpdateRolePositionAsync(guildRole, role.Hierarchy); } catch (Exception) { } } Dictionary <Guid, DiscordChannel> channelsMapping = new Dictionary <Guid, DiscordChannel>(); foreach (var channel in snapshotInfo.ChannelsSnapshot) { ChannelType type = ChannelType.Text; if (channel.IsCategory) { type = ChannelType.Category; } if (channel.IsText) { type = ChannelType.Text; } if (channel.IsVoice) { type = ChannelType.Voice; } var createdChannel = await guild.CreateChannelAsync(channel.ChannelName, type); channelsMapping.Add(channel.InternalChannelId, createdChannel); } foreach (var channel in snapshotInfo.ChannelsSnapshot) { if (!channel.ParentInternalChannelId.HasValue) { continue; } var guildChannel = channelsMapping[channel.InternalChannelId]; var guildParentChannel = channelsMapping[channel.ParentInternalChannelId.Value]; await guildChannel.ModifyAsync(parent : guildParentChannel); } var sortedChannels = snapshotInfo.ChannelsSnapshot; sortedChannels.Sort((c, k) => { if (c.ChannelPosition < k.ChannelPosition) { return(1); } if (c.ChannelPosition > k.ChannelPosition) { return(-1); } return(0); }); foreach (var channel in sortedChannels) { var guildChannel = channelsMapping[channel.InternalChannelId]; foreach (var channelRole in channel.ChannelRolesSnapshot) { var guildRole = rolesMapping[channelRole.InternalRoleId]; await guildChannel.AddOverwriteAsync(guildRole, (Permissions)channelRole.Allow, (Permissions)channelRole.Deny); } await guildChannel.ModifyPositionAsync(channel.ChannelPosition); } Core.DiscordBot.ToggleAutoSnapshot(guild.Id, true); }