buzzk 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Emin-G
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,192 @@
1
+ <img src="https://github.com/Emin-G/Img/blob/main/buzzk/buzzk_pamplet.gif?raw=true" alt="BuzzkThumb" width="100%">
2
+
3
+ # 뿌지직
4
+
5
+ <p align="center">
6
+ <img src="https://github.com/Emin-G/Img/blob/main/buzzk/buzzk_favi-min.png?raw=true" alt="BUZZK" width="30%">
7
+ </p>
8
+
9
+ <p align="center">
10
+ 뿌지직은 치지직 챗봇을 더욱 쉽게 개발할 수 있도록 돕는 비공식 라이브러리 입니다.
11
+ </p>
12
+ <p align="center">
13
+ 챗봇 개발에 초점이 맞춰저 있어, 여러 계정에 동시 로그인할 수 없습니다.
14
+ </p>
15
+
16
+ ---
17
+
18
+ ## 설치
19
+
20
+ 1. 이 레포지토리를 다운받아 프로젝트 폴더 안에 넣어주세요.
21
+ 2. 폴더 이름을 `buzzk-master` 에서 `buzzk` 으로 변경해주세요.
22
+ 3. `const buzzk = require("./buzzk");`
23
+
24
+ ## 빠른. 시작.
25
+
26
+ const buzzk = require("./buzzk");
27
+ buzzk.login("NID_AUT 쿠키 값", "NID_SES 쿠키 값");
28
+
29
+ const buzzkChat = buzzk.chat;
30
+
31
+ async function test () {
32
+
33
+ let chSearch = await buzzk.channel.getChannel("녹두로로"); //채널 검색
34
+
35
+ let channel = chSearch[0]; //검색 결과 첫번째 채널
36
+
37
+ const lvDetail = await buzzk.live.getLiveDetail(channel.channelID); //현재 방송 정보
38
+
39
+ let chat = new buzzkChat(channel.channelID);
40
+ await chat.connect(); //채팅창 연결
41
+
42
+ let recentChat = await chat.getRecentChat(); //최근 채팅 가져오기 (기본값 50개)
43
+ console.log(recentChat);
44
+
45
+ chat.onMessage((data) => { //채팅이 왔을 때
46
+ for (let o in data) {
47
+ console.log(data[o].message);
48
+
49
+ if (data[o].message === "!ping") await chat.send("pong!");
50
+ //채팅 보내기 (login 후에만 가능)
51
+ }
52
+ });
53
+
54
+ }
55
+
56
+ test();
57
+
58
+ ## 사용법
59
+
60
+ > login
61
+
62
+ buzzk.login("NID_AUT 쿠키 값", "NID_SES 쿠키 값");
63
+
64
+ dotenv와 함께 사용하는 것을 매우 권장합니다.
65
+
66
+ buzzk.login(process.env.NID_AUT, process.env.NID_SES);
67
+
68
+ ---
69
+
70
+ > channel
71
+
72
+ let chSearch = await buzzk.channel.getChannel("녹두로로");
73
+ console.log(chSearch);
74
+
75
+ <details>
76
+ <summary>Return 값 보기</summary>
77
+
78
+ - Return
79
+ - channelID
80
+ - name
81
+ - description
82
+ - follower
83
+ - imageURL
84
+ - isLive
85
+
86
+ </details>
87
+
88
+ ---
89
+
90
+ > live
91
+
92
+ const lvDetail = await buzzk.live.getLiveDetail("channelID 값");
93
+ console.log(lvDetail);
94
+
95
+ <details>
96
+ <summary>Return 값 보기</summary>
97
+
98
+ - Return
99
+ - channelID
100
+ - channel
101
+ - chatID
102
+ - userCount
103
+ - now
104
+ - total
105
+ - title
106
+ - startOn
107
+ - closeOn
108
+ - status
109
+ - polling
110
+
111
+ </details>
112
+
113
+ const lvStatus = await buzzk.live.getLiveStatus("channelID 값");
114
+ console.log(lvStatus);
115
+
116
+ <details>
117
+ <summary>Return 값 보기</summary>
118
+
119
+ - Return
120
+ - channelID
121
+ - chatID
122
+ - userCount
123
+ - now
124
+ - total
125
+ - title
126
+ - status
127
+ - polling
128
+
129
+ </details>
130
+
131
+ ---
132
+
133
+ > chat
134
+
135
+ const buzzkChat = buzzk.chat;
136
+ let chat = new buzzkChat("channelID 값");
137
+ await chat.connect(); //채팅창 연결
138
+
139
+ >
140
+
141
+ let recentChat = await chat.getRecentChat(갯수); //최근 채팅 가져오기 (기본값 50개)
142
+ console.log(recentChat);
143
+
144
+ <details>
145
+ <summary>Return 값 보기</summary>
146
+
147
+ - Return
148
+ - 0
149
+ - author
150
+ - id
151
+ - name
152
+ - imageURL
153
+ - message
154
+ - time
155
+ - 1
156
+ - 2
157
+ - 3
158
+ - ...
159
+
160
+ </details>
161
+
162
+ chat.onMessage((data) => { //채팅이 왔을 때
163
+ console.log(data);
164
+
165
+ for (let o in data) {
166
+ console.log(data[o].message); //메세지만 전부 꺼내기
167
+ }
168
+ });
169
+
170
+ <details>
171
+ <summary>Return 값 보기</summary>
172
+
173
+ - Return
174
+ - 0
175
+ - author
176
+ - id
177
+ - name
178
+ - imageURL
179
+ - message
180
+ - time
181
+ - 1
182
+ - 2
183
+ - 3
184
+ - ...
185
+
186
+ </details>
187
+
188
+ await chat.send("ㅋㅋㅋㅋㅋㅋ"); //채팅 보내기 (login 후에만 가능)
189
+
190
+ >
191
+
192
+ chat.disconnect(); //채팅창 연결 끊기
package/lib/channel.js ADDED
@@ -0,0 +1,36 @@
1
+ const { reqChzzk } = require("./tool.js");
2
+
3
+ class chzzkChannel {
4
+ constructor(channelID, name, description, follower, imageURL, isLive) {
5
+ this.channelID = channelID;
6
+ this.name = name;
7
+ this.description = description;
8
+ this.follower = follower;
9
+ this.imageURL = imageURL;
10
+ this.isLive = isLive;
11
+ }
12
+ }
13
+
14
+ async function getChannel (keyword) {
15
+ return new Promise(async (resolve, reject) => {
16
+
17
+ let chSearch = await reqChzzk("service/v1/search/channels?keyword=" + keyword + "&offset=0&size=13&withFirstChannelContent=false");
18
+ chSearch = chSearch.content.data;
19
+
20
+ if (!chSearch) return resolve();
21
+
22
+ let chRes = new Map();
23
+
24
+ for (let o in chSearch) {
25
+ let tempChannel = chSearch[o].channel;
26
+ chRes[Object.keys(chRes).length] = new chzzkChannel(tempChannel.channelId, tempChannel.channelName, tempChannel.channelDescription, tempChannel.followerCount, tempChannel.channelImageUrl, tempChannel.openLive);
27
+ }
28
+
29
+ return resolve(chRes);
30
+
31
+ });
32
+ }
33
+
34
+ module.exports = {
35
+ getChannel: getChannel
36
+ }
package/lib/chat.js ADDED
@@ -0,0 +1,223 @@
1
+ const { getLiveStatus } = require("./live.js");
2
+ const { reqGame } = require("./tool.js");
3
+
4
+ const { WebSocket } = require("ws");
5
+
6
+ class chzzkChat {
7
+ constructor(channelID) {
8
+ this.channelID = channelID;
9
+ }
10
+
11
+ //Private
12
+ #ws; //Chat Web Socket
13
+ #ssID; //kr-ss?.chat.naver.com/chat
14
+ #accTkn; //Account Access Token
15
+ #svcid; //game
16
+ #sid;
17
+ #chatID;
18
+ //Private
19
+
20
+ connect() {
21
+ return new Promise(async (resolve, reject) => {
22
+ //Get ChatID
23
+ let cidRes = await getLiveStatus(this.channelID);
24
+
25
+ this.#chatID = cidRes.chatID;
26
+ //Get ChatID
27
+
28
+ //Get accTkn
29
+ let accRes = await reqGame("nng_main/v1/chats/access-token?channelId=" + this.#chatID + "&chatType=STREAMING");
30
+
31
+ this.#accTkn = accRes.content.accessToken;
32
+ //Get accTkn
33
+
34
+ //Load Balancing
35
+ this.#ssID = Math.floor(Math.random() * 10) + 1;
36
+ //Load Balancing
37
+
38
+ //Connect Web Socket
39
+ this.#ws = new WebSocket("wss://kr-ss" + this.#ssID + ".chat.naver.com/chat");
40
+ //Connect Web Socket
41
+
42
+ //WS Open
43
+ this.#ws.on("open", () => {
44
+ console.log("[WS] Connected!");
45
+
46
+ //WS First Connect
47
+ let connectOpt = {
48
+ "ver": "2",
49
+ "cmd": 100,
50
+ "svcid": "game",
51
+ "cid": this.#chatID,
52
+ "bdy": {
53
+ "uid":"7d98a20c25251e404ca61d0981bb1ab9",
54
+ "devType":2001,
55
+ "accTkn": this.#accTkn,
56
+ "auth":"SEND"
57
+ },
58
+ "tid": 1
59
+ };
60
+
61
+ this.#ws.send(JSON.stringify(connectOpt));
62
+ //WS First Connect
63
+ });
64
+ //WS Open
65
+
66
+ this.#ws.on("error", console.error);
67
+
68
+ this.#ws.on("close", () => {
69
+ console.log("[WS] Disconnect!");
70
+ });
71
+
72
+ //WS Message
73
+ this.#ws.on("message", (data) => {
74
+ data = JSON.parse(data);
75
+
76
+ //Update Var
77
+ if (data.cid) this.#chatID = data.cid;
78
+ if (data.svcid) this.#svcid = data.svcid;
79
+ if (data.bdy && data.bdy.sid) this.#sid = data.bdy.sid;
80
+ //Update Var
81
+
82
+ //Ping Pong
83
+ if (data.cmd == 0) {
84
+ let pongOpt = {
85
+ "ver": "2",
86
+ "cmd": 10000
87
+ };
88
+ this.#ws.send(JSON.stringify(pongOpt));
89
+ }
90
+ //Ping Pong
91
+
92
+ //Connected
93
+ else if (data.cmd == 10100) resolve();
94
+ //Connected
95
+ });
96
+ //WS Message
97
+
98
+ });
99
+ }
100
+
101
+ send(message) {
102
+ return new Promise(async (resolve, reject) => {
103
+
104
+ let extras = {
105
+ "chatType":"STREAMING",
106
+ "osType":"PC",
107
+ "streamingChannelId": this.channelID,
108
+ "emojis":""
109
+ }
110
+
111
+ //WS Send
112
+ let date = new Date();
113
+ let sendOpt = {
114
+ "ver": "2",
115
+ "cmd": 3101,
116
+ "svcid": this.#svcid,
117
+ "cid": this.#chatID,
118
+ "sid": this.#sid,
119
+ "retry": false,
120
+ "bdy": {
121
+ "msg": message,
122
+ "msgTypeCode": 1,
123
+ "extras": JSON.stringify(extras),
124
+
125
+ "msgTime": date.getTime()
126
+ },
127
+ "tid": 3
128
+ };
129
+
130
+ this.#ws.send(JSON.stringify(sendOpt));
131
+
132
+ return resolve();
133
+ //WS Send
134
+
135
+ });
136
+ }
137
+
138
+ onMessage(callback) {
139
+ if (!this.#ws) return callback(null);
140
+
141
+ this.#ws.on("message", (data) => {
142
+ data = JSON.parse(data);
143
+
144
+ if (data.cmd == 93101) {
145
+
146
+ data = data.bdy;
147
+ let msgList = new Map();
148
+
149
+ for (let o in data) {
150
+ data[o].profile = JSON.parse(data[o].profile);
151
+
152
+ msgList[Object.keys(msgList).length] = {
153
+ author: {
154
+ id: data[o].uid,
155
+ name: data[o].profile.nickname,
156
+ imageURL: data[o].profile.profileImageUrl
157
+ },
158
+ message: data[o].msg,
159
+ time: data[o].msgTime
160
+ };
161
+ }
162
+
163
+ callback(msgList);
164
+
165
+ }
166
+ });
167
+ }
168
+
169
+ getRecentChat(size) {
170
+ return new Promise(async (resolve, reject) => {
171
+ if (!this.#ws) return resolve(null);
172
+
173
+ let reqOpt = {
174
+ "ver": "2",
175
+ "cmd": 5101,
176
+ "svcid": this.#svcid,
177
+ "cid": this.#chatID,
178
+ "sid": this.#sid,
179
+ "bdy": {
180
+ "recentMessageCount": size || 50
181
+ },
182
+ "tid": 2
183
+ };
184
+
185
+ this.#ws.send(JSON.stringify(reqOpt));
186
+
187
+ this.#ws.on("message", (data) => {
188
+ data = JSON.parse(data);
189
+
190
+ if (data.cmd == 15101) {
191
+ data = data.bdy.messageList;
192
+ let msgList = new Map();
193
+
194
+ for (let o in data) {
195
+ data[o].profile = JSON.parse(data[o].profile);
196
+
197
+ msgList[Object.keys(msgList).length] = {
198
+ author: {
199
+ id: data[o].userId,
200
+ name: data[o].profile.nickname,
201
+ imageURL: data[o].profile.profileImageUrl
202
+ },
203
+ message: data[o].content,
204
+ time: data[o].messageTime
205
+ };
206
+ }
207
+
208
+ return resolve(msgList);
209
+ }
210
+
211
+ });
212
+
213
+ });
214
+ }
215
+
216
+ disconnect() {
217
+ this.#ws.close();
218
+ }
219
+ }
220
+
221
+ module.exports = {
222
+ chzzkChat: chzzkChat
223
+ }
package/lib/index.js ADDED
@@ -0,0 +1,7 @@
1
+ module.exports = {
2
+ channel: require("./channel.js"),
3
+ chat: require("./chat.js").chzzkChat,
4
+ live: require("./live.js"),
5
+
6
+ login: require("./val.js").login
7
+ }
package/lib/live.js ADDED
@@ -0,0 +1,49 @@
1
+ const { reqChzzk } = require("./tool.js");
2
+
3
+ async function getLiveDetail (channelID) {
4
+ return new Promise(async (resolve, reject) => {
5
+
6
+ let res = await reqChzzk("service/v2/channels/" + channelID + "/live-detail");
7
+ res = res.content;
8
+
9
+ let lvDetail = {
10
+ channelID: res.channel.channelId,
11
+ channel: res.channel,
12
+ chatID: res.chatChannelId,
13
+ userCount: { now: res.concurrentUserCount, total: res.accumulateCount },
14
+ title: res.liveTitle,
15
+ startOn: res.openDate,
16
+ closeOn: res.closeDate,
17
+ status: res.status,
18
+ polling: JSON.parse(res.livePollingStatusJson)
19
+ }
20
+
21
+ return resolve(lvDetail);
22
+
23
+ });
24
+ }
25
+
26
+ async function getLiveStatus (channelID) {
27
+ return new Promise(async (resolve, reject) => {
28
+
29
+ let res = await reqChzzk("polling/v2/channels/" + channelID + "/live-status");
30
+ res = res.content;
31
+
32
+ let lvStatus = {
33
+ channelID: channelID,
34
+ chatID: res.chatChannelId,
35
+ userCount: { now: res.concurrentUserCount, total: res.accumulateCount },
36
+ title: res.liveTitle,
37
+ status: res.status,
38
+ polling: JSON.parse(res.livePollingStatusJson)
39
+ }
40
+
41
+ return resolve(lvStatus);
42
+
43
+ });
44
+ }
45
+
46
+ module.exports = {
47
+ getLiveDetail: getLiveDetail,
48
+ getLiveStatus: getLiveStatus
49
+ }
package/lib/tool.js ADDED
@@ -0,0 +1,36 @@
1
+ const { chzzkBaseURL, gameBaseURL, NID } = require("./val.js");
2
+
3
+ function reqChzzk (path) {
4
+ return new Promise(async (resolve, reject) => {
5
+
6
+ fetch(chzzkBaseURL + path, {
7
+ method: "GET",
8
+ headers: {
9
+ "Cookie": "NID_AUT=" + NID.AUT + ";NID_SES=" + NID.SES
10
+ }
11
+ })
12
+
13
+ .then((response) => resolve(response.json()));
14
+
15
+ });
16
+ }
17
+
18
+ function reqGame (path) {
19
+ return new Promise(async (resolve, reject) => {
20
+
21
+ fetch(gameBaseURL + path, {
22
+ method: "GET",
23
+ headers: {
24
+ "Cookie": "NID_AUT=" + NID.AUT + ";NID_SES=" + NID.SES
25
+ }
26
+ })
27
+
28
+ .then((response) => resolve(response.json()));
29
+
30
+ });
31
+ }
32
+
33
+ module.exports = {
34
+ reqChzzk: reqChzzk,
35
+ reqGame: reqGame
36
+ }
package/lib/val.js ADDED
@@ -0,0 +1,19 @@
1
+ const chzzkBaseURL = "https://api.chzzk.naver.com/";
2
+ const gameBaseURL = "https://comm-api.game.naver.com/";
3
+
4
+ let NID = {
5
+ AUT: null,
6
+ SES: null
7
+ }
8
+
9
+ function login (NID_AUT, NID_SES) {
10
+ NID.AUT = NID_AUT;
11
+ NID.SES = NID_SES;
12
+ }
13
+
14
+ module.exports = {
15
+ chzzkBaseURL: chzzkBaseURL,
16
+ gameBaseURL: gameBaseURL,
17
+ NID: NID,
18
+ login: login
19
+ }
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "buzzk",
3
+ "displayName": "BUZZK",
4
+ "version": "1.0.0",
5
+ "description": "뿌지직 (BUZZK) - 치지직(CHZZK) 챗봇을 더욱 쉽게 개발할 수 있도록 돕는 비공식 라이브러리.",
6
+ "main": "lib/index.js",
7
+ "directories": {
8
+ "lib": "lib"
9
+ },
10
+ "scripts": {
11
+ "test": "echo \"Error: no test specified\" && exit 1"
12
+ },
13
+ "author": {
14
+ "name": "Emin-G",
15
+ "email": "dev.mingowo@gmail.com"
16
+ },
17
+ "license": "MIT",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/Emin-G/buzzk.git"
21
+ },
22
+ "bugs": {
23
+ "url": "https://github.com/Emin-G/buzzk/issues"
24
+ },
25
+ "homepage": "https://github.com/Emin-G/buzzk",
26
+ "keywords": ["chzzk", "buzzk", "치지직", "뿌지직"]
27
+ }