buzzk 2.0.1 → 2.1.0

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,59 @@
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
+ | <img src="https://github.com/Emin-G/Img/blob/main/tags/tag_delete-min.png?raw=true" alt="BUZZK" width="70"> | 삭제 |
53
+ |--|--|
54
+ | | (callback).author.imageURL |
55
+
56
+ ---
57
+
58
+ buzzkChat.onDonation
59
+
60
+ | <img src="https://github.com/Emin-G/Img/blob/main/tags/tag_delete-min.png?raw=true" alt="BUZZK" width="70"> | 삭제 |
61
+ |--|--|
62
+ | | (callback).author.imageURL |
63
+
64
+ ---
65
+
66
+ | <img src="https://github.com/Emin-G/Img/blob/main/tags/tag_delete-min.png?raw=true" alt="BUZZK" width="70"> | 삭제 |
67
+ |--|--|
68
+ | | (callback).time |
69
+
70
+ ---
71
+
72
+ buzzk.live
73
+
74
+ | <img src="https://github.com/Emin-G/Img/blob/main/tags/tag_delete-min.png?raw=true" alt="BUZZK" width="70"> | 삭제 |
75
+ |--|--|
76
+ | | buzzk.live.getAccess() |
77
+
78
+ </details>
79
+
80
+ ---
81
+
32
82
  ## ✒️ 마이그레이션 가이드 (v.1.x -> v.2.0.0)
33
83
 
34
84
  <details>
@@ -83,37 +133,37 @@
83
133
 
84
134
  const buzzk = require("buzzk");
85
135
  buzzk.auth("ClientID 값", "ClientSecret 값");
86
- buzzk.login("NID_AUT 쿠키 값", "NID_SES 쿠키 값");
136
+
137
+ //buzzk.login("NID_AUT 쿠키 값", "NID_SES 쿠키 값");
138
+ //buzzk.channel.follow, unfollow 시에만 사용.
87
139
 
88
140
  const buzzkChat = buzzk.chat;
89
141
 
90
142
  async function test () {
91
143
 
92
- let chSearch = await buzzk.channel.search("녹두로로"); //채널 검색
93
-
94
- let channel = chSearch[0]; //검색 결과 첫번째 채널
95
-
96
- const lvDetail = await buzzk.live.getDetail(channel.channelID); //현재 방송 정보
144
+ let oauth = buzzk.oauth.get("code 값");
97
145
 
98
- let chat = new buzzkChat(channel.channelID);
146
+ let chat = new buzzkChat(oauth.accessToken);
99
147
  await chat.connect(); //채팅창 연결
100
148
 
101
- let recentChat = await chat.getRecentChat(); //최근 채팅 가져오기 (기본값 50개)
102
- console.log(recentChat);
103
-
104
149
  chat.onMessage(async (data) => { //채팅이 왔을 때
105
- for (let o in data) {
106
- console.log(data[o].message);
150
+ console.log(data.message);
151
+
152
+ if (data.message == "!ping") await chat.send("pong!");
153
+ //채팅 보내기
107
154
 
108
- if (data[o].message === "!ping") await chat.send("pong!");
109
- //채팅 보내기 (login 후에만 가능)
155
+ if (data.message == "!공지") await chat.setNotice("테스트!");
156
+ //채팅 상단 고정 (공지)
110
157
 
111
- let userInfo = await chat.getUserInfo(data[o].author.id);
112
- console.log(userInfo);
113
- //채팅 보낸 유저의 정보
114
- }
158
+ let userInfo = await chat.getUserInfo(data.author.id);
159
+ console.log(userInfo);
160
+ //채팅 보낸 유저의 정보
115
161
  });
116
162
 
163
+ chat.onDonation(async (data) => { //후원이 왔을 때
164
+ //TODO
165
+ });
166
+
117
167
  chat.onDisconnect(async () => { //채팅창 연결이 끊겼을 때
118
168
  //TODO
119
169
  });
@@ -144,6 +194,7 @@ dotenv와 함께 사용하는 것을 매우 권장합니다.
144
194
 
145
195
  buzzk.login("NID_AUT 쿠키 값", "NID_SES 쿠키 값");
146
196
 
197
+ * 해당 함수는 이제 buzzk.channel.follow, unfollow 시에만 사용됩니다.
147
198
  dotenv와 함께 사용하는 것을 매우 권장합니다.
148
199
 
149
200
  buzzk.login(process.env.NID_AUT, process.env.NID_SES);
@@ -298,95 +349,69 @@ dotenv와 함께 사용하는 것을 매우 권장합니다.
298
349
 
299
350
  ### chat
300
351
 
352
+ ✅ Official API
353
+
301
354
  const buzzkChat = buzzk.chat;
302
- let chat = new buzzkChat("channelID 값");
355
+ let chat = new buzzkChat("accessToken 값");
303
356
  await chat.connect(); //채팅창 연결
304
357
 
305
358
  >
306
359
 
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>
360
+ Official API
328
361
 
329
362
  chat.onMessage((data) => { //채팅이 왔을 때
330
363
  console.log(data);
331
-
332
- for (let o in data) {
333
- console.log(data[o].message); //메세지만 전부 꺼내기
334
- }
335
364
  });
336
365
 
337
366
  <details>
338
367
  <summary>callback</summary>
339
368
 
340
369
  - Return
341
- - 0
342
370
  - author
343
371
  - id
344
372
  - name
345
- - imageURL
346
373
  - hasMod //관리 권한을 가졌는지 (false / true)
347
374
  - message
375
+ - emoji (Object - Key: 이모지 이름, Value: 이미지 URL)
348
376
  - time
349
- - 1
350
- - 2
351
- - 3
352
- - ...
353
377
 
354
378
  </details>
355
379
 
380
+ >
381
+
382
+ ✅ Official API
383
+
356
384
  chat.onDonation((data) => { //도네이션이 왔을 때
357
385
  console.log(data);
358
-
359
- for (let o in data) {
360
- console.log(data[o].amount); //후원 금액만 전부 꺼내기
361
- }
362
386
  });
363
387
 
364
388
  <details>
365
389
  <summary>callback</summary>
366
390
 
367
391
  - 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
- - ...
392
+ - type //CHAT or VIDEO
393
+ - amount //후원 금액
394
+ - author
395
+ - id
396
+ - name
397
+ - hasMod //관리 권한을 가졌는지 (false / true)
398
+ - message
399
+ - emoji (Object - Key: 이모지 이름, Value: 이미지 URL)
381
400
 
382
401
  </details>
383
402
 
403
+ >
404
+
405
+ ✅ Official API
406
+
384
407
  chat.onDisconnect(() => { //채팅창 연결이 끊겼을 때
385
408
  //TODO
386
409
  });
387
410
 
388
411
  >
389
412
 
413
+ ✅ Official API
414
+
390
415
  await chat.send("ㅋㅋㅋㅋㅋㅋ"); //채팅 보내기 (login 후에만 가능)
391
416
 
392
417
  >
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,104 @@ 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
+ return this.#callbacks.message(chatData);
120
+ });
102
121
 
