NadekoBot/NadekoBot.Core/Modules/Administration/MigrationCommands.cs

382 lines
18 KiB
C#
Raw Normal View History

2016-10-04 23:57:35 +00:00
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Discord.Commands;
using NadekoBot.Services;
using NadekoBot.Services.Database.Models;
using Newtonsoft.Json;
using System.Collections.Concurrent;
using NadekoBot.Extensions;
using NadekoBot.Services.Database;
2016-10-11 20:06:31 +00:00
using Microsoft.Data.Sqlite;
2017-07-17 19:42:36 +00:00
using NadekoBot.Common.Attributes;
using NadekoBot.Common.Collections;
using NadekoBot.Modules.Administration.Common.Migration;
2016-10-04 23:57:35 +00:00
namespace NadekoBot.Modules.Administration
{
public partial class Administration
{
[Group]
2017-07-17 19:42:36 +00:00
public class MigrationCommands : NadekoSubmodule
2016-10-04 23:57:35 +00:00
{
private const int CURRENT_VERSION = 1;
private readonly DbService _db;
2016-10-04 23:57:35 +00:00
2017-07-17 19:42:36 +00:00
public MigrationCommands(DbService db)
2016-10-04 23:57:35 +00:00
{
_db = db;
2016-10-04 23:57:35 +00:00
}
[NadekoCommand, Usage, Description, Aliases]
2016-10-04 23:57:35 +00:00
[OwnerOnly]
2016-12-16 18:43:57 +00:00
public async Task MigrateData()
2016-10-04 23:57:35 +00:00
{
var version = 0;
using (var uow = _db.UnitOfWork)
2016-10-04 23:57:35 +00:00
{
version = uow.BotConfig.GetOrCreate().MigrationVersion;
}
try
{
for (var i = version; i < CURRENT_VERSION; i++)
{
switch (i)
{
case 0:
2016-10-12 15:35:27 +00:00
Migrate0_9To1_0();
2016-10-04 23:57:35 +00:00
break;
}
}
await ReplyConfirmLocalized("migration_done").ConfigureAwait(false);
2016-10-04 23:57:35 +00:00
}
2016-10-12 01:44:31 +00:00
catch (Exception ex)
2016-10-04 23:57:35 +00:00
{
2016-10-12 01:44:31 +00:00
_log.Error(ex);
await ReplyErrorLocalized("migration_error").ConfigureAwait(false);
2016-10-04 23:57:35 +00:00
}
}
2016-10-12 15:35:27 +00:00
private void Migrate0_9To1_0()
2016-10-04 23:57:35 +00:00
{
using (var uow = _db.UnitOfWork)
{
var botConfig = uow.BotConfig.GetOrCreate();
2016-10-11 20:06:31 +00:00
MigrateConfig0_9(uow, botConfig);
2016-10-12 15:53:00 +00:00
MigratePermissions0_9(uow);
MigrateServerSpecificConfigs0_9(uow);
2016-10-12 15:53:00 +00:00
MigrateDb0_9(uow);
//NOW save it
2016-10-12 15:35:27 +00:00
_log.Warn("Writing to disc");
uow.Complete();
2016-11-10 22:46:41 +00:00
botConfig.MigrationVersion = 1;
}
}
2016-10-11 20:06:31 +00:00
private void MigrateDb0_9(IUnitOfWork uow)
{
2016-10-11 23:06:21 +00:00
var db = new SqliteConnection("Data Source=data/nadekobot.sqlite");
2016-10-12 01:44:31 +00:00
if (!File.Exists("data/nadekobot.sqlite"))
{
_log.Warn("No data from the old database will be migrated.");
return;
}
2016-10-11 20:06:31 +00:00
db.Open();
var com = db.CreateCommand();
2016-10-12 15:35:27 +00:00
var i = 0;
try
2016-10-11 20:06:31 +00:00
{
com.CommandText = "SELECT * FROM Announcement";
var reader = com.ExecuteReader();
while (reader.Read())
{
var gid = (ulong)(long)reader["ServerId"];
var greet = (long)reader["Greet"] == 1;
var greetDM = (long)reader["GreetPM"] == 1;
var greetChannel = (ulong)(long)reader["GreetChannelId"];
var greetMsg = (string)reader["GreetText"];
var bye = (long)reader["Bye"] == 1;
var byeChannel = (ulong)(long)reader["ByeChannelId"];
var byeMsg = (string)reader["ByeText"];
var gc = uow.GuildConfigs.For(gid, set => set);
2016-10-11 20:06:31 +00:00
if (greetDM)
gc.SendDmGreetMessage = greet;
else
gc.SendChannelGreetMessage = greet;
gc.GreetMessageChannelId = greetChannel;
gc.ChannelGreetMessageText = greetMsg;
2016-10-11 20:06:31 +00:00
gc.SendChannelByeMessage = bye;
gc.ByeMessageChannelId = byeChannel;
gc.ChannelByeMessageText = byeMsg;
2016-10-11 20:06:31 +00:00
_log.Info(++i);
}
}
catch {
_log.Warn("Greet/bye messages won't be migrated");
2016-10-11 20:06:31 +00:00
}
var com2 = db.CreateCommand();
2017-02-14 13:30:21 +00:00
com2.CommandText = "SELECT * FROM CurrencyState GROUP BY UserId";
2016-10-11 20:06:31 +00:00
2016-10-12 15:35:27 +00:00
i = 0;
try
2016-10-11 20:06:31 +00:00
{
2017-02-14 13:30:21 +00:00
var reader2 = com2.ExecuteReader();
while (reader2.Read())
2016-10-11 20:06:31 +00:00
{
_log.Info(++i);
var curr = new Currency()
{
Amount = (long)reader2["Value"],
UserId = (ulong)(long)reader2["UserId"]
};
uow.Currency.Add(curr);
}
}
catch
{
_log.Warn("Currency won't be migrated");
2016-10-11 20:06:31 +00:00
}
db.Close();
2016-10-11 23:34:35 +00:00
try { File.Move("data/nadekobot.sqlite", "data/DELETE_ME_nadekobot.sqlite"); } catch { }
2016-10-11 20:06:31 +00:00
}
private void MigrateServerSpecificConfigs0_9(IUnitOfWork uow)
{
const string specificConfigsPath = "data/ServerSpecificConfigs.json";
2016-10-12 01:44:31 +00:00
if (!File.Exists(specificConfigsPath))
{
_log.Warn($"No data from {specificConfigsPath} will be migrated.");
return;
}
var configs = new ConcurrentDictionary<ulong, ServerSpecificConfig>();
2016-10-04 23:57:35 +00:00
try
{
configs = JsonConvert
.DeserializeObject<ConcurrentDictionary<ulong, ServerSpecificConfig>>(
File.ReadAllText(specificConfigsPath), new JsonSerializerSettings()
{
Error = (s, e) =>
{
if (e.ErrorContext.Member.ToString() == "GenerateCurrencyChannels")
{
e.ErrorContext.Handled = true;
}
}
});
}
catch (Exception ex)
{
_log.Warn(ex, "ServerSpecificConfig deserialization failed");
return;
}
2016-10-12 15:35:27 +00:00
var i = 0;
var selfAssRoles = new ConcurrentHashSet<SelfAssignedRole>();
configs
.Select(p => new { data = p.Value, gconfig = uow.GuildConfigs.For(p.Key) })
.AsParallel()
.ForAll(config =>
{
2016-10-12 15:35:27 +00:00
try
{
2016-10-12 15:35:27 +00:00
var guildConfig = config.gconfig;
var data = config.data;
guildConfig.AutoAssignRoleId = data.AutoAssignedRole;
guildConfig.DeleteMessageOnCommand = data.AutoDeleteMessagesOnCommand;
guildConfig.DefaultMusicVolume = data.DefaultMusicVolume;
guildConfig.ExclusiveSelfAssignedRoles = data.ExclusiveSelfAssignedRoles;
guildConfig.GenerateCurrencyChannelIds = new HashSet<GCChannelId>(data.GenerateCurrencyChannels.Select(gc => new GCChannelId() { ChannelId = gc.Key }));
selfAssRoles.AddRange(data.ListOfSelfAssignableRoles.Select(r => new SelfAssignedRole() { GuildId = guildConfig.GuildId, RoleId = r }).ToArray());
guildConfig.LogSetting.IgnoredChannels = new HashSet<IgnoredLogChannel>(data.LogserverIgnoreChannels.Select(id => new IgnoredLogChannel() { ChannelId = id }));
2016-12-12 23:44:52 +00:00
guildConfig.LogSetting.LogUserPresenceId = data.LogPresenceChannel;
2016-10-12 15:35:27 +00:00
guildConfig.FollowedStreams = new HashSet<FollowedStream>(data.ObservingStreams.Select(x =>
{
FollowedStream.FollowedStreamType type = FollowedStream.FollowedStreamType.Twitch;
switch (x.Type)
{
case StreamNotificationConfig0_9.StreamType.Twitch:
type = FollowedStream.FollowedStreamType.Twitch;
break;
case StreamNotificationConfig0_9.StreamType.Beam:
2017-08-06 14:07:48 +00:00
type = FollowedStream.FollowedStreamType.Mixer;
2016-10-12 15:35:27 +00:00
break;
case StreamNotificationConfig0_9.StreamType.Hitbox:
2017-08-06 15:34:52 +00:00
type = FollowedStream.FollowedStreamType.Smashcast;
2016-10-12 15:35:27 +00:00
break;
default:
break;
}
2016-10-12 15:35:27 +00:00
return new FollowedStream()
{
ChannelId = x.ChannelId,
GuildId = guildConfig.GuildId,
Username = x.Username.ToLowerInvariant(),
Type = type
};
}));
guildConfig.VoicePlusTextEnabled = data.VoicePlusTextEnabled;
_log.Info("Migrating SpecificConfig for {0} done ({1})", guildConfig.GuildId, ++i);
}
catch (Exception ex)
{
2016-10-12 15:35:27 +00:00
_log.Error(ex);
}
});
uow.SelfAssignedRoles.AddRange(selfAssRoles.ToArray());
try { File.Move("data/ServerSpecificConfigs.json", "data/DELETE_ME_ServerSpecificCOnfigs.json"); } catch { }
}
private void MigratePermissions0_9(IUnitOfWork uow)
{
2017-02-14 13:30:21 +00:00
var permissionsDict = new ConcurrentDictionary<ulong, ServerPermissions0_9>();
2016-10-11 07:39:51 +00:00
if (!Directory.Exists("data/permissions/"))
2016-10-12 01:44:31 +00:00
{
_log.Warn("No data from permissions will be migrated.");
return;
}
foreach (var file in Directory.EnumerateFiles("data/permissions/"))
{
try
{
var strippedFileName = Path.GetFileNameWithoutExtension(file);
if (string.IsNullOrWhiteSpace(strippedFileName)) continue;
var id = ulong.Parse(strippedFileName);
var data = JsonConvert.DeserializeObject<ServerPermissions0_9>(File.ReadAllText(file));
2017-02-14 13:30:21 +00:00
permissionsDict.TryAdd(id, data);
}
catch
{
// ignored
}
}
2016-10-12 15:35:27 +00:00
var i = 0;
2017-02-14 13:30:21 +00:00
permissionsDict
2016-10-12 15:35:27 +00:00
.Select(p => new { data = p.Value, gconfig = uow.GuildConfigs.For(p.Key) })
.AsParallel()
.ForAll(perms =>
{
try
{
var data = perms.data;
var gconfig = perms.gconfig;
gconfig.PermissionRole = data.PermissionsControllerRole;
gconfig.VerbosePermissions = data.Verbose;
gconfig.FilteredWords = new HashSet<FilteredWord>(data.Words.Select(w => w.ToLowerInvariant())
.Distinct()
.Select(w => new FilteredWord() { Word = w }));
gconfig.FilterWords = data.Permissions.FilterWords;
gconfig.FilterInvites = data.Permissions.FilterInvites;
gconfig.FilterInvitesChannelIds = new HashSet<FilterChannelId>();
gconfig.FilterInvitesChannelIds.AddRange(data.ChannelPermissions.Where(kvp => kvp.Value.FilterInvites)
.Select(cp => new FilterChannelId()
{
ChannelId = cp.Key
}));
gconfig.FilterWordsChannelIds = new HashSet<FilterChannelId>();
gconfig.FilterWordsChannelIds.AddRange(data.ChannelPermissions.Where(kvp => kvp.Value.FilterWords)
.Select(cp => new FilterChannelId()
{
ChannelId = cp.Key
}));
gconfig.CommandCooldowns = new HashSet<CommandCooldown>(data.CommandCooldowns
.Where(cc => !string.IsNullOrWhiteSpace(cc.Key) && cc.Value > 0)
.Select(cc => new CommandCooldown()
{
CommandName = cc.Key,
Seconds = cc.Value
}));
_log.Info("Migrating data from permissions folder for {0} done ({1})", gconfig.GuildId, ++i);
}
catch (Exception ex)
{
_log.Error(ex);
}
});
try { Directory.Move("data/permissions", "data/DELETE_ME_permissions"); } catch { }
}
2016-10-11 20:06:31 +00:00
private void MigrateConfig0_9(IUnitOfWork uow, BotConfig botConfig)
{
Config0_9 oldConfig;
2016-10-11 07:39:51 +00:00
const string configPath = "data/config.json";
try
{
oldConfig = JsonConvert.DeserializeObject<Config0_9>(File.ReadAllText(configPath));
2016-10-04 23:57:35 +00:00
}
catch (FileNotFoundException)
{
2016-10-05 02:03:57 +00:00
_log.Warn("config.json not found");
2016-10-04 23:57:35 +00:00
return;
}
catch (Exception)
{
2016-10-11 04:21:43 +00:00
_log.Error("Unknown error while deserializing file config.json, pls check its integrity, aborting migration");
2016-10-04 23:57:35 +00:00
throw new MigrationException();
}
//Basic
botConfig.ForwardMessages = oldConfig.ForwardMessages;
botConfig.ForwardToAllOwners = oldConfig.ForwardToAllOwners;
botConfig.BufferSize = (ulong)oldConfig.BufferSize;
botConfig.RemindMessageFormat = oldConfig.RemindMessageFormat;
botConfig.CurrencySign = oldConfig.CurrencySign;
botConfig.CurrencyName = oldConfig.CurrencyName;
botConfig.DMHelpString = oldConfig.DMHelpString;
botConfig.HelpString = oldConfig.HelpString;
//messages
botConfig.RotatingStatuses = oldConfig.IsRotatingStatus;
var messages = new List<PlayingStatus>();
oldConfig.RotatingStatuses.ForEach(i => messages.Add(new PlayingStatus { Status = i }));
botConfig.RotatingStatusMessages = messages;
//Blacklist
var blacklist = new HashSet<BlacklistItem>(oldConfig.ServerBlacklist.Select(server => new BlacklistItem() { ItemId = server, Type = BlacklistType.Server }));
blacklist.AddRange(oldConfig.ChannelBlacklist.Select(channel => new BlacklistItem() { ItemId = channel, Type = BlacklistType.Channel }));
blacklist.AddRange(oldConfig.UserBlacklist.Select(user => new BlacklistItem() { ItemId = user, Type = BlacklistType.User }));
botConfig.Blacklist = blacklist;
2016-10-04 23:57:35 +00:00
//Eightball
botConfig.EightBallResponses = new HashSet<EightBallResponse>(oldConfig._8BallResponses.Select(response => new EightBallResponse() { Text = response }));
2016-10-04 23:57:35 +00:00
2016-10-11 20:06:31 +00:00
//customreactions
uow.CustomReactions.AddRange(oldConfig.CustomReactions.SelectMany(cr =>
{
return cr.Value.Select(res => new CustomReaction()
{
2016-10-15 12:40:01 +00:00
GuildId = null,
2016-10-11 20:06:31 +00:00
IsRegex = false,
OwnerOnly = false,
Response = res,
Trigger = cr.Key.ToLowerInvariant(),
2016-10-11 20:06:31 +00:00
});
}).ToArray());
try { File.Move(configPath, "./data/DELETE_ME_config.json"); } catch { }
2016-10-04 23:57:35 +00:00
}
}
}
}