신원형

temp version

...@@ -14,6 +14,10 @@ const TOKEN = tokens.channel ...@@ -14,6 +14,10 @@ const TOKEN = tokens.channel
14 const id = tokens.id 14 const id = tokens.id
15 const pw = tokens.pw 15 const pw = tokens.pw
16 16
17 +var first = false; //첫 시도인지
18 +var second = false; //첫번째 분류 선택했는지
19 +var destCar = "";
20 +var destination = [];
17 const canvas = require('./khcanvas') 21 const canvas = require('./khcanvas')
18 const selector = require('./schedule_selector') 22 const selector = require('./schedule_selector')
19 const weather = require('./weather') 23 const weather = require('./weather')
...@@ -87,6 +91,66 @@ function is_good_weather(weather_data) { ...@@ -87,6 +91,66 @@ function is_good_weather(weather_data) {
87 return true 91 return true
88 } 92 }
89 93
94 +
95 +
96 +
97 +const csv = require('csv-parser')
98 +const results = [];
99 +
100 + function chooseFile () {
101 + if (destCar == "cafe") {
102 + fs.createReadStream('cafe_list.csv')
103 + .pipe(csv())
104 + .on('data', (data) => results.push(data))
105 + .on('end', () => {
106 + console.log(results);
107 + });
108 + }
109 + else if (destCar == "meal") {
110 + fs.createReadStream('meal_list.csv')
111 + .pipe(csv())
112 + .on('data', (data) => results.push(data))
113 + .on('end', () => {
114 + console.log(results);
115 + });
116 + }
117 + else if (destCar == "play") {
118 + fs.createReadStream('play_list.csv')
119 + .pipe(csv())
120 + .on('data', (data) => results.push(data))
121 + .on('end', () => {
122 + console.log(results);
123 + });
124 + }
125 + else if (destCar == "bar") {
126 + fs.createReadStream('bar_list.csv')
127 + .pipe(csv())
128 + .on('data', (data) => results.push(data))
129 + .on('end', () => {
130 + console.log(results);
131 + });
132 + }
133 +}
134 +
135 +function randomNum(min, max) {
136 + var randNum = Math.floor(Math.random() * (max - min + 1)) + min;
137 + return randNum;
138 +}
139 +
140 +function getX() {
141 + return destination.x;
142 +}
143 +function getY() {
144 + return destination.y;
145 +}
146 +function getName() {
147 + return destination.name;
148 +}
149 +function getAddress() {
150 + return destination.address;
151 +}
152 +
153 +
90 /* 154 /*
91 155
92 1. 사용자가 아무 메세지나 입력? 156 1. 사용자가 아무 메세지나 입력?
...@@ -101,8 +165,8 @@ app.use(bodyParser.json()); ...@@ -101,8 +165,8 @@ app.use(bodyParser.json());
101 app.post('/hook', async function (req, res) { 165 app.post('/hook', async function (req, res) {
102 166
103 var eventObj = req.body.events[0]; 167 var eventObj = req.body.events[0];
104 - // var source = eventObj.source; 168 + var source = eventObj.source;
105 - // var message = eventObj.message; 169 + var message = eventObj.message;
106 170
107 // request log 171 // request log
108 console.log('======================', new Date(), '======================'); 172 console.log('======================', new Date(), '======================');
...@@ -110,37 +174,134 @@ app.post('/hook', async function (req, res) { ...@@ -110,37 +174,134 @@ app.post('/hook', async function (req, res) {
110 console.log('[request source] ', eventObj.source); 174 console.log('[request source] ', eventObj.source);
111 console.log('[request message]', eventObj.message); 175 console.log('[request message]', eventObj.message);
112 176
113 - const today = new Date() 177 + if (first == false && eventObj.message.text == "처음") {
114 - const filter_result = filter_date(today, id, pw)
115 178
116 - if (filter_result) { 179 + const today = new Date()
117 - sendText(eventObj.replyToken, filter_result) 180 + const filter_result = filter_date(today, id, pw)
118 - } 181 +
182 + if (filter_result) {
183 + sendText(eventObj.replyToken, filter_result)
184 + }
185 +
186 + const good_weather = (await weather.get_weather_current()).then(it => {
187 + return is_good_weather(it)
188 + })
189 + if (!good_weather) {
190 + sendText(eventObj.replyToken, "날씨가 나쁨")
191 + }
119 192
120 - const good_weather = (await weather.get_weather_current()).then(it => { 193 + request.post(
121 - return is_good_weather(it) 194 + {
122 - }) 195 + url: TARGET_URL,
123 - if (!good_weather) { 196 + headers: {
124 - sendText(eventObj.replyToken, "날씨가 나쁨") 197 + 'Authorization': `Bearer ${TOKEN}`
198 + },
199 + json: {
200 + "replyToken": eventObj.replyToken,
201 + "messages": [
202 + {
203 + "type": "text",
204 + "text": "카테고리를 선택해주세요.\n1. 식사\n2. 카페\n3. 술\n4. 놀거리\n(숫자만 입력해주세요)"
205 + }
206 + ]
207 + },
208 + }, (error, response, body) => {
209 + console.log(body)
210 + });
211 + first = true;
212 + }
213 + else if (first == true && second == false) {
214 +
215 + if (eventObj.message.text == 1) { //식사 선택
216 + request.post(
217 + {
218 + url: TARGET_URL, headers: { 'Authorization': `Bearer ${TOKEN}` }, json: {
219 + "replyToken": eventObj.replyToken,
220 + "messages": [{ "type": "text", "text": "[식사] 키워드를 선택해주세요\n1. 양식\n2. 한식\n3. 중식\n4. 일식\n5. 기타\n(숫자만 입력해주세요)" }]
221 + }
222 + }, (error, response, body) => {
223 + console.log(body)
224 + });
225 + destCar = "meal";
226 + } else if (eventObj.message.text == 2) { //카페 선택
227 + request.post(
228 + {
229 + url: TARGET_URL, headers: { 'Authorization': `Bearer ${TOKEN}` }, json: {
230 + "replyToken": eventObj.replyToken,
231 + "messages": [{ "type": "text", "text": "[카페] 키워드를 선택해주세요\n1. 감성\n2. 카공\n3. 디저트\n(숫자만 입력해주세요)" }]
232 + }
233 + }, (error, response, body) => {
234 + console.log(body)
235 + });
236 + destCar = "cafe";
237 + } else if (eventObj.message.text == 3) { //술 선택
238 + request.post(
239 + {
240 + url: TARGET_URL, headers: { 'Authorization': `Bearer ${TOKEN}` }, json: {
241 + "replyToken": eventObj.replyToken,
242 + "messages": [{ "type": "text", "text": "[술] 키워드를 선택해주세요\n1. 소주\n2. 이자카야\n3. 막걸리\n4. 맥주\n(숫자만 입력해주세요)" }]
243 + }
244 + }, (error, response, body) => {
245 + console.log(body)
246 + });
247 + destCar = "bar"
248 + } else if (eventObj.message.text == 4) { //놀거리 선택
249 + request.post(
250 + {
251 + url: TARGET_URL, headers: { 'Authorization': `Bearer ${TOKEN}` }, json: {
252 + "replyToken": eventObj.replyToken,
253 + "messages": [{ "type": "text", "text": "[놀거리] 키워드를 선택해주세요\n1. 노래방\n2. 피시방\n3. 기타\n(숫자만 입력해주세요)" }]
254 + }
255 + }, (error, response, body) => {
256 + console.log(body)
257 + });
258 + destCar = "play"
259 + }
260 + chooseFile();
261 + second = true;
125 } 262 }
126 263
127 - 264 + else if (first == true && second == true) {
265 + if (destCar == "meal") {
266 + if (eventObj.message.text == 1) { var randpick = randomNum(0, 6); destination = results[randpick] }
267 + else if (eventObj.message.text == 2) { var randpick = randomNum(7, 19); destination = results[randpick] }
268 + else if (eventObj.message.text == 3) { var randpick = randomNum(20, 25); destination = results[randpick] }
269 + else if (eventObj.message.text == 4) { var randpick = randomNum(26, 36); destination = results[randpick] }
270 + else if (eventObj.message.text == 5) { var randpick = randomNum(37, 46); destination = results[randpick] }
271 + }
272 + else if (destCar == "cafe") {
273 + if (eventObj.message.text == 1) { var randpick = randomNum(0, 5); destination = results[randpick] }
274 + else if (eventObj.message.text == 2) { var randpick = randomNum(6, 12); destination = results[randpick] }
275 + else if (eventObj.message.text == 3) { var randpick = randomNum(13, 17); destination = results[randpick] }
276 + }
277 + else if (destCar == "bar") {
278 + if (eventObj.message.text == 1) { var randpick = randomNum(0, 5); destination = results[randpick] }
279 + else if (eventObj.message.text == 2) { var randpick = randomNum(6, 11); destination = results[randpick] }
280 + else if (eventObj.message.text == 3) { var randpick = randomNum(12, 15); destination = results[randpick] }
281 + else if (eventObj.message.text == 4) { var randpick = randomNum(16, 20); destination = results[randpick] }
282 + }
283 + else if (destCar == "play") {
284 + if (eventObj.message.text == 1) { var randpick = randomNum(0, 3); destination = results[randpick] }
285 + else if (eventObj.message.text == 2) { var randpick = randomNum(4, 8); destination = results[randpick] }
286 + else if (eventObj.message.text == 3) { var randpick = randomNum(9, 13); destination = results[randpick] }
287 + }
128 288
289 + sendLocation(eventObj.replyToken, getX(), getY(), getAddress(), getName())
290 + }
129 res.sendStatus(200); 291 res.sendStatus(200);
130 }); 292 });
131 293
132 try { 294 try {
133 - // const option = { 295 + const option = {
134 - // ca: fs.readFileSync('/etc/letsencrypt/live/' + domain + '/fullchain.pem'), 296 + ca: fs.readFileSync('/etc/letsencrypt/live/' + domain + '/fullchain.pem'),
135 - // key: fs.readFileSync(path.resolve(process.cwd(), '/etc/letsencrypt/live/' + domain + '/privkey.pem'), 'utf8').toString(), 297 + key: fs.readFileSync(path.resolve(process.cwd(), '/etc/letsencrypt/live/' + domain + '/privkey.pem'), 'utf8').toString(),
136 - // cert: fs.readFileSync(path.resolve(process.cwd(), '/etc/letsencrypt/live/' + domain + '/cert.pem'), 'utf8').toString(), 298 + cert: fs.readFileSync(path.resolve(process.cwd(), '/etc/letsencrypt/live/' + domain + '/cert.pem'), 'utf8').toString(),
137 - // }; 299 + };
138 300
139 - HTTPS.createServer(app).listen(sslport, () => { 301 + HTTPS.createServer(option, app).listen(sslport, () => {
140 console.log(`[HTTPS] Server is started on port ${sslport}`); 302 console.log(`[HTTPS] Server is started on port ${sslport}`);
141 }); 303 });
142 } catch (error) { 304 } catch (error) {
143 console.log('[HTTPS] HTTPS 오류가 발생하였습니다. HTTPS 서버는 실행되지 않습니다.'); 305 console.log('[HTTPS] HTTPS 오류가 발생하였습니다. HTTPS 서버는 실행되지 않습니다.');
144 console.log(error); 306 console.log(error);
145 -} 307 +}
146 -
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
11 "@types/axios": "^0.14.0", 11 "@types/axios": "^0.14.0",
12 "@types/selenium-webdriver": "^4.1.0", 12 "@types/selenium-webdriver": "^4.1.0",
13 "axios": "^0.27.2", 13 "axios": "^0.27.2",
14 + "csv-parser": "^3.0.0",
14 "express": "^4.18.1", 15 "express": "^4.18.1",
15 "express-session": "^1.17.3", 16 "express-session": "^1.17.3",
16 "mocha": "^10.0.0", 17 "mocha": "^10.0.0",
...@@ -555,6 +556,20 @@ ...@@ -555,6 +556,20 @@
555 "node": ">= 8" 556 "node": ">= 8"
556 } 557 }
557 }, 558 },
559 + "node_modules/csv-parser": {
560 + "version": "3.0.0",
561 + "resolved": "https://registry.npmjs.org/csv-parser/-/csv-parser-3.0.0.tgz",
562 + "integrity": "sha512-s6OYSXAK3IdKqYO33y09jhypG/bSDHPuyCme/IdEHfWpLf/jKcpitVFyOC6UemgGk8v7Q5u2XE0vvwmanxhGlQ==",
563 + "dependencies": {
564 + "minimist": "^1.2.0"
565 + },
566 + "bin": {
567 + "csv-parser": "bin/csv-parser"
568 + },
569 + "engines": {
570 + "node": ">= 10"
571 + }
572 + },
558 "node_modules/dashdash": { 573 "node_modules/dashdash": {
559 "version": "1.14.1", 574 "version": "1.14.1",
560 "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", 575 "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
...@@ -1721,6 +1736,11 @@ ...@@ -1721,6 +1736,11 @@
1721 "node": ">=10" 1736 "node": ">=10"
1722 } 1737 }
1723 }, 1738 },
1739 + "node_modules/minimist": {
1740 + "version": "1.2.6",
1741 + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
1742 + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
1743 + },
1724 "node_modules/mocha": { 1744 "node_modules/mocha": {
1725 "version": "10.0.0", 1745 "version": "10.0.0",
1726 "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.0.0.tgz", 1746 "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.0.0.tgz",
...@@ -3150,6 +3170,14 @@ ...@@ -3150,6 +3170,14 @@
3150 "which": "^2.0.1" 3170 "which": "^2.0.1"
3151 } 3171 }
3152 }, 3172 },
3173 + "csv-parser": {
3174 + "version": "3.0.0",
3175 + "resolved": "https://registry.npmjs.org/csv-parser/-/csv-parser-3.0.0.tgz",
3176 + "integrity": "sha512-s6OYSXAK3IdKqYO33y09jhypG/bSDHPuyCme/IdEHfWpLf/jKcpitVFyOC6UemgGk8v7Q5u2XE0vvwmanxhGlQ==",
3177 + "requires": {
3178 + "minimist": "^1.2.0"
3179 + }
3180 + },
3153 "dashdash": { 3181 "dashdash": {
3154 "version": "1.14.1", 3182 "version": "1.14.1",
3155 "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", 3183 "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
...@@ -4031,6 +4059,11 @@ ...@@ -4031,6 +4059,11 @@
4031 "brace-expansion": "^2.0.1" 4059 "brace-expansion": "^2.0.1"
4032 } 4060 }
4033 }, 4061 },
4062 + "minimist": {
4063 + "version": "1.2.6",
4064 + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
4065 + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
4066 + },
4034 "mocha": { 4067 "mocha": {
4035 "version": "10.0.0", 4068 "version": "10.0.0",
4036 "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.0.0.tgz", 4069 "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.0.0.tgz",
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
16 "@types/axios": "^0.14.0", 16 "@types/axios": "^0.14.0",
17 "@types/selenium-webdriver": "^4.1.0", 17 "@types/selenium-webdriver": "^4.1.0",
18 "axios": "^0.27.2", 18 "axios": "^0.27.2",
19 + "csv-parser": "^3.0.0",
19 "express": "^4.18.1", 20 "express": "^4.18.1",
20 "express-session": "^1.17.3", 21 "express-session": "^1.17.3",
21 "mocha": "^10.0.0", 22 "mocha": "^10.0.0",
......
1 -
2 -var first = false; //첫 시도인지
3 -var second = false; //첫번째 분류 선택했는지
4 -var destCar = "";
5 -var destination = [];
6 -
7 -const bodyParser = require('body-parser');
8 -var app = express();
9 -app.use(bodyParser.json());
10 -app.post('/hook', function (req, res) {
11 -
12 - var eventObj = req.body.events[0];
13 - var source = eventObj.source;
14 - var message = eventObj.message;
15 -
16 - // request log
17 - console.log('======================', new Date(), '======================');
18 - console.log('[request]', req.body);
19 - console.log('[request source] ', eventObj.source);
20 - console.log('[request message]', eventObj.message);
21 -
22 -
23 - if (first == false && eventObj.message.text == "처음") {
24 - request.post(
25 - {
26 - url: TARGET_URL,
27 - headers: {
28 - 'Authorization': `Bearer ${TOKEN}`
29 - },
30 - json: {
31 - "replyToken": eventObj.replyToken,
32 - "messages": [
33 - {
34 - "type": "text",
35 - "text": "카테고리를 선택해주세요.\n1. 식사\n2. 카페\n3. 술\n4. 놀거리\n(숫자만 입력해주세요)"
36 - }
37 - ]
38 - },
39 - }, (error, response, body) => {
40 - console.log(body)
41 - });
42 - first = true;
43 - }
44 -
45 - else if (first == true && second == false) {
46 -
47 - if (eventObj.message.text == 1) { //식사 선택
48 - request.post(
49 - {
50 - url: TARGET_URL, headers: { 'Authorization': `Bearer ${TOKEN}` }, json: {
51 - "replyToken": eventObj.replyToken,
52 - "messages": [{ "type": "text", "text": "[식사] 키워드를 선택해주세요\n1. 양식\n2. 한식\n3. 중식\n4. 일식\n5. 기타\n(숫자만 입력해주세요)" }]
53 - }
54 - }, (error, response, body) => {
55 - console.log(body)
56 - });
57 - destCar = "meal";
58 - } else if (eventObj.message.text == 2) { //카페 선택
59 - request.post(
60 - {
61 - url: TARGET_URL, headers: { 'Authorization': `Bearer ${TOKEN}` }, json: {
62 - "replyToken": eventObj.replyToken,
63 - "messages": [{ "type": "text", "text": "[카페] 키워드를 선택해주세요\n1. 감성\n2. 카공\n3. 디저트\n(숫자만 입력해주세요)" }]
64 - }
65 - }, (error, response, body) => {
66 - console.log(body)
67 - });
68 - destCar = "cafe";
69 - } else if (eventObj.message.text == 3) { //술 선택
70 - request.post(
71 - {
72 - url: TARGET_URL, headers: { 'Authorization': `Bearer ${TOKEN}` }, json: {
73 - "replyToken": eventObj.replyToken,
74 - "messages": [{ "type": "text", "text": "[술] 키워드를 선택해주세요\n1. 소주\n2. 이자카야\n3. 막걸리\n4. 맥주\n(숫자만 입력해주세요)" }]
75 - }
76 - }, (error, response, body) => {
77 - console.log(body)
78 - });
79 - destCar = "bar"
80 - } else if (eventObj.message.text == 4) { //놀거리 선택
81 - request.post(
82 - {
83 - url: TARGET_URL, headers: { 'Authorization': `Bearer ${TOKEN}` }, json: {
84 - "replyToken": eventObj.replyToken,
85 - "messages": [{ "type": "text", "text": "[놀거리] 키워드를 선택해주세요\n1. 노래방\n2. 피시방\n3. 기타\n(숫자만 입력해주세요)" }]
86 - }
87 - }, (error, response, body) => {
88 - console.log(body)
89 - });
90 - destCar = "play"
91 - }
92 - chooseFile();
93 - second = true;
94 -
95 - }
96 -
97 - else if (first == true && second == true) {
98 - if (destCar == "meal") {
99 - if (eventObj.message.text == 1) { var randpick = randomNum(0, 6); destination = results[randpick] }
100 - else if (eventObj.message.text == 2) { var randpick = randomNum(7, 19); destination = results[randpick] }
101 - else if (eventObj.message.text == 3) { var randpick = randomNum(20, 25); destination = results[randpick] }
102 - else if (eventObj.message.text == 4) { var randpick = randomNum(26, 36); destination = results[randpick] }
103 - else if (eventObj.message.text == 5) { var randpick = randomNum(37, 46); destination = results[randpick] }
104 - }
105 - else if (destCar == "cafe") {
106 - if (eventObj.message.text == 1) { var randpick = randomNum(0, 5); destination = results[randpick] }
107 - else if (eventObj.message.text == 2) { var randpick = randomNum(6, 12); destination = results[randpick] }
108 - else if (eventObj.message.text == 3) { var randpick = randomNum(13, 17); destination = results[randpick] }
109 - }
110 - else if (destCar == "bar") {
111 - if (eventObj.message.text == 1) { var randpick = randomNum(0, 5); destination = results[randpick] }
112 - else if (eventObj.message.text == 2) { var randpick = randomNum(6, 11); destination = results[randpick] }
113 - else if (eventObj.message.text == 3) { var randpick = randomNum(12, 15); destination = results[randpick] }
114 - else if (eventObj.message.text == 4) { var randpick = randomNum(16, 20); destination = results[randpick] }
115 - }
116 - else if (destCar == "play") {
117 - if (eventObj.message.text == 1) { var randpick = randomNum(0, 3); destination = results[randpick] }
118 - else if (eventObj.message.text == 2) { var randpick = randomNum(4, 8); destination = results[randpick] }
119 - else if (eventObj.message.text == 3) { var randpick = randomNum(9, 13); destination = results[randpick] }
120 - }
121 -
122 -
123 - }
124 - res.sendStatus(200);
125 -
126 -
127 -});
128 -
129 -try {
130 - const option = {
131 - ca: fs.readFileSync('/etc/letsencrypt/live/' + domain + '/fullchain.pem'),
132 - key: fs.readFileSync(path.resolve(process.cwd(), '/etc/letsencrypt/live/' + domain + '/privkey.pem'), 'utf8').toString(),
133 - cert: fs.readFileSync(path.resolve(process.cwd(), '/etc/letsencrypt/live/' + domain + '/cert.pem'), 'utf8').toString(),
134 - };
135 -
136 - HTTPS.createServer(option, app).listen(sslport, () => {
137 - console.log(`[HTTPS] Server is started on port ${sslport}`);
138 - });
139 -} catch (error) {
140 - console.log('[HTTPS] HTTPS 오류가 발생하였습니다. HTTPS 서버는 실행되지 않습니다.');
141 - console.log(error);
142 -}
143 -
144 -const csv = require('csv-parser')
145 -const results = [];
146 -
147 -chooseFile = function () {
148 - if (destCar == "cafe") {
149 - fs.createReadStream('cafe_list.csv')
150 - .pipe(csv())
151 - .on('data', (data) => results.push(data))
152 - .on('end', () => {
153 - console.log(results);
154 - });
155 - }
156 - else if (destCar == "meal") {
157 - fs.createReadStream('meal_list.csv')
158 - .pipe(csv())
159 - .on('data', (data) => results.push(data))
160 - .on('end', () => {
161 - console.log(results);
162 - });
163 - }
164 - else if (destCar == "play") {
165 - fs.createReadStream('play_list.csv')
166 - .pipe(csv())
167 - .on('data', (data) => results.push(data))
168 - .on('end', () => {
169 - console.log(results);
170 - });
171 - }
172 - else if (destCar == "bar") {
173 - fs.createReadStream('bar_list.csv')
174 - .pipe(csv())
175 - .on('data', (data) => results.push(data))
176 - .on('end', () => {
177 - console.log(results);
178 - });
179 - }
180 -}
181 -
182 -function randomNum(min, max) {
183 - var randNum = Math.floor(Math.random() * (max - min + 1)) + min;
184 - return randNum;
185 -}
186 -
187 -function getX() {
188 - return destination.x;
189 -}
190 -function getY() {
191 - return destination.y;
192 -}
193 -function getName() {
194 - return destination.name;
195 -}
196 -function getAddress() {
197 - return destination.address;
198 -}
199 -
200 -export { getX, getY, getName, getAddress }
...\ No newline at end of file ...\ No newline at end of file