buzzk 2.0.1 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,16 +9,13 @@
9
9
  <p align="center">
10
10
  뿌지직은 치지직 챗봇을 더욱 쉽게 개발할 수 있도록 돕는 비공식 라이브러리 입니다.
11
11
  </p>
12
- <p align="center">
13
- 챗봇 개발에 초점이 맞춰저 있어, 여러 계정에 동시 로그인할 수 없습니다.
14
- </p>
15
12
 
16
13
  ---
17
14
 
18
15
  ## 📖 업데이트 내역
19
16
 
20
- ### 🎉 2.0 업데이트
21
- - 공식 API 일부 지원하기 시작했어요!
17
+ ### 🎉 2.1 업데이트
18
+ - 공식 API 거의 대부분의 작업을 대체했어요!
22
19
 
23
20
  > [!CAUTION]
24
21
  > 이 버전 이후부터는 공식 API와 비공식 API를 혼용하여 사용합니다.
@@ -29,6 +26,62 @@
29
26
  > * 비공식 API로만 이루어진 모듈을 사용하시려면
30
27
  > `npm install buzzk@1.11.3`
31
28
 
29
+ ---
30
+
31
+ ## ✒️ 마이그레이션 가이드 (v.2.0.x -> v.2.1.0)
32
+
33
+ <details>
34
+ <summary>펼쳐보기</summary>
35
+
36
+ buzzkChat
37
+
38
+ | <img src="https://github.com/Emin-G/Img/blob/main/tags/tag_change-min.png?raw=true" alt="BUZZK" width="70"> | new buzzkChat("channelID 값") |
39
+ |--|--|
40
+ | | new buzzkChat("accessToken 값") |
41
+
42
+ ---
43
+
44
+ | <img src="https://github.com/Emin-G/Img/blob/main/tags/tag_delete-min.png?raw=true" alt="BUZZK" width="70"> | 삭제 |
45
+ |--|--|
46
+ | | (chat).getRecentChat() |
47
+
48
+ ---
49
+
50
+ buzzkChat.onMessage
51
+
52
+ > 채팅 여러개를 한 번에 받던 구조 (data[o].message) (for 문으로 data[o] 돌리던 구조)에서
53
+ > 채팅 하나 씩 받는 구조로 변경 (data.message)
54
+
55
+ | <img src="https://github.com/Emin-G/Img/blob/main/tags/tag_delete-min.png?raw=true" alt="BUZZK" width="70"> | 삭제 |
56
+ |--|--|
57
+ | | (callback).author.imageURL |
58
+
59
+ ---
60
+
61
+ buzzkChat.onDonation
62
+
63
+ | <img src="https://github.com/Emin-G/Img/blob/main/tags/tag_delete-min.png?raw=true" alt="BUZZK" width="70"> | 삭제 |
64
+ |--|--|
65
+ | | (callback).author.imageURL |
66
+
67
+ ---
68
+
69
+ | <img src="https://github.com/Emin-G/Img/blob/main/tags/tag_delete-min.png?raw=true" alt="BUZZK" width="70"> | 삭제 |
70
+ |--|--|
71
+ | | (callback).time |
72
+
73
+ ---
74
+
75
+ buzzk.live
76
+
77
+ | <img src="https://github.com/Emin-G/Img/blob/main/tags/tag_delete-min.png?raw=true" alt="BUZZK" width="70"> | 삭제 |
78
+ |--|--|
79
+ | | buzzk.live.getAccess() |
80
+
81
+ </details>
82
+
83
+ ---
84
+
32
85
  ## ✒️ 마이그레이션 가이드 (v.1.x -> v.2.0.0)
33
86
 
34
87
  <details>
@@ -83,37 +136,37 @@
83
136
 
84
137
  const buzzk = require("buzzk");
85
138
  buzzk.auth("ClientID 값", "ClientSecret 값");
86
- buzzk.login("NID_AUT 쿠키 값", "NID_SES 쿠키 값");
139
+
140
+ //buzzk.login("NID_AUT 쿠키 값", "NID_SES 쿠키 값");
141
+ //buzzk.channel.follow, unfollow 시에만 사용.
87
142
 
88
143
  const buzzkChat = buzzk.chat;
89
144
 
