forked from DarkTornado/ProjectM
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path4. Merisia.js
265 lines (237 loc) · 11.5 KB
/
4. Merisia.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
/*
Project - M
4th Open Source : Merisia
© 2018 Dark Tornado, All rights reserved.
<작동 방식>
1. 채팅방에서 사람들이 하는 채팅을 '내장메모리/Merisia/' 폴더에 '방이름.txt' 파일로 저장.
-> 각 채팅들은 엔터로 구분합니다. 따라서, 엔터가 포함된 채팅은 저장하지 않습니다.
-> 학습 및 채팅 전송은 채팅방마다 따로따로 작동합니다.
2. 해당 채팅방에서 채팅이 수신되면 10% 확률로 수신된 채팅과 파일 안에 있는 채팅의 유사도를 대충 검사.
-> 어절이 하나 이상 일치하면 유사하다고 봄.
3. 유사하다고 판단된 채팅들에 대한 답변을 채팅방으로 전송.
-> 배운게 없으면 전송 안함
<가이드라인>
- 개발자의 허락 없이 소스 코드 무단 배포 금지. 들키면, 싸대기 퍽퍽.
- 소스 사용시 원작자를 밝혀주세요.
ex) 이 봇은 Dark Tornado의 Project M - Merisia 소스를 사용하였습니다.
<라이선스>
이 소스에는 LGPL 3.0이 적용되어있습니다.
one line to give the library's name and an idea of what it does.
Copyright (C) 2018 Dark Tornado
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*상수 선언*/
const sdcard = android.os.Environment.getExternalStorageDirectory().getAbsolutePath(); //내장메모리 최상위 경로
/*상수 (객체) 선언*/
const Merisia = {}; //메리시아 관련 객체
const DB = {}; //파일 입/출력용 객제인데, 이름이 DB인건 기분탓
const preChat = {}; //도배 방지 구현용
const lastSender = {}; //보낸 사람 구분용
const botOn = {}; //봇 작동여부 설정용
/*변수 선언*/
var chatData = []; //1:1 채팅방에서 사용할 대화 목록이 저장될 배열
/*Merisia 객체*/
Merisia.checkWord = function(que, msg) { //적당히 비슷한 말인지 비교
var data = msg.split(" "); //수신된 채팅을 어절 단위로 나눔
var count = 0;
for (var n = 0; n < data.length; n++) { //저장된 채팅에 어절들이 몇 개 포함되어 있는지
if (que.indexOf(data[n]) != -1) {
count++;
}
}
return count; //반환
};
Merisia.getReply = function(room, msg) { //수신된 채팅에 대한 적당한 답변 반환
var data = DB.readData(room); //저장된 채팅들을 불러옴
if (data != null && Math.floor(Math.random() * 10) == 0) { //저장된 채팅이 없거나, 10% 확률이 터진게 아니면, 작동 안함
data = data.split("\n"); //냥
var result = []; //비슷한 말들이 들어갈 배열
var max = 0; //최대 유사도(?) 값
for (var n = 0; n < data.length - 1; n++) { //저장된 채팅들 중 비슷하다 싶은 녀석들을 배열에 넣을건데,
var count = Merisia.checkWord(data[n], msg); //유사도(?)를 가져와서
if (count > 0) { //유사한 채팅이라면,
if (count > max) { //기존에 확인했던 녀석들보다 유사도가 높으면, 결과 배열 초기화 및 최대 유사도 값 변경
max = count;
result = [];
}
if (count == max) { //이미 유사도가 더 높은 말이 있다면, 저장 안함
result.push(data[n + 1]); //배열에 추가
}
}
}
if (result[0] != null) return result[Math.floor(Math.random() * result.length)]; //배열이 빈게 아니라면 아무거나 하나 반환
}
return null; //일치하는게 없거나, 저장된 채팅이 없거나, 발동할 확률(?)이 아니면, null 반환
};
Merisia.say = function(msg, replier) { //그냥 말하는 함수
replier.reply("[AI] " + msg); //앞에다가 이상한 문구 붙이는 용도
};
Merisia.isValidData = function(msg) { //배울 만한 채팅인지 구분하는 함수
var invalids = ["#", "/"];
for (var n = 0; n < invalids.length; n++) {
if (msg.charAt(0) == invalids[n]) return false; //특정 문자로 시작하는 것은 학습 X.
}
var noStudy = ["\n", "//"]; //엔터가 포함된건 학습 X. 비속어 필터링 등도 여기다가 넣으면 이상한 말은 안배움
for (var n = 0; n < noStudy.length; n++) {
if (msg.indexOf(noStudy[n]) != -1) return false;
}
return true;
};
/*DB 객체*/
DB.createDir = function() { //배운 채팅들이 저장될 폴더를 만드는 함수
var folder = new java.io.File(sdcard + "/Merisia/"); //File 인스턴스 생성
folder.mkdirs(); //폴더 생성
};
DB.saveData = function(name, msg) { //파일에 내용을 저장하는 함수
try { //사실, 나도 어디서 긁어와서 이곳저곳에서 사용하는 거임
var file = new java.io.File(sdcard + "/Merisia/" + name + ".txt");
var fos = new java.io.FileOutputStream(file);
var str = new java.lang.String(msg);
fos.write(str.getBytes());
fos.close();
} catch (e) {
Log.debug(e + ", " + e.lineNumber);
}
};
DB.readData = function(name) { //파일에 저장된 내용을 불러오는 함수
try { //사실, 나도 어디서 긁어와서 이곳저곳에서 사용하는 거임
var file = new java.io.File(sdcard + "/Merisia/" + name + ".txt");
if (!file.exists()) return null;
var fis = new java.io.FileInputStream(file);
var isr = new java.io.InputStreamReader(fis);
var br = new java.io.BufferedReader(isr);
var str = br.readLine();
var line = "";
while ((line = br.readLine()) != null) {
str += "\n" + line;
}
fis.close();
isr.close();
br.close();
return str;
} catch (e) {
Log.debug(e + ", " + e.lineNumber);
}
};
DB.initChatData = function() {
var data = DB.loadChatData();
if (data == null) Log.info("1:1 채팅방용 대화 데이터를 받아오지 못했습니다.");
else chatData = data.split("\n");
};
DB.loadChatData = function() {
try {
var url = new java.net.URL("https://raw.githubusercontent.com/DarkTornado/ProjectM/master/ChatData.txt");
var con = url.openConnection();
if (con != null) {
con.setConnectTimeout(5000);
con.setUseCaches(false);
var isr = new java.io.InputStreamReader(con.getInputStream());
var br = new java.io.BufferedReader(isr);
var str = br.readLine();
var line = "";
while ((line = br.readLine()) != null) {
str += "\n" + line;
}
isr.close();
br.close();
con.disconnect();
}
return str.toString();
} catch (e) {
Log.debug(e);
}
};
/*전역에서 실행할 것들*/
DB.createDir(); //폴더 생성
DB.initChatData(); //Project M 깃허브에서 대화 목록을 불러옴
/*response 부분*/
function response(room, msg, sender, isGroupChat, replier) {
/*모르면 골롬*/
msg = msg.trim();
sender = sender.trim();
room = room.trim();
/*도배 방지*/
if (preChat[room] == msg) return; //동일한 채팅이 두 번 이상 연속으로 수신되면, 가볍게 무시
preChat[room] = msg;
/*봇 작동여부 결정 및 명령어 처리*/
procCmd(msg, room, isGroupChat, replier);
if (botOn[room] === undefined) botOn[room] = true;
if (botOn[room] == false) return;
/*반응 안할 채팅들*/
var noReply = [".", "사진", "동영상", "음성메시지", "카카오톡 프로필", "(이모티콘)", "카카오링크 이미지"];
for (var n = 0; n < noReply.length; n++) {
if (msg == noReply[n]) return;
}
/*1:1 채팅방은 chatHook에서 따로 처리*/
if (!isGroupChat) {
chatHook(room, msg, sender, replier);
return;
}
/*적당한 채팅 하나 가져와서 답장(?)하는 부분*/
var chat = Merisia.getReply(room, msg);
if (chat != null) Merisia.say(chat, replier);
/*채팅을 학습하는 부분*/
if (Merisia.isValidData(msg)) { //배울 만한 채팅인 경우,
var data = DB.readData(room); //배운 채팅 목록을 가져옴
if (data == null) { //이미 배운게 있다면
DB.saveData(room, msg); //새로 저장
} else { //아니면,
if (lastSender[room] == sender) { //같은 사람이 연속으로 채팅을 한 경우,
DB.saveData(room, data + " " + msg); //같은 채팅으로 분류
} else { //아니면,
DB.saveData(room, data + "\n" + msg); //다른 채팅으로 분류
}
}
}
lastSender[room] = sender;
}
/*1:1 채팅방용*/
function chatHook(room, msg, sender, replier) {
if (Math.floor(Math.random() * 3) != 0) { //2/3 확률로 반응
var result = []; //비슷한 말들이 들어갈 배열
for (var n = 0; n < chatData.length - 1; n++) { //저장된 채팅들 중 비슷하다 싶은 녀석들을 배열에 넣을건데,
if (Merisia.checkWord(chatData[n], msg) > 0) { //귀찮으니(?), 유사도가 1 이상이면 유사하다고 봄
result.push(chatData[n + 1]); //배열에 추가
}
}
if (result[0] != null) replier.reply(result[Math.floor(Math.random() * result.length)]); //배열이 빈게 아니라면 아무거나 하나 전송
}
}
/*그냥 명령어 목록*/
function procCmd(cmd, room, isGroupChat, r) {
switch (cmd) {
case "/on":
Merisia.say("Merisia가 말을 하기 시작합니다.", r);
botOn[room] = true;
break;
case "/off":
Merisia.say("Merisia가 말을 하지 않기 시작합니다.", r);
botOn[room] = false;
break;
case "/info":
Merisia.say("[Project M - 4th]\n봇 이름 : Merisia\n버전 : 3.0\n원작자 : Dark Tornado\n\n 나름 사람처럼 대화하는 카카오톡 봇 만들기 프로젝트(?) 중 4번째 오픈 소스인 Merisia(메리시아)입니다.", r);
break;
case "/help":
Merisia.say("Merisia의 명령어 목록입니다.\n /on - Merisia을 활성화시킵니다.\n /off - Merisia을 비활성화시킵니다.\n /info - Merisia의 정보를 띄웁니다.\n /help - Merisia의 명령어 목록을 띄웁니다.\n /DB - 단체 채팅방의 경우, 해당 채팅방에서 배운 채팅들의 수를 출력합니다.", r);
break;
case "/DB":
if (isGroupChat) {
var data = DB.readData(room);
if (data == null) Merisia.say("0개입니다.", r);
else Merisia.say(data.split("\n").length + "개입니다.", r);
} else {
Merisia.say(chatData.length + "개입니다.", r);
}
break;
}
}