YouTube-оповещения | Discord Бот | discord.js [Решено]

xyligan

Паркетный пол трещит под моей крышкой черепной
Сообщения
900
Реакции
432
Решил я значит с нуля написать систему YouTube-оповещений о новом видео, которая ранее была мной реализована криво. Пишу значит, пишу. Проверяю. Всё работает, но с одной проблемой. Оповещения отправляются в один и тот же канал. Ниже предоставлен код.
JavaScript:
client.on('ready', () => {
    setInterval(() => {
        console.log('Start checking...');
        let a = quick.fetch(`youtubeNotify`);
 
        let requiredArray = Object.values(a)
 
        var youtubeArr = []
        requiredArray.forEach(function(x){
            youtubeArr.push({ "youtubeChannelID": x.youtubeChannelID, "discordChannelID": x.discordChannelID, "message": x.message })
        })

        var i;
        for(i = 0; i < youtubeArr.length; i++) {
            var number = i;
            console.log(i);
            parser.parseURL(`https://www.youtube.com/feeds/videos.xml?channel_id=${youtubeArr[i].youtubeChannelID}`).then(data => {   
                console.log(i, number);
                if(data.items[0] === undefined) return;
                if(!quick.fetch(`postedVideos`)) {
                    quick.set(`postedVideos`, [ data.items[0].link ]);
                    quick.set(`videoData`, data.items[0]);
                    let parsed = quick.fetch(`videoData`);
                    let channel = client.channels.cache.get(youtubeArr[number].discordChannelID);
                    if(!channel) return;
                    let message = youtubeArr[number].message
                        .replace(/{author}/g, parsed.author)
                        .replace(/{title}/g, Discord.Util.escapeMarkdown(parsed.title))
                        .replace(/{url}/g, parsed.link);
                    channel.send(message);
                }else{
                    if(quick.fetch(`postedVideos`).includes(data.items[0].link)) {
                        return;
                    }else{
                        quick.set(`videoData`, data.items[0]);
                        quick.push(`postedVideos`, data.items[0].link);
                        let parsed = quick.fetch(`videoData`);
                        let channel = client.channels.cache.get(youtubeArr[number].discordChannelID);
                        if(!channel) return;
                        let message = youtubeArr[number].message
                            .replace(/{author}/g, parsed.author)
                            .replace(/{title}/g, Discord.Util.escapeMarkdown(parsed.title))
                            .replace(/{url}/g, parsed.link);
                        channel.send(message);
                    }
                }
            }).catch(err => {
                console.log(err);
            })
        }
    }, 5000)
})
Думал, что возможно проблема из-за того, что parser.parseURL() и т.д. выполняется в цикле, но нет. Перенос кода за цикл не помог. Проблема осталась, но при этом появилась ошибка. Буду рад услышать ваши мысли и возможно даже советы.
Структура БД:
{
  '569572836465967115': {
    guildID: '569572836465967115',
    youtubeChannelID: 'UCwYp94E8aBvxt4J8C1e-Tuw',
    discordChannelID: '732588709194498110',
    message: '{url}'
  },
  '774671958842540063': {
    guildID: '774671958842540063',
    youtubeChannelID: 'UCrcBbfwZ8Y_mevvNJwD_69w',
    discordChannelID: '775056497918345267',
    message: '{url}'
  }
}
 

Reiko1231

AlexTheRegent
Сообщения
508
Реакции
1,335
Проблема в том, что number объявлен как var. Аналог var в SourceMod - глобальная статическая переменная, т.е. она существует в единственном экземпляре до конца работы программы после того момента, как была выполнена строка с её объявлением. Парсер использует обещания (Promise), а это механизм работы с асинхронным кодом в javascript. В итоге у вас есть цикл, который запускает два обещания одновременно, в то же время увеличивая значение number на единицу при каждом запуске. Когда парсер получит данные от сайта, цикл for к тому моменту уже закончится. В результате все обещания будут выполнены (resolved) с number равным 2. Потому что number у вас var. Если бы он был let, то всё бы работало как надо, т.к. let это обычная локальная переменная, которая будет существовать в разных экземплярах для каждого шага цикла.