90
145
  async function test () {
91
146
 
92
- let chSearch = await buzzk.channel.search("녹두로로"); //채널 검색
93
-
94
- let channel = chSearch[0]; //검색 결과 첫번째 채널
95
-
96
- const lvDetail = await buzzk.live.getDetail(channel.channelID); //현재 방송 정보
147
+ let oauth = buzzk.oauth.get("code 값");
97
148
 
98
- let chat = new buzzkChat(channel.channelID);
149
+ let chat = new buzzkChat(oauth.access);
99
150
  await chat.connect(); //채팅창 연결
100
151
 
101
- let recentChat = await chat.getRecentChat(); //최근 채팅 가져오기 (기본값 50개)
102
- console.log(recentChat);
103
-
104
152
  chat.onMessage(async (data) => { //채팅이 왔을 때
105
- for (let o in data) {
106
- console.log(data[o].message);
153
+ console.log(data.message);
154
+
155
+ if (data.message == "!ping") await chat.send("pong!");
156
+ //채팅 보내기
107
157
 
108
- if (data[o].message === "!ping") await chat.send("pong!");
109
- //채팅 보내기 (login 후에만 가능)
158
+ if (data.message == "!공지") await chat.setNotice("테스트!");
159
+ //채팅 상단 고정 (공지)
110
160
 
111
- let userInfo = await chat.getUserInfo(data[o].author.id);
112
- console.log(userInfo);
113
- //채팅 보낸 유저의 정보
114
- }
161
+ let userInfo = await chat.getUserInfo(data.author.id);
162
+ console.log(userInfo);
163
+ //채팅 보낸 유저의 정보
115
164
  });
116
165
 
166
+ chat.onDonation(async (data) => { //후원이 왔을 때
167
+ //TODO
168
+ });
169
+
117
170
  chat.onDisconnect(async () => { //채팅창 연결이 끊겼을 때
118
171
  //TODO
119
172
  });
@@ -144,6 +197,7 @@ dotenv와 함께 사용하는 것을 매우 권장합니다.
144
197
 
145
198
  buzzk.login("NID_AUT 쿠키 값", "NID_SES 쿠키 값");
146
199
 
200
+ * 해당 함수는 이제 buzzk.channel.follow, unfollow 시에만 사용됩니다.
147
201
  dotenv와 함께 사용하는 것을 매우 권장합니다.
148
202
 
149
203
  buzzk.login(process.env.NID_AUT, process.env.NID_SES);
@@ -298,95 +352,69 @@ dotenv와 함께 사용하는 것을 매우 권장합니다.
298
352
 
299
353
  ### chat
300
354
 
355
+ ✅ Official API
356
+
301
357
  const buzzkChat = buzzk.chat;
302
- let chat = new buzzkChat("channelID 값");
358
+ let chat = new buzzkChat("accessToken 값");
303
359
  await chat.connect(); //채팅창 연결
304
360
 
305
361
  >
306
362
 
307
- let recentChat = await chat.getRecentChat(갯수); //최근 채팅 가져오기 (기본값 50개)
308
- console.log(recentChat);
309
-
310
- <details>
311
- <summary>return</summary>
312
-
313
- - Return
314
- - 0
315
- - author
316
- - id
317
- - name
318
- - imageURL
319
- - hasMod //관리 권한을 가졌는지 (false / true)
320
- - message
321
- - time
322
- - 1
323
- - 2
324
- - 3
325
- - ...
326
-
327
- </details>
363
+ Official API
328
364
 
329
365
  chat.onMessage((data) => { //채팅이 왔을 때
330
366
  console.log(data);
331
-
332
- for (let o in data) {
333
- console.log(data[o].message); //메세지만 전부 꺼내기
334
- }
335
367
  });
336
368
 
337
369
  <details>
338
370
  <summary>callback</summary>
339
371
 
340
372
  - Return
341
- - 0
342
373
  - author
343
374
  - id
344
375
  - name
345
- - imageURL
346
376
  - hasMod //관리 권한을 가졌는지 (false / true)
347
377
  - message
378
+ - emoji (Object - Key: 이모지 이름, Value: 이미지 URL)
348
379
  - time
349
- - 1
350
- - 2
351
- - 3
352
- - ...
353
380
 
354
381
  </details>
355
382
 
383
+ >
384
+
385
+ ✅ Official API
386
+
356
387
  chat.onDonation((data) => { //도네이션이 왔을 때
357
388
  console.log(data);
358
-
359
- for (let o in data) {
360
- console.log(data[o].amount); //후원 금액만 전부 꺼내기
361
- }
362
389
  });
363
390
 
364
391
  <details>
365
392
  <summary>callback</summary>
366
393
 
367
394
  - Return
368
- - 0
369
- - amount //후원 금액
370
- - author
371
- - id
372
- - name
373
- - imageURL
374
- - hasMod //관리 권한을 가졌는지 (false / true)
375
- - message
376
- - time
377
- - 1
378
- - 2
379
- - 3
380
- - ...
395
+ - type //CHAT or VIDEO
396
+ - amount //후원 금액
397
+ - author
398
+ - id
399
+ - name
400
+ - hasMod //관리 권한을 가졌는지 (false / true)
401
+ - message
402
+ - emoji (Object - Key: 이모지 이름, Value: 이미지 URL)
381
403
 
382
404
  </details>
383
405
 
406
+ >
407
+
408
+ ✅ Official API
409
+
384
410
  chat.onDisconnect(() => { //채팅창 연결이 끊겼을 때
385
411
  //TODO
386
412
  });
387
413
 
388
414
  >
389
415
 
416
+ ✅ Official API
417
+
390
418
  await chat.send("ㅋㅋㅋㅋㅋㅋ"); //채팅 보내기 (login 후에만 가능)
391
419
 
392
420
  >
package/lib/chat.js CHANGED
@@ -1,33 +1,54 @@
1
1
  const { getStatus } = require("./live.js");
2
- const { reqChzzk, reqGame } = require("./tool.js");
3
- const vm = require("./vm.js");
2
+ const oauth = require("./oauth.js");
3
+ const { USER, reqGame } = require("./tool.js");
4
4
 
5
- const { WebSocket } = require("ws");
5
+ const io = require("socket.io-client");
6
+
7
+ class chzzkChatData {
8
+ constructor(id, name, hasMod, message, emojis, time) {
9
+ this.author = {
10
+ id: id,
11
+ name: name,
12
+ hasMod: hasMod
13
+ };
14
+ this.message = message;
15
+ this.emojis = emojis;
16
+ this.time = time;
17
+ }
18
+ }
19
+
20
+ class chzzkDonationData {
21
+ constructor(type, amount, id, name, hasMod, message, emojis) {
22
+ this.type = type;
23
+ this.amount = amount;
24
+ this.author = {
25
+ id: id,
26
+ name: name,
27
+ hasMod: hasMod
28
+ };
29
+ this.message = message;
30
+ this.emojis = emojis;
31
+ }
32
+ }
6
33
 
7
34
  class chzzkChat {
8
- constructor(channelID) {
9
- this.channelID = channelID;
35
+ constructor(accessToken) {
36
+ this.accessToken = accessToken;
10
37
  }
11
38
 
12
39
  //Private
13
40
  #ws; //Chat Web Socket
14
- #ssID; //kr-ss?.chat.naver.com/chat
15
- #accTkn; //Account Access Token
16
- #extTkn //for RealName Auth
17
- #svcid; //game
18
- #uid;
19
- #sid;
20
- #chatID;
21
- #callbacks = new Map();
41
+ #channelID;
42
+ #session;
43
+ #callbacks = {
44
+ message: null,
45
+ donation: null,
46
+ disconnect: null
47
+ };
22
48
 
23
49
  #status = {
24
- polling: false,
25
- pollingID: 0,
26
- ping: false,
27
- pingID: 0,
28
50
  ws: false,
29
- wsID: 0,
30
- reconnect: false
51
+ established: false
31
52
  }
32
53
  //Private
33
54
 
@@ -36,176 +57,105 @@ class chzzkChat {
36
57
  */
37
58
  connect() {
38
59
  return new Promise(async (resolve, reject) => {
39
- if (this.#status.ws) return resolve(null);
40
- if (this.#status.polling && !this.#status.reconnect) return resolve(null);
41
- else this.#status.reconnect = false;
42
-
43
- //Get ChatID
44
- let cidRes = await getStatus(this.channelID);
45
- if (!cidRes) {
46
- if (!this.#status.polling) {
47
- this.#status.polling = true;
48
- this.#polling();
49
- }
60
+
61
+ if (this.#status.ws) return resolve(false);
62
+ this.#status.ws = true;
63
+
64
+ let channel = await oauth.resolve(this.accessToken);
65
+ if (!channel) {
66
+ this.#status.ws = false;
50
67
  return resolve(null);
51
68
  }
52
- this.#chatID = cidRes.chatID;
53
- //Get ChatID
69
+ this.#channelID = channel.channelID;
54
70
 
55
- //Set Status
56
- this.#status.ws = true;
57
- //Set Status
58
-
59
- //Get UID
60
- let myInfo = await reqGame("nng_main/v1/user/getUserStatus");
61
- if (myInfo.code != 200) return resolve(null);
62
- this.#uid = myInfo.content.userIdHash;
63
- //Get UID
64
-
65
- //Get accTkn
66
- let accRes = await reqGame("nng_main/v1/chats/access-token?channelId=" + this.#chatID + "&chatType=STREAMING");
67
- if (accRes.code != 200) return resolve(null);
68
- this.#accTkn = accRes.content.accessToken;
69
- this.#extTkn = accRes.content.extraToken;
70
- //Get accTkn
71
-
72
- //Load Balancing
73
- this.#ssID = Math.floor(Math.random() * 10) + 1;
74
- //Load Balancing
75
-
76
- //Connect Web Socket
77
- this.#ws = new WebSocket("wss://kr-ss" + this.#ssID + ".chat.naver.com/chat", {
78
- handshakeTimeout: 60000
79
- });
80
- this.#status.wsID++;
81
- //Connect Web Socket
71
+ let session = await USER.get(this.accessToken, "open/v1/sessions/auth");
72
+ if (!session || session.code != 200) {
73
+ this.#status.ws = false;
74
+ return resolve(null);
75
+ }
76
+ session = session.content.url;
77
+ console.log(session);
78
+
79
+ const socketOption = {
80
+ reconnection: false,
81
+ "force new connection": true,
82
+ "connect timeout": 6000,
83
+ transports: ["websocket"]
84
+ };
85
+
86
+ this.#ws = io.connect(session, socketOption);
82
87
 
83
- //WS Open
84
- await this.#openHandler(this.#status.wsID);
85
- //WS Open
88
+ this.#ws.on("connect", async (data) => {
89
+ console.log("[WS] Connected!");
90
+ });
86
91
 
87
- this.#ws.on("error", console.error);
92
+ this.#ws.on("SYSTEM", async (data) => {
93
+ data = JSON.parse(data);
94
+ console.log(data);
88
95
 
89
- this.#closeHandler(this.#status.wsID);
96
+ if (data.type == "connected") {
97
+ this.#session = data.data.sessionKey;
98
+
99
+ let subChat = await USER.post(this.accessToken, "open/v1/sessions/events/subscribe/chat?sessionKey=" + this.#session, null);
100
+ let subDona = await USER.post(this.accessToken, "open/v1/sessions/events/subscribe/donation?sessionKey=" + this.#session, null);
101
+ if (!subChat || subChat.type == "subscribed" || !subDona || subDona.type == "subscribed") return resolve(null);
102
+ this.#status.established = true;
103
+ console.log("[WS] Subscribed!");
104
+ return resolve(true);
105
+ }
106
+ });
90
107
 
91
- //WS Message
92
- await this.#connectHandler(this.#status.wsID);
93
- return resolve(true);
94
- //WS Message
108
+ this.#ws.on("CHAT", (data) => {
109
+ data = JSON.parse(data);
110
+ console.log(data);
95
111
 
96
- });
97
- }
112
+ let hasMod = false;
113
+ for (let o in data.profile.badges) {
114
+ if (data.profile.badges[o].imageUrl == "https://ssl.pstatic.net/static/nng/glive/icon/streamer.png") hasMod = true;
115
+ if (data.profile.badges[o].imageUrl == "https://ssl.pstatic.net/static/nng/glive/icon/manager.png") hasMod = true;
116
+ }
98
117
 
99
- getwsID() {
100
- return this.#status.wsID;
101
- }
118
+ let chatData = new chzzkChatData(data.senderChannelId, data.profile.nickname, hasMod, data.content, data.emojis, data.messageTime);
119
+ if (!this.#callbacks.message) return;
120
+ return this.#callbacks.message(chatData);
121
+ });
102
122
 
103
- getwsStatus() {
104
- return this.#status.ws;
105
- }
123
+ this.#ws.on("DONATION", (data) => {
124
+ data = JSON.parse(data);
125
+ console.log(data);
106
126
 
107
- #openHandler(wsID) {
108
- return new Promise(async (resolve, reject) => {
109
- this.#ws.on("open", () => {
110
- if (wsID != this.#status.wsID) return;
111
- console.log("[WS] Connected!");
127
+ let hasMod = false;
128
+ for (let o in data.profile.badges) {
129
+ if (data.profile.badges[o].imageUrl == "https://ssl.pstatic.net/static/nng/glive/icon/streamer.png") hasMod = true;
130
+ if (data.profile.badges[o].imageUrl == "https://ssl.pstatic.net/static/nng/glive/icon/manager.png") hasMod = true;
131
+ }
112
132
 
113
- //WS First Connect
114
- let connectOpt = {
115
- "ver": "3",
116
- "cmd": 100,
117
- "svcid": "game",
118
- "cid": this.#chatID,
119
- "bdy": {
120
- "uid": this.#uid,
121
- "devType":2001,
122
- "accTkn": this.#accTkn,
123
- "auth":"SEND",
124
- "devName": "BUZZK/" + vm.getVersion(),
125
- "libVer": vm.getVersion(),
126
- "locale": "ko",
127
- "osVer": "Windows/10",
128
- "timezone": "Asia/Seoul"
129
- },
130
- "tid": 1
131
- };
132
-
133
- this.#ws.send(JSON.stringify(connectOpt));
134
- return resolve(true);
135
- //WS First Connect
133
+ let donationData = new chzzkDonationData(data.donationType, data.payAmount, data.donatorChannelId, data.donatorNickname, hasMod, data.donationText, data.emojis);
134
+ return this.#callbacks.donation(donationData);
136
135
  });
137
- });
138
- }
139
136
 
