디스코드 베스트 포스트 추천 봇 만들기 2편 - Mock데이터로 10008에러 데이터도 수집하기 그 와중에 또다시 발생한 모든메시지0인 예외 케이스
현재 노트: KR-110.00 b 디스코드 베스트 포스트 추천 봇 만들기 2편 - Mock데이터로 10008에러 데이터도 수집하기 그 와중에 또다시 발생한 모든메시지0인 예외 케이스
상위 분류: KR-110.00 디스코드 베스트 포스트 추천 봇 제작기
디스코드 베스트 포스트 추천 봇 만들기 2편 - Mock데이터로 10008에러 데이터도 수집하기 그 와중에 또다시 발생한 모든메시지0인 예외 케이스
본 글의 내용은 일반적으로 다음과 같은 흐름으로 진행됩니다.
원하는 것을 구현 시도 -> 문제가 생김 -> 해결
또다른 기능등 시도-> 다시 새로운 문제가 생김 -> 해결
... 구현 또는 수정이나 해결될 때까지 적절하게 반복
결론 잘모르는 데이터를 일단 사용하는 것도 구현을 위해 필요하지만, 프로퍼티나 메소드와 같은 구조를 1번이라도 일단 파악해두면 매우매우 큰 도움이 될것
Mock데이터생성하기 (단 1개의 메시지나 리액션이없는 예외케이스)
메시지가 10008에러로 얻지못하는 경우 삭제된 메시지에 대한 시도라 try-catch로 넘어갔는데 그 삭제된 메시지만 제외하면 쓰레드에는 여전히 리액션과 서브메시지가 있기에 해당 프로퍼티만 unknown이라는 임의의 값으로 채운 메시지로 시도한다면 데이터를 얻을 수 있기에 mock데이터 생성해서 임의의 값 채워넣기 시도
실제 쓰레드 데이터와 비교해보기 일단 어떤 데이터의 값을 mock으로 채워넣어야하는지 확인
사용하는 프로퍼티등을 확인하여 다음과 같이 생성
// Fetch the starter message (main post) within try-catch
let starterMessage;
try {
starterMessage = await thread.fetchStarterMessage();
} catch (error) {
console.error(`Failed to fetch starter message for thread ${thread.name}:`, error.message);
starterMessage = {
content: "이 스레드의 시작 메시지를 가져오지 못했습니다.", // Dummy message content
author: { username: "Unknown Author" }, // Dummy author
reactions: {
cache: new Map([]), // Initialize reactions as an empty Map
},
};
}
if (!starterMessage) {
console.log(`Could not fetch starter message for thread ${thread.name}`);
}
해당 startMessage
를 사용하는 다른 변수에서 문제발생
없는 값에 대해 reduce를 해서 그런지 에러남
const mainPostReactions = starterMessage.reactions.cache.reduce(
(acc, reaction) => acc + reaction.count,
0
);
배열 빈값인지 확인하는 과정 추가
const mainPostReactions = starterMessage.reactions.cache.size > 0
? Array.from(starterMessage.reactions.cache.values()).reduce(
(acc, reaction) => acc + reaction.count,
0
)
: 0;
다른 함수에서 사용하면서 앞에서 바꾼 Mock을 사용하면서 문제 발생 (단 1개의 메시지나 리액션이없는 예외케이스)
getThreadMetadata
함수에서도 messages를 사용하다보니 에러발생
이 함수서도 당연히 메시지가있고, reaction 값이 있는 것으로 산정
해당 내용은 함수분리 구조측면에서 생긴문제인데 A라는 함수에서 Mock을 만들어서 테스트할때 그이전에 Mock이없는 값을 기준으로 데이터를 처리하는 B함수를 수정안해서 생긴 문제였습니다.
const messages = await thread.messages.fetch({ limit: 100 });
const mainPost = messages.last();
const mainPostReactions = mainPost.reactions.cache.reduce((acc, reaction) => acc + reaction.count, 0);
const totalReactions = messages.reduce((acc, message) =>
acc + message.reactions.cache.reduce((reactionAcc, reaction) => reactionAcc + reaction.count, 0), 0);
유사하게 mock 데이터생성.
원래라면 좀더 깊게 생각해볼 문제지만, 현재 삭제된 메시지에 대한 쓰레드만이 유일한 상황임으로 해당 문제를 mock데이터 생성 형태로 해결 시도
일단 끝까지 프로그램 실행되는 것 확인
표기는 문제없지만, 기록 시 메시지와 reaction문제
저장된 데이터의 리액션과 메시지의 숫자가 양수이지만, 저장시 0으로 처리됨
console.log에서는 양수값이 표현되지만, 저장시에는 0이되는 문제였습니다.
다음과 같이 삼항연산자에서 빈 배열을 확인 조건으로 설정함
const mainPostReactions = mainPost.reactions.cache.size > 0 ?
mainPost.reactions.cache.reduce((acc, reaction) => acc + reaction.count, 0) :
0;
const totalReactions = messages.length > 0 // Check if messages is not empty
? messages.reduce((acc, message) =>
acc + (message.reactions && message.reactions.cache
? message.reactions.cache.reduce((reactionAcc, reaction) => reactionAcc + reaction.count, 0)
: 0), 0)
: 0;
처음에는 배열 빈값 체크 였지만 문제가 있는것같음.
단순히 에러시 플래그 활용하는 방법으로 변경
let messageFailed = false;
try {
messages = await thread.messages.fetch({ limit: 100 });
} catch (error) {
console.error(`Failed to fetch messages for thread ${thread.id}:`, error);
messageFailed = true;
}
이후 리액션 개수 제대로 저장되는것 확인
데이터 전부 안 가져와지는 문제 로그없이 감각으로 문제 파악하기
단 1개의 메시지나 리액션이없는 예외케이스
이건 어떤의미로 고찰? 센스가 필요한 느낌이었습니다. 왜냐하면 이번에는 정말 특수한 쓰레드 1개에서 문제가 생겼는데, 그 원인에대한 로그도 없는 상황이기 때문입니다.
음 일단 터미널값에서 확인결과 메시지,리액션이 전부0인 특수한 쓰레드라서 생기는 예외같음
왜냐하면 messagecount 가 -1로표기됨
즉 터미널에서 비상적인 값을 근거로 추론
Number of messages in thread 1주차 과제 요구사항 관련 기재되지 않은 애매한
내용 같이 판단해보기: -1
추가로 error stack에서 명시한 문제는 다음 항목
const mainPostReactions = !messageFailed ?
mainPost.reactions.cache.reduce((acc, reaction) => acc + reaction.count, 0) :
0;
Failed to fetch threads: Cannot read properties of undefined (reading 'rea
ctions')
Stack trace: TypeError: Cannot read properties of undefined (reading 'reactions')
at getThreadMetadata (C:\Users\KMC\Desktop\Project\discord-librarian\getmetadata.js:224:18)
두 메소드가 미묘하게 다른거 같음
지금까지 문제생기고 해결하려한 메소드
await thread.fetchStarterMessage();
지금 문제 생긴 메소드
messages = await thread.messages.fetch({ limit: 100 });
fetch에 성공은 했지만 빈 스레드라서 문제생긴거같음.
지금까지 10008에러의경우 쓰레드에대한 fetch에서 문제가 생겼는데 이번에는 쓰레드 fetch는 성공했지만 리액션과 메시지 수에 대한 처리에서 문제인 느낌이긴 때문입니다.
계속 사용하던 messages 구조 처음으로 확인해보기 (단 1개의 메시지나 리액션이없는 예외케이스)
messages 출력시 다음과 같이 나옴
messages: [object Map]
오브젝트의 오브젝트의 오브젝트 중첩구조로 예상
AI 도움움로 전체 출력
console.log(
messages: ${JSON.stringify(Array.from(messages.values()), null, 2)});
지금까지 파악하지 않고 무심코 사용하던 message의 출력 결과는 매우 복잡한 구조였음
쓰레드의 각 메시지마다 다음의 구조를 가지는것 같음.
메시지 갯수만큼의 오브젝트가 messages
에 들어감
{
"channelId": "1200000000000000",
"guildId": "1200000000000000",
"id": "1200000000000000",
"createdTimestamp": 1728982824472,
"type": 0,
"system": false,
"content": "메시지 내용",
"authorId": "1200000000000000",
"pinned": false,
"tts": false,
"nonce": null,
"embeds": [],
"components": [],
"attachments": [],
"stickers": [],
"position": null,
"roleSubscriptionData": null,
"resolved": null,
"editedTimestamp": null,
"mentions": {
"everyone": false,
"users": [],
"roles": [],
"crosspostedChannels": [],
"repliedUser": null,
"members": [],
"channels": []
},
"webhookId": null,
"groupActivityApplicationId": null,
"applicationId": null,
"activity": null,
"flags": 0,
"reference": null,
"interactionMetadata": null,
"interaction": null,
"poll": null,
"call": null,
"cleanContent": "메시지내용.
"
},
이 내용을 통대로 계속해서 에러가 발생하는 메시지의 구조를 출력해보자
과연 에러가 발생한 케이스의 경우는?
messages: []
Failed to fetch threads: Cannot read properties of undefined (reading 'reactions')
Stack trace: TypeError: Cannot read properties of undefined (reading 'reactions')
at getThreadMetadata (C:\Users\KMC\Desktop\Project\discord-librarian\getmetadata.js:225:18)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async getMetadata (C:\Users\KMC\Desktop\Project\discord-librarian\getmetadata.js:110:40)
at async main (C:\Users\KMC\Desktop\Project\discord-librarian\main.js:16:25)
가져온 데이터: undefined
아하! 역시 이번 케이스는 삭제된 케이스
그리고 메시지가 1개도 없는 케이스
가 겹쳐서 생긴 경우 였음. 만약 쓰레드에 1개라도 메시지가 있다면 messages는 빈배열이 아니기 때문
반환값이 collection인 경우 length값은 항상0이며 size프로퍼티를 사용해야함.
추가로 계속 로직에러가 생긴 부분 실제로 확인해보기.
원하는 값이 안나오는 문제였는데 messages에 값이 있고, 여러오브젝트가 있음에도 불구하고 무조건 0이출력됨
console.log(`Object.keys(messages).length: ${Object.keys(messages).length} , messages.length:${messages.length}`)
=>
Object.keys(messages).length: 0 , messages.length:undefined
...
그래서 원하는 값을 찾아보기 위해 이전에 사용한 size
프로퍼티 테스트
원하는 값 출력 확인(메시지의 개수)
** length
값 이 제대로 안 나오고 size에서 원하는 값이 나오는 이유는 messages
가 array
가
아니라 collection
이기 때문**
디스코드에서는 fetch값을 js에는 없지만 collection으로 반환하고, 해당 객체는 length가 아닌 size값을 사용함
공식문서에서 이런 messages관련 구조 언급돼 있었음 (messages 구조 확인해보기)
https://discord.js.org/docs/packages/discord.js/14.16.3/MessageManager:Class#fetch
Fetches message(s) from a channel. The returned Collection does not contain reaction users of the messages if they were not cached. Those need to be fetched separately in such a case.
fetch
메소드 반환값이 Collection
이라는 언급과 cached된 값이 없으면 reaction
을 포함하지 않는다라는 설명이 있었음
해결방법 size
프로퍼티를 조건으로 추가
const mainPost = !messageFailed && messages.size ? ...
const mainPostReactions = !messageFailed && messages.size ? ...
const totalReactions = !messageFailed && messages.size ?
어제까지 90개에 달하던 포스트가 63개로 줄음
하루동안 생성포스트고려하면 무조건 늘어야할텐데...
음 아카이빙된 쓰레드의 fetchActive()
함수에서 검출이 안될 수 있다고함
(Chatgpt)
한 번 확인해보기로하자
const fetchedArchivedThreads = await channel.threads.fetchArchived();
아카이브 포함된 채널의 검색결과 총 채널은 107이고, 어제 93개랑 비교하면 확실히 늘어남
Successfully fetched 63 active threads in forum channel.
Archieved channel 44
추가로 디스코드 공식docs에서 아카이브되는 채널의 기준을 살펴보자
공식 설명에서는 활동이 없는 채널이 아카이브된다. 예시로 7일로 설명됨.
https://discord.com/developers/docs/topics/threads#active-archived-threads
결론
즉 아마 검색된 채널이 줄어든 이유는 일부 채널이 자동으로 아카아빙되고,
현재 기능에서는 활성화된 채널만 집계되기에 줄어든 것이라 결론