|  | @@ -0,0 +1,257 @@
 | 
	
		
			
				|  |  | +import { existsSync, readFileSync, renameSync } from "fs";
 | 
	
		
			
				|  |  | +import { ReactionType, ReactionEmote } from "./entity/ReactionEmote";
 | 
	
		
			
				|  |  | +import { getRepository, Repository } from "typeorm";
 | 
	
		
			
				|  |  | +import { KnownUser, User, UserRole } from "./entity/KnownUser";
 | 
	
		
			
				|  |  | +import { Dictionary } from "lodash";
 | 
	
		
			
				|  |  | +import { Guide, GuideKeyword, GuideType } from "./entity/Guide";
 | 
	
		
			
				|  |  | +import { Quote } from "./entity/Quote";
 | 
	
		
			
				|  |  | +import { MessageReaction } from "./entity/MessageReaction";
 | 
	
		
			
				|  |  | +import { FaceCaptionMessage, FaceCaptionType } from "./entity/FaceCaptionMessage";
 | 
	
		
			
				|  |  | +import { PostedForumNewsItem } from "./entity/PostedForumsNewsItem";
 | 
	
		
			
				|  |  | +import { KnownChannel } from "./entity/KnownChannel";
 | 
	
		
			
				|  |  | +import { DeadChatReply } from "./entity/DeadChatReply";
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +type EmoteTable = { [reactionType: string]: string[] };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +interface IGuide {
 | 
	
		
			
				|  |  | +    name: string;
 | 
	
		
			
				|  |  | +    displayName: string;
 | 
	
		
			
				|  |  | +    content: string;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +interface IEditors {
 | 
	
		
			
				|  |  | +    roles: string[];
 | 
	
		
			
				|  |  | +    users: string[];
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +interface INewsItem {
 | 
	
		
			
				|  |  | +    hash: string;
 | 
	
		
			
				|  |  | +    messageId: string;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +interface IQuote {
 | 
	
		
			
				|  |  | +    author: string;
 | 
	
		
			
				|  |  | +    message: string;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +interface IFaceCaptionTable {
 | 
	
		
			
				|  |  | +    pre: string[];
 | 
	
		
			
				|  |  | +    post: string[];
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +type NewsTable = { [id: string] : INewsItem | boolean};
 | 
	
		
			
				|  |  | +type MessageReactionsTable = { [message: string] : string };
 | 
	
		
			
				|  |  | +type FaceEditProbabilityTable = { [channel: string] : number };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +interface IOldDatabase {
 | 
	
		
			
				|  |  | +    emotes: EmoteTable;
 | 
	
		
			
				|  |  | +    reactableMentionedUsers: string[];
 | 
	
		
			
				|  |  | +    specialUsers: string[];
 | 
	
		
			
				|  |  | +    bigUsers: string[];
 | 
	
		
			
				|  |  | +    dedUsers: string[];
 | 
	
		
			
				|  |  | +    editors: IEditors;
 | 
	
		
			
				|  |  | +    memes: IGuide[];
 | 
	
		
			
				|  |  | +    miscs: IGuide[];
 | 
	
		
			
				|  |  | +    guides: IGuide[];
 | 
	
		
			
				|  |  | +    quotes: IQuote[];
 | 
	
		
			
				|  |  | +    messageReactions: MessageReactionsTable;
 | 
	
		
			
				|  |  | +    faceCaptions: IFaceCaptionTable;
 | 
	
		
			
				|  |  | +    postedNewsGuids: NewsTable;
 | 
	
		
			
				|  |  | +    faceEditChannels: FaceEditProbabilityTable;
 | 
	
		
			
				|  |  | +    deadChatReplies: string[];
 | 
	
		
			
				|  |  | +    newsPostVerifyChannel: string;
 | 
	
		
			
				|  |  | +    feedOutputChannel: string;
 | 
	
		
			
				|  |  | +    aggregateChannel: string;
 | 
	
		
			
				|  |  | +    latestKissDiaryEntry: number;
 | 
	
		
			
				|  |  | +    latestCom3D2WorldDiaryEntry: number;
 | 
	
		
			
				|  |  | +    lastCOMJPVersion: number;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +async function migrateEmotes(db: IOldDatabase) {
 | 
	
		
			
				|  |  | +    let repo = getRepository(ReactionEmote);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    for (const emoteType in db.emotes) {
 | 
	
		
			
				|  |  | +        if(!Object.values(ReactionType).includes(emoteType)) {
 | 
	
		
			
				|  |  | +            console.log(`WARN: emote type ${emoteType} is not a predefined emote type!`)
 | 
	
		
			
				|  |  | +            continue;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        await repo.save(db.emotes[emoteType].map(id => repo.create({
 | 
	
		
			
				|  |  | +            reactionId: id,
 | 
	
		
			
				|  |  | +            type: emoteType as ReactionType
 | 
	
		
			
				|  |  | +        })));
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +async function migrateUsers(db: IOldDatabase) {
 | 
	
		
			
				|  |  | +    let userRepo = getRepository(User);
 | 
	
		
			
				|  |  | +    let roleRepo = getRepository(UserRole);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    let users : Dictionary<User> = {};
 | 
	
		
			
				|  |  | +    let roles : Dictionary<UserRole> = {};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    let iterator = <T extends KnownUser>(targetDict : Dictionary<T>, repo: Repository<T>, ids: string[], action: (u: T) => void) => {
 | 
	
		
			
				|  |  | +        for(let userId of ids) {
 | 
	
		
			
				|  |  | +            let u : T;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            if(userId in users)
 | 
	
		
			
				|  |  | +                u = targetDict[userId];
 | 
	
		
			
				|  |  | +            else{
 | 
	
		
			
				|  |  | +                u = targetDict[userId] = repo.create();
 | 
	
		
			
				|  |  | +                u.userID = userId;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            action(u);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    iterator(users, userRepo, db.reactableMentionedUsers, u => u.mentionReactionType = ReactionType.ANGERY);
 | 
	
		
			
				|  |  | +    iterator(users, userRepo, db.specialUsers, u => u.replyReactionType = ReactionType.HUG);
 | 
	
		
			
				|  |  | +    iterator(users, userRepo, db.bigUsers, u => u.replyReactionType = ReactionType.BIG);
 | 
	
		
			
				|  |  | +    iterator(users, userRepo, db.dedUsers, u => u.replyReactionType = ReactionType.DED);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    iterator(users, userRepo, db.editors.users, u => u.canModerate = true);
 | 
	
		
			
				|  |  | +    iterator(roles, roleRepo, db.editors.roles, r => r.canModerate = true);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    await userRepo.save(Object.values(users));
 | 
	
		
			
				|  |  | +    await roleRepo.save(Object.values(roles));
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +async function migrateGuides(db : IOldDatabase) {
 | 
	
		
			
				|  |  | +    let guideRepo = getRepository(Guide);
 | 
	
		
			
				|  |  | +    let keywordsRepo = getRepository(GuideKeyword);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    let keywords : Dictionary<GuideKeyword> = {};
 | 
	
		
			
				|  |  | +    let dbGuides : Guide[] = [];
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    let process = async (guides: IGuide[], type: GuideType) => {
 | 
	
		
			
				|  |  | +        for(let guide of guides){
 | 
	
		
			
				|  |  | +            if(!guide.displayName)
 | 
	
		
			
				|  |  | +                continue;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            let guideKeywords = guide.name.split(" ").map(s => s.trim().toLowerCase());
 | 
	
		
			
				|  |  | +            let keywordsObjects : GuideKeyword[] = [];
 | 
	
		
			
				|  |  | +            
 | 
	
		
			
				|  |  | +            for(let keyword of guideKeywords) {
 | 
	
		
			
				|  |  | +                let keywordObj : GuideKeyword;
 | 
	
		
			
				|  |  | +    
 | 
	
		
			
				|  |  | +                if(keyword in keywords)
 | 
	
		
			
				|  |  | +                    keywordObj = keywords[keyword];
 | 
	
		
			
				|  |  | +                else
 | 
	
		
			
				|  |  | +                    keywordObj = keywords[keyword] = await keywordsRepo.save(keywordsRepo.create({ keyword: keyword }));
 | 
	
		
			
				|  |  | +                keywordsObjects.push(keywordObj);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +    
 | 
	
		
			
				|  |  | +            let guideObj = guideRepo.create({
 | 
	
		
			
				|  |  | +                keywords: keywordsObjects,
 | 
	
		
			
				|  |  | +                type: type,
 | 
	
		
			
				|  |  | +                displayName: guide.displayName,
 | 
	
		
			
				|  |  | +                content: guide.content
 | 
	
		
			
				|  |  | +            });
 | 
	
		
			
				|  |  | +    
 | 
	
		
			
				|  |  | +            dbGuides.push(guideObj);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    await process(db.memes, GuideType.MEME);
 | 
	
		
			
				|  |  | +    await process(db.guides, GuideType.GUIDE);
 | 
	
		
			
				|  |  | +    await process(db.miscs, GuideType.MISC);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // await keywordsRepo.save(Object.values(keywords));
 | 
	
		
			
				|  |  | +    await guideRepo.save(dbGuides);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +async function migrateQuotes(db: IOldDatabase) {
 | 
	
		
			
				|  |  | +    let repo = getRepository(Quote);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    await repo.save(db.quotes.map(q => repo.create({
 | 
	
		
			
				|  |  | +        author: q.author,
 | 
	
		
			
				|  |  | +        message: q.message
 | 
	
		
			
				|  |  | +    })));
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +async function migrateMessageReactions(db: IOldDatabase) {
 | 
	
		
			
				|  |  | +    let repo = getRepository(MessageReaction);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    await repo.save(Object.entries(db.messageReactions).map(([message, emoteId]) => repo.create({
 | 
	
		
			
				|  |  | +        message: message,
 | 
	
		
			
				|  |  | +        reactionEmoteId: emoteId
 | 
	
		
			
				|  |  | +    })));
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +async function migrateFaceCaptions(db: IOldDatabase) {
 | 
	
		
			
				|  |  | +    let repo = getRepository(FaceCaptionMessage);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    await repo.save([
 | 
	
		
			
				|  |  | +        ...db.faceCaptions.pre.map(s => repo.create( {
 | 
	
		
			
				|  |  | +            message: s,
 | 
	
		
			
				|  |  | +            type: FaceCaptionType.PREFIX
 | 
	
		
			
				|  |  | +        })),
 | 
	
		
			
				|  |  | +        ...db.faceCaptions.post.map(s => repo.create( {
 | 
	
		
			
				|  |  | +            message: s,
 | 
	
		
			
				|  |  | +            type: FaceCaptionType.POSTFIX
 | 
	
		
			
				|  |  | +        }))
 | 
	
		
			
				|  |  | +    ]);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +async function migrateNews(db: IOldDatabase) {
 | 
	
		
			
				|  |  | +    let repo = getRepository(PostedForumNewsItem);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    await repo.save(Object.entries(db.postedNewsGuids).filter(([id, item]) => typeof item != "boolean").map(([id, item]) => repo.create({
 | 
	
		
			
				|  |  | +        hash: (item as INewsItem).hash,
 | 
	
		
			
				|  |  | +        postedMessageId: (item as INewsItem).messageId,
 | 
	
		
			
				|  |  | +        id: id
 | 
	
		
			
				|  |  | +    })));
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +async function migrateChannels(db: IOldDatabase) {
 | 
	
		
			
				|  |  | +    let repo = getRepository(KnownChannel);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    await repo.save([
 | 
	
		
			
				|  |  | +        repo.create({
 | 
	
		
			
				|  |  | +            channelId: db.newsPostVerifyChannel,
 | 
	
		
			
				|  |  | +            channelType: "newsPostVerify"            
 | 
	
		
			
				|  |  | +        }),
 | 
	
		
			
				|  |  | +        repo.create({
 | 
	
		
			
				|  |  | +            channelId: db.aggregateChannel,
 | 
	
		
			
				|  |  | +            channelType: "aggregatorManager"
 | 
	
		
			
				|  |  | +        }),
 | 
	
		
			
				|  |  | +        repo.create({
 | 
	
		
			
				|  |  | +            channelId: db.feedOutputChannel,
 | 
	
		
			
				|  |  | +            channelType: "newsFeed"
 | 
	
		
			
				|  |  | +        }),
 | 
	
		
			
				|  |  | +        ...Object.entries(db.faceEditChannels).map(([channelId, prob]) => repo.create({
 | 
	
		
			
				|  |  | +            channelId: channelId,
 | 
	
		
			
				|  |  | +            faceMorphProbability: prob
 | 
	
		
			
				|  |  | +        }))
 | 
	
		
			
				|  |  | +    ]);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +async function migrateDeadMessages(db: IOldDatabase) {
 | 
	
		
			
				|  |  | +    let repo = getRepository(DeadChatReply);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    await repo.save(db.deadChatReplies.map(r => repo.create({
 | 
	
		
			
				|  |  | +        message: r
 | 
	
		
			
				|  |  | +    })));
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +export async function migrate() {
 | 
	
		
			
				|  |  | +    if(!existsSync("./db.json"))
 | 
	
		
			
				|  |  | +        return;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    let json = readFileSync("./db.json", { encoding: "utf-8" });
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    let db = JSON.parse(json) as IOldDatabase;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    await migrateEmotes(db);
 | 
	
		
			
				|  |  | +    await migrateUsers(db);
 | 
	
		
			
				|  |  | +    await migrateGuides(db);
 | 
	
		
			
				|  |  | +    await migrateQuotes(db);
 | 
	
		
			
				|  |  | +    await migrateMessageReactions(db);
 | 
	
		
			
				|  |  | +    await migrateFaceCaptions(db);
 | 
	
		
			
				|  |  | +    await migrateNews(db);
 | 
	
		
			
				|  |  | +    await migrateChannels(db);
 | 
	
		
			
				|  |  | +    await migrateDeadMessages(db);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    renameSync("./db.json", "./db_migrated.json");
 | 
	
		
			
				|  |  | +}
 |