103
- getwsStatus() {
104
- return this.#status.ws;
105
- }
122
+ this.#ws.on("DONATION", (data) => {
123
+ data = JSON.parse(data);
124
+ console.log(data);
106
125
 
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!");
126
+ let hasMod = false;
127
+ for (let o in data.profile.badges) {
128
+ if (data.profile.badges[o].imageUrl == "https://ssl.pstatic.net/static/nng/glive/icon/streamer.png") hasMod = true;
129
+ if (data.profile.badges[o].imageUrl == "https://ssl.pstatic.net/static/nng/glive/icon/manager.png") hasMod = true;
130
+ }
112
131
 
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
132
+ let donationData = new chzzkDonationData(data.donationType, data.payAmount, data.donatorChannelId, data.donatorNickname, hasMod, data.donationText, data.emojis);
133
+ return this.#callbacks.donation(donationData);
136
134
  });
137
- });
138
- }
139
135
 
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);
136
+ this.#ws.on("error", (error) => {
137
+ console.log(error);
138
+
139
+ if (!this.#status.established) return resolve(null);
155
140
  });
156
- });
157
- }
158
141
 
159
- #connectHandler(wsID) {
160
- return new Promise(async (resolve, reject) => {
161
- this.#ws.on("message", async (data) => {
162
- if (wsID != this.#status.wsID) return;
142
+ this.#ws.on("disconnect", (data) => {
143
+ console.log("[WS] Disconnected! (" + data + ")");
163
144
 
164
- try {
165
- data = await JSON.parse(data);
166
- }
145
+ this.#status.ws = false;
146
+ this.#status.established = false;
147
+ this.#callbacks.disconnect(data);
167
148
 
168
- catch (error) {
169
- return;
170
- }
149
+ if (this.#ws) this.#ws.off();
150
+ if (!this.#status.established) resolve(null);
171
151
 
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
152
+ this.#ws = null;
153
+ this.#channelID = null;
154
+ this.#session = null;
155
+ return this.connect();
208
156
  });
157
+
209
158
  });
210
159
  }
211
160
 