140
- #closeHandler(wsID) {
141
- return new Promise(async (resolve, reject) => {
142
- this.#ws.on("close", () => {
143
- console.log("[WS] Disconnect!");
144
- if (wsID != this.#status.wsID) return;
145
- if (this.#status.reconnect) return;
146
- this.#ws = null;
147
- this.#status.ws = false;
148
- this.#status.wsID++;
149
- this.#status.ping = false;
150
- this.#status.pingID++;
151
- setTimeout(() => {
152
- this.#status.reconnect = true;
153
- this.connect();
154
- }, 2000);
137
+ this.#ws.on("error", (error) => {
138
+ console.log(error);
139
+
140
+ if (!this.#status.established) return resolve(null);
155
141
  });
156
- });
157
- }
158
142
 
159
- #connectHandler(wsID) {
160
- return new Promise(async (resolve, reject) => {
161
- this.#ws.on("message", async (data) => {
162
- if (wsID != this.#status.wsID) return;
143
+ this.#ws.on("disconnect", (data) => {
144
+ console.log("[WS] Disconnected! (" + data + ")");
163
145
 
164
- try {
165
- data = await JSON.parse(data);
166
- }
146
+ this.#status.ws = false;
147
+ this.#status.established = false;
148
+ this.#callbacks.disconnect(data);
167
149
 
168
- catch (error) {
169
- return;
170
- }
150
+ if (this.#ws) this.#ws.off();
151
+ if (!this.#status.established) resolve(null);
171
152
 
172
- //Update Var
173
- if (data.cid) this.#chatID = data.cid;
174
- if (data.svcid) this.#svcid = data.svcid;
175
- if (data.bdy && data.bdy.sid) this.#sid = data.bdy.sid;
176
- //Update Var
177
-
178
- //Ping Pong
179
- if (data.cmd == 0) {
180
- let pongOpt = {
181
- "ver": "2",
182
- "cmd": 10000
183
- };
184
- this.#ws.send(JSON.stringify(pongOpt));
185
- }
186
- //Ping Pong
187
-
188
- //Connected
189
- else if (data.cmd == 10100) {
190
- if (!this.#status.ping) {
191
- this.#status.ping = true;
192
- this.#status.pingID++;
193
- this.#ping(this.#status.pingID);
194
- }
195
- if (!this.#status.polling) {
196
- this.#status.polling = true;
197
- this.#polling();
198
- }
199
-
200
- for (let o in this.#callbacks) {
201
- if (this.#callbacks[o].type === "message") this.#onMessageHandler(this.#status.wsID, this.#callbacks[o].callback);
202
- else if (this.#callbacks[o].type === "donation") this.#onDonationHandler(this.#status.wsID, this.#callbacks[o].callback);
203
- }
204
-
205
- resolve(true);
206
- }
207
- //Connected
153
+ this.#ws = null;
154
+ this.#channelID = null;
155
+ this.#session = null;
156
+ return this.connect();
208
157
  });
158
+
209
159
  });
210
160
  }
211
161
 
@@ -215,299 +165,83 @@ class chzzkChat {
215
165
  */
216
166
  send(message) {
217
167
  return new Promise(async (resolve, reject) => {
218
- if (!this.#status.ws) return resolve(null);
168
+ if (!this.#ws || !this.#status.ws || !this.#status.established) return resolve(false);
169
+ console.log(message);
219
170
 
220
- let extras = {
221
- "chatType":"STREAMING",
222
- "osType":"PC",
223
- "streamingChannelId": this.channelID,
224
- "emojis":"",
225
- "extraToken": this.#extTkn
171
+ let reqMessage = {
172
+ message: message
226
173
  }
227
174
 
228
- //WS Send
229
- let date = new Date();
230
- let sendOpt = {
231
- "ver": "3",
232
- "cmd": 3101,
233
- "svcid": this.#svcid,
234
- "cid": this.#chatID,
235
- "sid": this.#sid,
236
- "retry": false,
237
- "bdy": {
238
- "msg": message,
239
- "msgTypeCode": 1,
240
- "extras": JSON.stringify(extras),
241
-
242
- "msgTime": date.getTime()
243
- },
244
- "tid": 3
245
- };
175
+ let chatRes = await USER.post(this.accessToken, "open/v1/chats/send", JSON.stringify(reqMessage));
176
+ if (!chatRes || chatRes.code != 200) return resolve(false);
246
177
 
247
- try {
248
- this.#ws.send(JSON.stringify(sendOpt));
249
- }
178
+ return resolve(true);
179
+ });
180
+ }
250
181
 
251
- catch (error) {
252
- return resolve(false);
182
+ /**
183
+ * @param {string} message
184
+ * @returns {Promise<boolean>}
185
+ */
186
+ setNotice(message) {
187
+ return new Promise(async (resolve, reject) => {
188
+ if (!this.#ws || !this.#status.ws || !this.#status.established) return resolve(false);
189
+
190
+ let reqMessage = {
191
+ message: message
253
192
  }
254
193
 
255
- return resolve(true);
256
- //WS Send
194
+ let noticeRes = await USER.post(this.accessToken, "open/v1/chats/notice", JSON.stringify(reqMessage));
195
+ if (!noticeRes || noticeRes.code != 200) return resolve(false);
257
196
 
197
+ return resolve(true);
258
198
  });
259
199
  }
260
200
 
261
201
  /**
262
- * @typedef {Object.<number, chzzkMessage>} chzzkMessages
202
+ * @typedef {Object} chzzkMessage
203
+ * @property {chzzkAuthor} author
204
+ * @property {string} message
205
+ * @property {Object} emoji
206
+ * @property {number} time
263
207
  */
264
208
 
265
209
  /**
266
- * @typedef {Object} chzzkMessage
267
- * @property {chzzkMessageAuthor} author
210
+ * @typedef {Object} chzzkDonation
211
+ * @property {string} type
212
+ * @property {string} amount
213
+ * @property {chzzkAuthor} author
268
214
  * @property {string} message
215
+ * @property {Object} emoji
269
216
  * @property {number} time
270
217
  */
271
218
 
272
219
  /**
273
- * @typedef {Object} chzzkMessageAuthor
220
+ * @typedef {Object} chzzkAuthor
274
221
  * @property {string} id
275
222
  * @property {string} name
276
- * @property {string} imageURL
277
223
  * @property {boolean} hasMod
278
224
  */
279
225
 
280
226
  /**
281
- * @param {function(chzzkMessages)} callback
227
+ * @param {function(chzzkMessage)} callback
282
228
  */
283
229
  onMessage(callback) {
284
- this.#callbacks[Object.keys(this.#callbacks).length] = {
285
- type: "message",
286
- callback: callback
287
- };
288
- this.#onMessageHandler(this.#status.wsID, callback);
289
- }
290
-
291
- #onMessageHandler (wsID, callback) {
292
- if (!this.#status.ws) return callback(null);
293
-
294
- this.#ws.on("message", async (data) => {
295
- if (wsID != this.#status.wsID) return;
296
-
297
- try {
298
- data = await JSON.parse(data);
299
- }
300
-
301
- catch (error) {
302
- return;
303
- }
304
-
305
- if (data.cmd == 93101) {
306
-
307
- data = data.bdy;
308
- let msgList = new Map();
309
-
310
- for (let o in data) {
311
- if (data[o].profile) {
312
- try {
313
- data[o].profile = await JSON.parse(data[o].profile);
314
- }
315
-
316
- catch (error) {
317
- return;
318
- }
319
- }
320
- else {
321
- data[o].profile = {
322
- nickname: null,
323
- profileImageUrl: null,
324
- userRoleCode: null
325
- };
326
- }
327
-
328
- try {
329
- let modVal = false;
330
- if (data[o].profile.userRoleCode === "streamer" || data[o].profile.userRoleCode === "streaming_channel_manager" || data[o].profile.userRoleCode === "streaming_chat_manager") modVal = true;
331
-
332
- msgList[Object.keys(msgList).length] = {
333
- author: {
334
- id: data[o].uid,
335
- name: data[o].profile.nickname,
336
- imageURL: data[o].profile.profileImageUrl,
337
- hasMod: modVal
338
- },
339
- message: data[o].msg,
340
- time: data[o].msgTime
341
- };
342
- }
343
-
344
- catch(error) {
345
- return;
346
- }
347
- }
348
-
349
- callback(msgList);
350
-
351
- }
352
- });
230
+ this.#callbacks.message = callback;
353
231
  }
354
232
 
233
+ /**
234
+ * @param {function(chzzkDonation)} callback
235
+ */
355
236
  onDonation(callback) {
356
- this.#callbacks[Object.keys(this.#callbacks).length] = {
357
- type: "donation",
358
- callback: callback
359
- };
360
- this.#onDonationHandler(callback);
361
- }
362
-
363
- #onDonationHandler (callback) {
364
- if (!this.#status.ws) return callback(null);
365
-
366
- this.#ws.on("message", async (data) => {
367
- try {
368
- data = await JSON.parse(data);
369
- }
370
-
371
- catch (error) {
372
- return;
373
- }
374
-
375
- if (data.cmd == 93102) {
376
-
377
- data = data.bdy;
378
- let msgList = new Map();
379
-
380
- for (let o in data) {
381
- if (data[o].profile) {
382
- try {
383
- data[o].profile = await JSON.parse(data[o].profile);
384
- data[o].extras = await JSON.parse(data[o].extras);
385
- }
386
-
387
- catch (error) {
388
- return;
389
- }
390
- }
391
- else {
392
- data[o].profile = {
393
- nickname: null,
394
- profileImageUrl: null,
395
- userRoleCode: null
396
- };
397
- }
398
-
399
- try {
400
- let modVal = false;
401
- if (data[o].profile.userRoleCode === "streamer" || data[o].profile.userRoleCode === "streaming_channel_manager" || data[o].profile.userRoleCode === "streaming_chat_manager") modVal = true;
402
-
403
- msgList[Object.keys(msgList).length] = {
404
- amount: data[o].extras.payAmount,
405
- author: {
406
- id: data[o].uid,
407
- name: data[o].profile.nickname,
408
- imageURL: data[o].profile.profileImageUrl,
409
- hasMod: modVal
410
- },
411
- message: data[o].msg,
412
- time: data[o].msgTime
413
- };
414
- }
415
-
416
- catch(error) {
417
- return;
418
- }
419
- }
420
-
421
- callback(msgList);
422
-
423
- }
424
- });
237
+ this.#callbacks.donation = callback;
425
238
  }
426
239
 
427
240
  /**
428
241
  * @param {function} callback
429
242
  */
430
243
  onDisconnect(callback) {
431
- if (!this.#status.ws) return callback(null);
432
-
433
- this.#ws.on("close", () => {
434
- callback();
435
- });
436
- }
437
-
438
- /**
439
- * @param {?number} size
440
- * @return {Promise<chzzkMessages>}
441
- */
442
- getRecentChat(size) {
443
- return new Promise(async (resolve, reject) => {
444
- if (!this.#status.ws) return resolve(null);
445
-
446
- let reqOpt = {
447
- "ver": "3",
448
- "cmd": 5101,
449
- "svcid": this.#svcid,
450
- "cid": this.#chatID,
451
- "sid": this.#sid,
452
- "bdy": {
453
- "recentMessageCount": size || 50
454
- },
455
- "tid": 2
456
- };
457
-
458
- this.#ws.send(JSON.stringify(reqOpt));
459
-
460
- this.#ws.on("message", async (data) => {
461
- try {
462
- data = await JSON.parse(data);
463
- }
464
-
465
- catch (error) {
466
- return;
467
- }
468
-
469
- if (data.cmd == 15101) {
470
- data = data.bdy.messageList;
471
- let msgList = new Map();
472
-
473
- for (let o in data) {
474
- try {
475
- data[o].profile = await JSON.parse(data[o].profile);
476
- }
477
-
478
- catch (error) {
479
- return;
480
- }
481
-
482
- if (!data[o].profile) {
483
- data[o].profile = {
484
- nickname: null,
485
- profileImageUrl: null,
486
- userRoleCode: null
487
- }
488
- }
489
-
490
- let modVal = false;
491
- if (data[o].profile.userRoleCode === "streamer" || data[o].profile.userRoleCode === "streaming_channel_manager" || data[o].profile.userRoleCode === "streaming_chat_manager") modVal = true;
492
-
493
- msgList[Object.keys(msgList).length] = {
494
- author: {
495
- id: data[o].userId,
496
- name: data[o].profile.nickname,
497
- imageURL: data[o].profile.profileImageUrl,
498
- hasMod: modVal
499
- },
500
- message: data[o].content,
501
- time: data[o].messageTime
502
- };
503
- }
504
-
505
- return resolve(msgList);
506
- }
507
-
508
- });
509
-
510
- });
244
+ this.#callbacks.disconnect = callback;
511
245
  }
512
246
 
513
247
  /**
@@ -526,8 +260,11 @@ class chzzkChat {
526
260
  */
527
261
  async getUserInfo (authorID) {
528
262
  return new Promise(async (resolve, reject) => {
529
- let profileCard = await reqGame("nng_main/v1/chats/" + this.#chatID + "/users/" + authorID + "/profile-card?chatType=STREAMING");
530
- if (profileCard.code != 200) return resolve(null);
263
+ let status = await getStatus(this.#channelID);
264
+ if (!status) return resolve(null);
265
+
266
+ let profileCard = await reqGame("nng_main/v1/chats/" + status.chatID + "/users/" + authorID + "/profile-card?chatType=STREAMING");
267
+ if (!profileCard || profileCard.code != 200) return resolve(null);
531
268
  profileCard = profileCard.content;
532
269
 
533
270
  let userInfo = {
@@ -551,60 +288,15 @@ class chzzkChat {
551
288
  return new Promise(async (resolve, reject) => {
552
289
  if (!this.#status.ws) return resolve(null);
553
290
 
291
+ await this.#ws.off();
554
292
  await this.#ws.close();
555
- this.#ws = null;
293
+
556
294
  this.#status.ws = false;
557
- this.#status.wsID++;
295
+ this.#status.established = false;
296
+ console.log("[WS] Disconnected! (Force)");
558
297
  return resolve(true);
559
298
  });
560
299
  }
561
-
562
- async #polling () {
563
- if (!this.#status.polling) return;
564
-
565
- //Get ChatID
566
- let cidRes = await getStatus(this.channelID);
567
- if (cidRes && cidRes.chatID && cidRes.chatID != this.#chatID) {
568
- this.#chatID = cidRes.chatID;
569
-
570
- //Reconnect
571
- this.#status.reconnect = true;
572
- await this.disconnect();
573
- await this.connect();
574
- //Reconnect
575
- }
576
- //Get ChatID
577
-
578
- let interval = Math.floor(Math.random() * 20000) + 30000;
579
-
580
- setTimeout(() => {
581
- return this.#polling();
582
- }, interval);
583
- }
584
-
585
- async #ping (pingID) {
586
- if (!this.#status.ws) return this.#status.ping = false;
587
- if (!this.#status.ping || pingID != this.#status.pingID) return;
588
-
589
- let interval = Math.floor(Math.random() * 20000) + 30000;
590
-
591
- let pingOpt = {
592
- "ver": "3",
593
- "cmd": 0
594
- };
595
- try {
596
- await this.#ws.send(JSON.stringify(pingOpt));
597
- }
598
- catch (error) {
599
- return setTimeout(async () => {
600
- return this.#ping(pingID);
601
- }, Math.floor(Math.random() * 20000) + 30000);
602
- }
603
-
604
- setTimeout(() => {
605
- return this.#ping(pingID);
606
- }, interval);
607
- }
608
300
  }
609
301
 
610
302
  module.exports = {
package/lib/live.js CHANGED
@@ -1,4 +1,4 @@
1
- const { reqChzzk, reqGame } = require("./tool.js");
1
+ const { reqChzzk } = require("./tool.js");
2
2
 
3
3
  /**
4
4
  * @typedef {Object} chzzkLiveDetail
@@ -38,6 +38,7 @@ async function getDetail (channelID) {
38
38
  return new Promise(async (resolve, reject) => {
39
39
 
40
40
  let res = await reqChzzk("service/v3/channels/" + channelID + "/live-detail");
41
+ console.log(res);
41
42
  if (res.code != 200 || !res.content) return resolve(null);
42
43
  res = res.content;
43
44
 
@@ -112,16 +113,7 @@ async function getStatus (channelID) {
112
113
  });
113
114
  }
114
115
 
115
- async function getAccess (chatID) {
116
- return new Promise(async (resolve, reject) => {
117
- let accRes = await reqGame("nng_main/v1/chats/access-token?channelId=" + chatID + "&chatType=STREAMING");
118
- if (accRes.code != 200) return resolve(null);
119
- return resolve(accRes.content.accessToken);
120
- });
121
- }
122
-
123
116
  module.exports = {
124
117
  getDetail: getDetail,
125
- getStatus: getStatus,
126
- getAccess: getAccess
118
+ getStatus: getStatus
127
119
  }
package/lib/tool.js CHANGED
@@ -123,7 +123,6 @@ function reqChzzk (path) {
123
123
  fetch(chzzkBaseURL + path, {
124
124
  method: "GET",
125
125
  headers: {
126
- "Cookie": "NID_AUT=" + NID.AUT + ";NID_SES=" + NID.SES,
127
126
  "User-Agent": "BuzzkLib/" + vm.getVersion() + " (Node)"
128
127
  }
129
128
  })
@@ -175,7 +174,7 @@ function reqGame (path) {
175
174
  fetch(gameBaseURL + path, {
176
175
  method: "GET",
177
176
  headers: {
178
- "Cookie": "NID_AUT=" + NID.AUT + ";NID_SES=" + NID.SES
177
+ "User-Agent": "BuzzkLib/" + vm.getVersion() + " (Node)"
179
178
  }
180
179
  })
181
180
 
@@ -200,7 +199,7 @@ function reqNaver (path) {
200
199
  fetch(naverBaseURL + path, {
201
200
  method: "GET",
202
201
  headers: {
203
- "Cookie": "NID_AUT=" + NID.AUT + ";NID_SES=" + NID.SES
202
+ "User-Agent": "BuzzkLib/" + vm.getVersion() + " (Node)"
204
203
  }
205
204
  })
206
205
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "buzzk",
3
3
  "displayName": "BUZZK",
4
- "version": "2.0.1",
4
+ "version": "2.1.1",
5
5
  "description": "뿌지직 (BUZZK) - 치지직(CHZZK) 챗봇을 더욱 쉽게 개발할 수 있도록 돕는 비공식 라이브러리.",
6
6
  "main": "lib/index.js",
7
7
  "type": "commonjs",
@@ -31,7 +31,6 @@
31
31
  "뿌지직"
32
32
  ],
33
33
  "dependencies": {
34
- "socket.io-client": "^4.8.1",
35
- "ws": "^8.18.0"
34
+ "socket.io-client": "^2.0.3"
36
35
  }
37
36
  }