kimcore / chzzk Goto Github PK
View Code? Open in Web Editor NEW네이버 라이브 스트리밍 서비스 치지직의 비공식 API 라이브러리
License: MIT License
네이버 라이브 스트리밍 서비스 치지직의 비공식 API 라이브러리
License: MIT License
async disconnect() {
if (!this.connected) {
throw new Error('Not connected')
}
// 이하 코드 생략
}
현 시점 모듈 코드의 채팅disconnect()
메서드 코드 일부입니다. 위 코드 진행대로라면 계정(봇) 이 채팅에 연결되어 있지 않을 때 Not connected 오류가 발생하며 코드 진행이 중단되게 되는 것 같습니다.
disconnect() 메서드를 사용하지 않는다면 발생하지 않을 문제지만, chatChannelId
가 채널별 고정이 아닌 세션별 랜덤 생성이라는 점에서 disconnect()
메서드의 사용은 불가피할 것으로 판단됩니다.
위 코드를 오류가 발생하면 해당 오류를 알려주고 다시 시도 와 같이 프로그램이 다운되지 않도록 바꿔주실 수 있을까요?
프로그램 실행 중, 간헐적으로 채팅을 받아올 때 #3 와 똑같은 오류가 발생합니다.
환경 : Node.js 18, 19, 20, 21
| Bun 1.0.15, 1.0.16, 1.0.17, 1.0.18, 1.0.19, 1.0.20
이모티콘등을 전송할수있도록 sendChat 메서드 내에 extras를 수정할 수 있는 추가 인자를 추가해주셨으면 좋겠습니다
현재 v1 API에 들어가면, code 9004와 함께 "앱 업데이트 후에 정상 시청 가능합니다." 메시지가 나옵니다.
코드가 9004라면 v1에서 v2로, v2에서도 코드가 9004라면 v2에서 v3로 자동으로 올려주는 기능이 추가되면 모듈을 계속 업데이트하지 않아도 사용할 수 있을 것 같아 편리할 것 같습니다.
방송이 켜져있을 때 친 채팅은 제대로 나오지만,
방송이 꺼진 후의 채팅은 나오지 않는 것으로 보입니다.
이로 인해 방송이 켜질 때마다 코드를 재실행해줘야하는 문제가 있습니다.
이 문제를 수정하면 채팅창에서 치지직 채팅창이 방송을 시작했을 때 새로고침을 하지 않아도 제대로 채팅이 나올 것 같습니다.
https://comm-api.game.naver.com/nng_main/v1/chats/access-token?channelId={채널 ID}&chatType=STREAMING
{
"code": 50001,
"message": "서버 오류입니다.",
"content": null
}
금일 1:00AM 까지는 정상 동작 확인했습니다.
(테스트 영상: https://chzzk.naver.com/video/2256053)
현재는 해당 요청이 500을 반환하고 있습니다. 확인부탁드립니다.
live.d.ts쪽 코드
export interface LiveSettingParams {
adult: boolean;
categoryType?: string;
chatActive: boolean;
chatAvailableGroup: string;
defaultLiveTitle: string;
defaultThumbnailImageUrl?: string;
liveCategory?: string;
paidPromotion: boolean;
}
export interface LiveSetting {
defaultLiveTitle: string;
category: {
categoryType?: "GAME" | "ETC";
liveCategory?: string;
liveCategoryName?: string;
};
defaultThumbnailImageUrl?: string;
chatActive: boolean;
chatAvailableGroup: string;
paidPromotion: boolean;
adult: boolean;
chatAvailableCondition: string;
minFollowerMinute: number;
}
LiveDetail, LiveSetting에는 chatAvailableCondition이 제대로 존재하지만, LiveSettingParams에는 누락되어 있습니다.
채널 관리자의 방제 변경, 카테고리 변경, 유저 활동제한 등의 api도 업데이트 계획이 있을까요?
https://api.chzzk.naver.com/manage/v1/channels/${channelId}/restrict-users/${userIdHash}
예를 들어서
소스코드 중에서
| return this.client.fetch(/manage/v1/channels/${channelId}/temporary-restrict-users
, {
| method: "POST",
| headers: { "Content-Type": "application/json" },
| body: JSON.stringify({ targetId })
이 부분에서 temporary-restrict-users로 post를 보낼때 body가 chatchannelid도 포함되어야 하는게 아닌가요?
client.chat을 이용하고 있는도중 간헐적인 에러가 발생합니다.
정확한 원인은 callstack이 명확하게 표시되지 않아서 알기어렵지만 아래와 같은 로그가 표시됩니다.
node:internal/deps/undici/undici:12344
Error.captureStackTrace(err, this);
^
TypeError: fetch failed
at node:internal/deps/undici/undici:12344:11
at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
cause: Error: read ECONNRESET
at TLSWrap.onStreamRead (node:internal/stream_base_commons:217:20) {
errno: -4077,
code: 'ECONNRESET',
syscall: 'read'
}
}
fetch failed
임을 봤을때 요청 관련 에러로 보이며
ECONNRESET
code는 반대편에서 연결을 끊었을때 발생합니다.
아래 에러 로그처럼 간헐적으로 발생합니다 (로그 시간 참고)
모든 에러의 초가 14초, 44초로 30초 간격으로 맞아떨어짐으로 짐작하자면 스케줄링된 polling 관련 로직이 아닐까 합니다.
/user/.pm2/logs/project-error-23.log last 200 lines:
23|project | 2024-02-08T14:23:14: TypeError: fetch failed
23|project | 2024-02-08T14:23:14: at node:internal/deps/undici/undici:12344:11
23|project | 2024-02-08T14:23:14: at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
23|project | 2024-02-08T14:23:14: cause: Error: read ECONNRESET
23|project | 2024-02-08T14:23:14: at TLSWrap.onStreamRead (node:internal/stream_base_commons:217:20) {
23|project | 2024-02-08T14:23:14: errno: -104,
23|project | 2024-02-08T14:23:14: code: 'ECONNRESET',
23|project | 2024-02-08T14:23:14: syscall: 'read'
23|project | 2024-02-08T14:23:14: }
23|project | 2024-02-08T14:23:14: }
23|project | 2024-02-08T14:34:14: TypeError: fetch failed
23|project | 2024-02-08T14:34:14: at node:internal/deps/undici/undici:12344:11
23|project | 2024-02-08T14:34:14: at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
23|project | 2024-02-08T14:34:14: cause: Error: read ECONNRESET
23|project | 2024-02-08T14:34:14: at TLSWrap.onStreamRead (node:internal/stream_base_commons:217:20) {
23|project | 2024-02-08T14:34:14: errno: -104,
23|project | 2024-02-08T14:34:14: code: 'ECONNRESET',
23|project | 2024-02-08T14:34:14: syscall: 'read'
23|project | 2024-02-08T14:34:14: }
23|project | 2024-02-08T14:34:14: }
23|project | 2024-02-08T14:45:44: TypeError: fetch failed
23|project | 2024-02-08T14:45:44: at node:internal/deps/undici/undici:12344:11
23|project | 2024-02-08T14:45:44: at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
23|project | 2024-02-08T14:45:44: cause: Error: read ECONNRESET
23|project | 2024-02-08T14:45:44: at TLSWrap.onStreamRead (node:internal/stream_base_commons:217:20) {
23|project | 2024-02-08T14:45:44: errno: -104,
23|project | 2024-02-08T14:45:44: code: 'ECONNRESET',
23|project | 2024-02-08T14:45:44: syscall: 'read'
23|project | 2024-02-08T14:45:44: }
23|project | 2024-02-08T14:45:44: }
23|project | 2024-02-08T14:55:44: TypeError: fetch failed
23|project | 2024-02-08T14:55:44: at node:internal/deps/undici/undici:12344:11
23|project | 2024-02-08T14:55:44: at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
23|project | 2024-02-08T14:55:44: cause: Error: read ECONNRESET
23|project | 2024-02-08T14:55:44: at TLSWrap.onStreamRead (node:internal/stream_base_commons:217:20) {
23|project | 2024-02-08T14:55:44: errno: -104,
23|project | 2024-02-08T14:55:44: code: 'ECONNRESET',
23|project | 2024-02-08T14:55:44: syscall: 'read'
23|project | 2024-02-08T14:55:44: }
23|project | 2024-02-08T14:55:44: }
23|project | 2024-02-08T15:19:44: TypeError: fetch failed
23|project | 2024-02-08T15:19:44: at node:internal/deps/undici/undici:12344:11
23|project | 2024-02-08T15:19:44: at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
23|project | 2024-02-08T15:19:44: cause: Error: read ECONNRESET
23|project | 2024-02-08T15:19:44: at TLSWrap.onStreamRead (node:internal/stream_base_commons:217:20) {
23|project | 2024-02-08T15:19:44: errno: -104,
23|project | 2024-02-08T15:19:44: code: 'ECONNRESET',
23|project | 2024-02-08T15:19:44: syscall: 'read'
23|project | 2024-02-08T15:19:44: }
23|project | 2024-02-08T15:19:44: }
23|project | 2024-02-08T15:32:14: TypeError: fetch failed
23|project | 2024-02-08T15:32:14: at node:internal/deps/undici/undici:12344:11
23|project | 2024-02-08T15:32:14: at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
23|project | 2024-02-08T15:32:14: cause: Error: read ECONNRESET
23|project | 2024-02-08T15:32:14: at TLSWrap.onStreamRead (node:internal/stream_base_commons:217:20) {
23|project | 2024-02-08T15:32:14: errno: -104,
23|project | 2024-02-08T15:32:14: code: 'ECONNRESET',
23|project | 2024-02-08T15:32:14: syscall: 'read'
23|project | 2024-02-08T15:32:14: }
23|project | 2024-02-08T15:32:14: }
23|project | 2024-02-08T15:42:14: TypeError: fetch failed
23|project | 2024-02-08T15:42:14: at node:internal/deps/undici/undici:12344:11
23|project | 2024-02-08T15:42:14: at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
23|project | 2024-02-08T15:42:14: cause: Error: read ECONNRESET
23|project | 2024-02-08T15:42:14: at TLSWrap.onStreamRead (node:internal/stream_base_commons:217:20) {
23|project | 2024-02-08T15:42:14: errno: -104,
23|project | 2024-02-08T15:42:14: code: 'ECONNRESET',
23|project | 2024-02-08T15:42:14: syscall: 'read'
23|project | 2024-02-08T15:42:14: }
23|project | 2024-02-08T15:42:14: }
23|project | 2024-02-08T18:36:14: TypeError: fetch failed
23|project | 2024-02-08T18:36:14: at node:internal/deps/undici/undici:12344:11
23|project | 2024-02-08T18:36:14: at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
23|project | 2024-02-08T18:36:14: cause: Error: read ECONNRESET
23|project | 2024-02-08T18:36:14: at TLSWrap.onStreamRead (node:internal/stream_base_commons:217:20) {
23|project | 2024-02-08T18:36:14: errno: -104,
23|project | 2024-02-08T18:36:14: code: 'ECONNRESET',
23|project | 2024-02-08T18:36:14: syscall: 'read'
23|project | 2024-02-08T18:36:14: }
23|project | 2024-02-08T18:36:14: }
위 정황들로 파악했을때
api / live.ts > ChzzkLive > status > fetch(`/polling/v2/channels/${channelId}/live-status`)
에서 발생하는것으로 추측됩니다
.catch(()=>{ //logic })
을 추가하여 관련 핸들링이 필요해보입니다
어느 방식이 적절한가요?
const chzzkManage = new ChzzkManage(client);
await chzzkManage.temporaryRestrict(channel.channelId, chzzkChat.chatChannelId, chat.profile.userIdHash)
await client.manage.temporaryRestrict(channel.channelId, chzzkChat.chatChannelId, chat.profile.userIdHash)
두방식 둘다 안돼서 조언을 받고 싶습니다.
혹시 cookie 에서 수동으로 복사 붙여넣기 하는 방법 이외에는 로그인 할수 있는 방법이 아직 없을까요?
그리고 chat 기능은 로그인이 필수일까요? (read만 하려고하는 경우)
감사합니다.
메시지 고정과 메시지 블라인드 방법좀 알려주세요~~
멤버 차단 부분도여
/service/v1/home/recommendation-channels
로 랜덤한 추천 채널을 받을 수 있는것으로 보여 해당 api를 통한 채널 추천 값을 받을수있도록 하는 기능이 추가되면 어떨까 제안드립니다.
클라이언트 앱에서 본 비공식 API 사용 시, 개발 단계에서 로컬 프록시를 이용할 수 있도록
ChzzkClient 객체 생성 시점에 API 주소를 주입 받을 수 있게 개선하면 좋을 것 같습니다.
예를 들어 http-proxy-middleware
라이브러리를 사용하면
별도의 node 서버를 세팅하지 않고도 로컬 프록시 구조를 만들 수 있습니다.
아래와 같은 프록시 구조를 만들기 위해서는
client --> /api
--> 프록시서버 --> https://api.chzzk.naver.com
이와 같은 코드를 작성하면 됩니다.
const { createProxyMiddleware } = require("http-proxy-middleware");
module.exports = function (app) {
app.use(
"/api",
createProxyMiddleware({
target: "https://api.chzzk.naver.com",
changeOrigin: true,
pathRewrite: { "^/api": "" },
})
);
};
위 프록시 세팅 코드는
fetch("/api") 요청을 프록시 서버가 받아들이고
프록시 서버가 다시 /api
요청을 그대로 https://api.chzzk.naver.com
로 보내서
응답을 반환하도록 하는 세팅입니다.
하지만 위와 같은 로컬 프록시 라이브러리가 동작하도록 하려면
ChzzkClient 가 로컬 프록시 주소를 주입받을 수 있도록 코드를 변경해야 합니다.
현재는 ChzzkClient 가 고정적으로 consts.ts
로 부터 네이버측 api 주소를 받아서 fetch 하고 있습니다.
대신 ChzzkClient 가 생성 시점이나 세팅 메서드를 통해서 프록시 주소 /api
를 받아서 fetch 할 수 있도록 개선하면 좋을 것 같습니다.
Bun 최신 버전, Node.js 21에서 공통적으로 발생하는 문제입니다.
252 | this.startPingTimer();
253 | }
254 | });
255 | }
256 | parseChat(chat, isRecent = false) {
257 | const profile = JSON.parse(chat['profile']);
^
SyntaxError: JSON Parse error: Unexpected identifier "undefined"
at parseChat (/home/ubuntu/chzzkbot/node_modules/chzzk/dist/chat/chat.js:257:25)
at /home/ubuntu/chzzkbot/node_modules/chzzk/dist/chat/chat.js:243:41
at /home/ubuntu/chzzkbot/node_modules/chzzk/dist/chat/chat.js:8:9
at new Promise (:1:21)
at /home/ubuntu/chzzkbot/node_modules/chzzk/dist/chat/chat.js:4:12
undefined:1
undefined
^
SyntaxError: "undefined" is not valid JSON
at JSON.parse (<anonymous>)
at ChzzkChat.parseChat (/home/ubuntu/chzzkbot/node_modules/chzzk/dist/chat/chat.js:257:30)
at ChzzkChat.<anonymous> (/home/ubuntu/chzzkbot/node_modules/chzzk/dist/chat/chat.js:243:46)
at Generator.next (<anonymous>)
at /home/ubuntu/chzzkbot/node_modules/chzzk/dist/chat/chat.js:8:71
at new Promise (<anonymous>)
at __awaiter (/home/ubuntu/chzzkbot/node_modules/chzzk/dist/chat/chat.js:4:12)
at ChzzkChat.handleMessage (/home/ubuntu/chzzkbot/node_modules/chzzk/dist/chat/chat.js:196:16)
at callListener (/home/ubuntu/chzzkbot/node_modules/ws/lib/event-target.js:290:14)
at WebSocket.onMessage (/home/ubuntu/chzzkbot/node_modules/ws/lib/event-target.js:209:9)
Node.js v21.6.1
메시지가 고정 해제되었을 때에 대한 예외처리가 필요해 보입니다.
node_modules/chzzk/dist/chat/chat.js:146
for (const chat of chats) {
^
TypeError: chats is not iterable
at ChzzkChat.<anonymous> (node_modules/chzzk/dist/chat/chat.js:146:40)
at Generator.next (<anonymous>)
at node_modules/chzzk/dist/chat/chat.js:8:71
at new Promise (<anonymous>)
at __awaiter (node_modules/chzzk/dist/chat/chat.js:4:12)
at ChzzkChat.handleMessage (node_modules/chzzk/dist/chat/chat.js:124:16)
at callListener (node_modules/ws/lib/event-target.js:290:14)
at WebSocket.onMessage (node_modules/ws/lib/event-target.js:209:9)
at WebSocket.emit (node:events:517:28)
at Receiver.receiverOnMessage (node_modules/ws/lib/websocket.js:1203:20)
Node.js 18, 19, 20, 21 모두 오류가 발생합니다.
프론트엔드에서 chatChannelId polling / reconnect가 가능한지 궁금합니다.
가능하다면 방법을 알려주시면 감사하겠습니다.
채팅에서 구독 뱃지 관련 데이터를 가져오기 위해 StreamingProperty에 subscription을 정의해야 할 것 같습니다.
chat/types.ts 쪽에서 DonationExtras 부분에 donationType 정의가 필요할 것 같습니다.
donationType은 "CHAT"과 "VIDEO"로 이루어져 있습니다.
export interface DonationExtras extends Extras {
payType: string
payAmount: number
weeklyRankList: DonationRank[],
donationUserWeeklyRank: number
}
export interface DonationExtras extends Extras {
donationType: "CHAT" | "VIDEO"
payType: string
payAmount: number
weeklyRankList: DonationRank[]
donationUserWeeklyRank: number
}
chzzkChat.on으로 donation 이벤트를 수신할 때, 처음 시작 시에만 작동하고 chat 이벤트처럼 이벤트가 감지될때마다 새로 올라오지 않는 문제가 있습니다. systemMessage도 똑같을 것으로 예상됩니다.
// 유저의 팔로우 일시 불러오기
// client.chat.profileCard(chzzkChat.chatChannelId, chat.profile.userIdHash).then(profile => {
// console.log(profile.following ? `${profile.following.followDate} 에 팔로우 함` : "팔로우 안함")
// })
위 예시를 보면 profile.following
과 profile.following.followDate
가 있습니다.
하지만, 실제 API를 보면 profile.streamingProperty.following
과 profile.streamingProperty.following.followDate
로 되어있습니다.
위 텍스트를 수정해주시면 감사하겠습니다.
현재 livePlaybackJson은 모듈 내에서 "livePlayback"으로 되어있습니다.
livePlaybackJson으로 변경이 필요할 듯 합니다.
export interface Live {
liveTitle: string;
liveImageUrl: string;
defaultThumbnailImageUrl?: string;
concurrentUserCount: number;
accumulateCount: number;
openDate: string;
liveId: number;
adult: boolean;
chatChannelId: string;
categoryType?: string;
liveCategory?: string;
liveCategoryValue?: string;
channelId: string;
livePlayback: LivePlayback;
channel: PartialChannel;
}
export interface Live {
liveTitle: string;
liveImageUrl: string;
defaultThumbnailImageUrl?: string;
concurrentUserCount: number;
accumulateCount: number;
openDate: string;
liveId: number;
adult: boolean;
chatChannelId: string;
categoryType?: string;
liveCategory?: string;
liveCategoryValue?: string;
channelId: string;
livePlaybackJson: LivePlayback;
channel: PartialChannel;
}
client.search.channels("asdf") 코드를 넣었다고 했을 때,
asdf의 방송이 꺼지면 프로그램 실행이 중단되는 문제가 있습니다.
스트리머의 chatChannel 에서 특정 user의 followDate 값을 가져오고 싶은데 api를 추가해주실 수 있나요?
https://comm-api.game.naver.com/nng_main/v1/chats/${chatChannelId}/users/${userIdHash}/profile-card?chatType=STREAMING
검색 결과가 없을 때 content.page
가 null로 대체되는데,
이것이 제대로 처리되지 않아 content.page.next.offset
을 불러오려 할 때 오류가 발생합니다.
https://github.com/kimcore/chzzk/blob/main/src/api/search.ts#L59
https://github.com/kimcore/chzzk/blob/main/src/api/search.ts#L81
https://github.com/kimcore/chzzk/blob/main/src/api/search.ts#L109
추가로, search
메소드의 반환 타입을 제대로 지정하여 추후 이런 문제가 재발하지 않도록 하는것도 좋아보이네요.
Node.js v21.5.0
undefined:1
undefined
^
SyntaxError: "undefined" is not valid JSON
at JSON.parse (<anonymous>)
at /node_modules/chzzk/dist/api/live.js:48:55
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async /index.js:41:24
Node.js v21.5.0
Bun v1.0.21
43 | const livePlaybackJson = content['livePlaybackJson'];
44 | const livePlayback = livePlaybackJson ? JSON.parse(livePlaybackJson) : null;
45 | delete content['livePlaybackJson'];
46 | const channel = content['channel'];
47 | const channelLivePollingStatusJson = channel['livePollingStatusJson'];
48 | const channelLivePollingStatus = JSON.parse(channelLivePollingStatusJson);
^
SyntaxError: JSON Parse error: Unexpected identifier "undefined"
at /node_modules/chzzk/dist/api/live.js:48:50
at processTicksAndRejections (:61:77)
1.1.1 버전까지는 발생하지 않던 오류였으나, 1.1.2 버전에서 오류가 발생합니다.
const liveDetail = await client.live.detail(channel.channelId);
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.