@@ -215,299 +164,83 @@ class chzzkChat {
215
164
  */
216
165
  send(message) {
217
166
  return new Promise(async (resolve, reject) => {
218
- if (!this.#status.ws) return resolve(null);
167
+ if (!this.#ws || !this.#status.ws || !this.#status.established) return resolve(false);
168
+ console.log(message);
219
169
 
220
- let extras = {
221
- "chatType":"STREAMING",
222
- "osType":"PC",
223
- "streamingChannelId": this.channelID,
224
- "emojis":"",
225
- "extraToken": this.#extTkn
170
+ let reqMessage = {
171
+ message: message
226
172
  }
227
173
 
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
- };
174
+ let chatRes = await USER.post(this.accessToken, "open/v1/chats/send", JSON.stringify(reqMessage));
175
+ if (!chatRes || chatRes.code != 200) return resolve(false);
246
176
 
247
- try {
248
- this.#ws.send(JSON.stringify(sendOpt));
249
- }
177
+ return resolve(true);
178
+ });
179
+ }
250
180
 
251
- catch (error) {
252
- return resolve(false);
181
+ /**
182
+ * @param {string} message
183
+ * @returns {Promise<boolean>}
184
+ */
185
+ setNotice(message) {
186
+ return new Promise(async (resolve, reject) => {
187
+ if (!this.#ws || !this.#status.ws || !this.#status.established) return resolve(false);
188
+
189
+ let reqMessage = {
190
+ message: message
253
191
  }
254
192
 
255
- return resolve(true);
256
- //WS Send
193
+ let noticeRes = await USER.post(this.accessToken, "open/v1/chats/notice", JSON.stringify(reqMessage));
194
+ if (!noticeRes || noticeRes.code != 200) return resolve(false);
257
195
 
196
+ return resolve(true);
258
197
  });
259
198
  }
260
199
 
261
200
  /**
262
- * @typedef {Object.<number, chzzkMessage>} chzzkMessages
201
+ * @typedef {Object} chzzkMessage
202
+ * @property {chzzkAuthor} author
203
+ * @property {string} message
204
+ * @property {Object} emoji
205
+ * @property {number} time
263
206
  */
264
207
 
265
208
  /**
266
- * @typedef {Object} chzzkMessage
267
- * @property {chzzkMessageAuthor} author
209
+ * @typedef {Object} chzzkDonation
210
+ * @property {string} type
211
+ * @property {string} amount
212
+ * @property {chzzkAuthor} author
268
213
  * @property {string} message
214
+ * @property {Object} emoji
269
215
  * @property {number} time
270
216
  */
271
217
 
272
218
  /**
273
- * @typedef {Object} chzzkMessageAuthor
219
+ * @typedef {Object} chzzkAuthor
274
220
  * @property {string} id
275
221
  * @property {string} name
276
- * @property {string} imageURL
277
222
  * @property {boolean} hasMod
278
223
  */
279
224
 
280
225
  /**
281
- * @param {function(chzzkMessages)} callback
226
+ * @param {function(chzzkMessage)} callback
282
227
  */
283
228
  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
- });
229
+ this.#callbacks.message = callback;
353
230
  }
354
231
 
232
+ /**
233
+ * @param {function(chzzkDonation)} callback
234
+ */
355
235
  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
- });
236
+ this.#callbacks.donation = callback;
425
237
  }
426
238
 
427
239
  /**
428
240
  * @param {function} callback
429
241
  */
430
242
  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
- });
243
+ this.#callbacks.disconnect = callback;
511
244
  }
512
245
 
513
246
  /**
@@ -526,8 +259,11 @@ class chzzkChat {
526
259
  */
527
260
  async getUserInfo (authorID) {
528
261
  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);
262
+ let status = await getStatus(this.#channelID);
263
+ if (!status) return resolve(null);
264
+
265
+ let profileCard = await reqGame("nng_main/v1/chats/" + status.chatID + "/users/" + authorID + "/profile-card?chatType=STREAMING");
266
+ if (!profileCard || profileCard.code != 200) return resolve(null);
531
267
  profileCard = profileCard.content;
532
268
 
533
269
  let userInfo = {
@@ -551,60 +287,15 @@ class chzzkChat {
551
287
  return new Promise(async (resolve, reject) => {
552
288
  if (!this.#status.ws) return resolve(null);
553
289
 
290
+ await this.#ws.off();
554
291
  await this.#ws.close();
555
- this.#ws = null;
292
+
556
293
  this.#status.ws = false;
557
- this.#status.wsID++;
294
+ this.#status.established = false;
295
+ console.log("[WS] Disconnected! (Force)");
558
296
  return resolve(true);
559
297
  });
560
298
  }
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
299
  }
609
300
 
610
301
  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.0",
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
  }