Я попытался сократить ваш код и оптимизировать его, но т.к. не могу проверить, выкладываю свой непроверенный вариант:
JavaScript:
client.on('ready', () => {
    setInterval(() => {
        console.log('Start checking...');
        let a = quick.fetch(`youtubeNotify`);

        let requiredArray = Object.values(a);
        requiredArray.forEach(item => {
            parser.parseURL(`https://www.youtube.com/feeds/videos.xml?channel_id=${item.youtubeChannelID}`).then(data => {
                if(data.items[0] === undefined) {
                    return;
                }
            
                if(quick.fetch(`postedVideos`)) {
                    if(quick.fetch(`postedVideos`).includes(data.items[0].link)) {
                        return;
                    }
                    quick.push(`postedVideos`, data.items[0].link);
                }
                else {
                    quick.set(`postedVideos`, [ data.items[0].link ]);
                }
            
                quick.set(`videoData`, data.items[0]);
            
                let parsed = quick.fetch(`videoData`);
                let channel = client.channels.cache.get(item.discordChannelID);
                if(!channel) {
                    return;
                }
            
                let message = item.message
                    .replace(/{author}/g, parsed.author)
                    .replace(/{title}/g, Discord.Util.escapeMarkdown(parsed.title))
                    .replace(/{url}/g, parsed.link);
                channel.send(message);
            }).catch(err => {
                console.log(err);
            });
        });
    }, 5000);
});

Я избавился от некоторых операций, которые вы делали. Например, убрал распаковку requiredArray в youtubeArr, сразу делая проход по элементам. Кроме этого, я попытался объединить if в более простую конструкцию для чтения, следуя принципу "выходить из функции как можно быстрее, а не создавать новые ветки". Но там используется модуль quick, поэтому я не уверен, что могу переставить операции местами (проверьте, подходит ли это вам). Также проверьте, правильно ли я расписал условия if.
 
Последнее редактирование модератором:

xyligan

Паркетный пол трещит под моей крышкой черепной
Сообщения
900
Реакции
432
Проблема в том, что number объявлен как var. Аналог var в SourceMod - глобальная статическая переменная, т.е. она существует в единственном экземпляре до конца работы программы после того момента, как была выполнена строка с её объявлением. Парсер использует обещания (Promise), а это механизм работы с асинхронным кодом в javascript. В итоге у вас есть цикл, который запускает два обещания одновременно, в то же время увеличивая значение number на единицу при каждом запуске. Когда парсер получит данные от сайта, цикл for к тому моменту уже закончится. В результате все обещания будут выполнены (resolved) с number равным 2. Потому что number у вас var. Если бы он был let, то всё бы работало как надо, т.к. let это обычная локальная переменная, которая будет существовать в разных экземплярах для каждого шага цикла.

Я попытался сократить ваш код и оптимизировать его, но т.к. не могу проверить, выкладываю свой непроверенный вариант:
JavaScript:
client.on('ready', () => {
    setInterval(() => {
        console.log('Start checking...');
        let a = quick.fetch(`youtubeNotify`);

        let requiredArray = Object.values(a);
        requiredArray.forEach(item => {
            parser.parseURL(`https://www.youtube.com/feeds/videos.xml?channel_id=${item.youtubeChannelID}`).then(data => {
                if(data.items[0] === undefined) {
                    return;
                }
           
                if(quick.fetch(`postedVideos`)) {
                    if(quick.fetch(`postedVideos`).includes(data.items[0].link)) {
                        return;
                    }
                    quick.push(`postedVideos`, data.items[0].link);
                }
                else {
                    quick.set(`postedVideos`, [ data.items[0].link ]);
                }
           
                quick.set(`videoData`, data.items[0]);
           
                let parsed = quick.fetch(`videoData`);
                let channel = client.channels.cache.get(item.discordChannelID);
                if(!channel) {
                    return;
                }
           
                let message = item.message
                    .replace(/{author}/g, parsed.author)
                    .replace(/{title}/g, Discord.Util.escapeMarkdown(parsed.title))
                    .replace(/{url}/g, parsed.link);
                channel.send(message);
            }).catch(err => {
                console.log(err);
            });
        });
    }, 5000);
});

Я избавился от некоторых операций, которые вы делали. Например, убрал распаковку requiredArray в youtubeArr, сразу делая проход по элементам. Кроме этого, я попытался объединить if в более простую конструкцию для чтения, следуя принципу "выходить из функции как можно быстрее, а не создавать новые ветки". Но там используется модуль quick, поэтому я не уверен, что могу переставить операции местами (проверьте, подходит ли это вам). Также проверьте, правильно ли я расписал условия if.
К сожалению Ваш код не будет работать так как нужно. Здесь вся фишка именно в цикле. Я уже всё исправил. Всё работает хорошо. Спасибо за помощь😄
 
Сверху Снизу