오윤석

character model

1 +axios = require('axios');
2 +
3 +const crwalCharacterCode = async function(nickname, isReboot = false) {
4 + try {
5 + const resp = await axios.get("https://maplestory.nexon.com/Ranking/World/Total?c=" + encodeURI(nickname) + "&w=" + (isReboot ? "0" : "254"));
6 +
7 + const regex = new RegExp(`<dt><a href=\\"\\/Common\\/Character\\/Detail\\/[^\\?]+?\\?p=(.+?)\\"\\s+target=.+?\\/>${nickname}<\\/a><\\/dt>`);
8 + const regexResult = regex.exec(resp.data);
9 +
10 + if (!regexResult) {
11 + if (isReboot)
12 + return -2;
13 + else
14 + return await crwalCharacterCode(nickname, true);
15 + }
16 +
17 + return regexResult[1];
18 + } catch (error) {
19 + console.log(error);
20 + return -1;
21 + }
22 +};
23 +
24 +const getCharacterInfo = async function(nickname, characterCode) {
25 + try {
26 + const resp = await axios.get("https://maplestory.nexon.com/Common/Character/Detail/" + encodeURI(nickname) + "?p=" + characterCode);
27 +
28 + if (resp.data.indexOf("공개하지 않은 정보입니다.") >= 0) {
29 + throw new Error("private_character");
30 + }
31 +
32 + if (resp.data.indexOf("메이플스토리 게임 점검 중에는 이용하실 수 없습니다.") >= 0) {
33 + throw new Error("game_checking");
34 + }
35 +
36 + const character = {
37 + nickname: nickname,
38 + characterCode: characterCode,
39 + job: null,
40 + level: null,
41 + avatar: null,
42 + server: {
43 + icon: null,
44 + name: null
45 + },
46 + majorName: null,
47 + attackPowerName: null
48 + };
49 + const stats = {
50 + major: 0,
51 + minor: 0,
52 + majorHyper: 0,
53 + damageHyper: 0,
54 + criticalDamage: 0,
55 + bossAttackDamage: 0,
56 + ignoreGuard: 0,
57 + statAttackPower: 0
58 + };
59 +
60 + const { JSDOM } = require('jsdom');
61 + const dom = new JSDOM(resp.data);
62 + const $ = (require('jquery'))(dom.window);
63 +
64 + const jobModel = require('../model/job');
65 + const statModel = require('../model/stat');
66 +
67 + character.job = $(".tab01_con_wrap .table_style01:eq(0) tbody tr:eq(0) td:eq(1) span").text();
68 + character.level = parseInt($(".char_info dl:eq(0) dd").text().substring(3));
69 + character.avatar = $(".char_img img").attr("src");
70 + character.server = {
71 + name: $(".char_info dl:eq(2) dd").text(),
72 + icon: $(".char_info dl:eq(2) dd img").attr("src")
73 + };
74 + character.majorName = jobModel[character.job].major;
75 + character.attackPowerName = character.majorName == "INT" ? "마력" : "공격력";
76 +
77 + const $statInfo = $(".tab01_con_wrap .table_style01:eq(1)");
78 + $("tbody tr", $statInfo).each(function() {
79 + if ($("th", this).length == 1) {
80 + if ($("th span", this).text() == "하이퍼스탯") {
81 + const values = $("td span", this).html().split("<br>");
82 +
83 + const regexMajor = new RegExp(`${statModel[character.majorName].korean} (\\d+) 증가`);
84 + const regexDamage = new RegExp(`^데미지 (\\d+)% 증가`);
85 +
86 + let regexResult;
87 + for (let i = 0; i < values.length; i++) {
88 + if (regexResult = regexMajor.exec(values[i]))
89 + stats['majorHyper'] = parseInt(regexResult[1]);
90 + else if (regexResult = regexDamage.exec(values[i]))
91 + stats['damageHyper'] = parseInt(regexResult[1]);
92 + }
93 + }
94 + } else {
95 + for (let i = 0; i < 2; i++) {
96 + const statName = $(`th:eq(${i}) span`, this).text();
97 + const value = $(`td:eq(${i}) span`, this).text().replace(/\,/g, "");
98 +
99 + switch (statName) {
100 + case character.majorName:
101 + stats['major'] = parseInt(value);
102 + break;
103 + case jobModel[character.job].minor:
104 + stats['minor'] = parseInt(value);
105 + break;
106 + case "크리티컬 데미지":
107 + stats['criticalDamage'] = parseInt(value);
108 + break;
109 + case "보스공격력":
110 + stats['bossAttackDamage'] = parseInt(value);
111 + break;
112 + case "방어율무시":
113 + stats['ignoreGuard'] = parseInt(value);
114 + break;
115 + case "스탯공격력":
116 + stats['statAttackPower'] = parseInt(value.split(' ~ ')[1]);
117 + }
118 + }
119 + }
120 + });
121 +
122 + return {
123 + character: character,
124 + stats: stats
125 + };
126 + } catch (error) {
127 + console.log(error);
128 + if (error.message == "private_character")
129 + return -1;
130 + else if (error.message == "game_checking")
131 + return -2;
132 + else
133 + return -999;
134 + }
135 +}
136 +
137 +const analyzeEquipment = async function(nickname, characterCode, job) {
138 + try {
139 + const resp = await axios.get("https://maplestory.nexon.com/Common/Character/Detail/" + encodeURI(nickname) + "/Equipment?p=" + characterCode);
140 +
141 + if (resp.data.indexOf("공개하지 않은 정보입니다.") >= 0) {
142 + throw new Error("private_character");
143 + }
144 +
145 + if (resp.data.indexOf("메이플스토리 게임 점검 중에는 이용하실 수 없습니다.") >= 0) {
146 + throw new Error("game_checking");
147 + }
148 +
149 + const { JSDOM } = require('jsdom');
150 + const dom = new JSDOM(resp.data);
151 + const $ = (require('jquery'))(dom.window);
152 +
153 + // 아케인심볼 분석
154 + let majorArcane = 0;
155 + const arcaneURLs = [];
156 + $(".tab03_con_wrap .arcane_weapon_wrap .item_pot li span a").each(async function() {
157 + if (!!$(this).attr("href"))
158 + arcaneURLs.push("https://maplestory.nexon.com" + $(this).attr("href"));
159 + });
160 +
161 + for (let i = 0; i < arcaneURLs.length; i++) {
162 + const equipmentResp = await axios.get(arcaneURLs[i], {
163 + headers: {
164 + 'X-Requested-With': 'XMLHttpRequest'
165 + }
166 + });
167 +
168 + const equipmentDom = new JSDOM(equipmentResp.data.view);
169 + const $equipment = (require('jquery'))(equipmentDom.window);
170 +
171 + majorArcane += parseInt($equipment(".stet_info ul li:eq(2) .point_td font:eq(0)").text().substring(1));
172 + }
173 +
174 + // 장비 분석
175 + const jobModel = require('../model/job');
176 +
177 + let damagePercent = 0;
178 + let majorPercent = 0;
179 + let attackPowerPercent = 0;
180 + let weapon = undefined;
181 + const equipmentURLs = [];
182 + $(".tab01_con_wrap .weapon_wrap .item_pot li span a").each(async function() {
183 + equipmentURLs.push("https://maplestory.nexon.com" + $(this).attr("href"));
184 + });
185 +
186 + for (let i = 0; i < equipmentURLs.length; i++) {
187 + const equipmentResp = await axios.get(equipmentURLs[i], {
188 + headers: {
189 + 'X-Requested-With': 'XMLHttpRequest'
190 + }
191 + });
192 +
193 + const equipmentDom = new JSDOM(equipmentResp.data.view);
194 + const $equipment = (require('jquery'))(equipmentDom.window);
195 +
196 + const equipmentType = $equipment(".item_ability .ablilty02:eq(1) .job_name em").text();
197 + if (equipmentType.indexOf("손무기") >= 0 && equipmentType.indexOf("블레이드") < 0 && equipmentType.indexOf("대검") < 0) {
198 + weapon = equipmentType.split(" (")[0];
199 + }
200 +
201 + $equipment(".stet_info ul li").each(function() {
202 + const regexMajor1 = new RegExp(`${jobModel[job].major} : \\+(\\d+)%`);
203 + const regexMajor2 = new RegExp(`올스탯 : \\+(\\d+)%`);
204 + const regexAttackPower = (jobModel[job].major == "INT") ?
205 + new RegExp(`마력 : \\+(\\d+)%`) :
206 + new RegExp(`공격력 : \\+(\\d+)%`);
207 + const regexDamage = new RegExp(`^데미지 : \\+(\\d+)%`);
208 +
209 + if ($(this).find(".stet_th span").text() == "올스탯") {
210 + majorPercent += parseInt($(this).find(".point_td font:eq(0)").text().substring(1));
211 + } else if ($(this).find(".stet_th span").text().indexOf("잠재옵션") >= 0) {
212 + const values = $(this).find(".point_td").html().split("<br>");
213 + for (let j = 0; j < values.length; j++) {
214 + const value = values[j].trim();
215 + let regexResult;
216 +
217 + if (regexResult = (regexMajor1.exec(value) || regexMajor2.exec(value))) {
218 + majorPercent += parseInt(regexResult[1]);
219 + } else if (regexResult = regexAttackPower.exec(value)) {
220 + attackPowerPercent += parseInt(regexResult[1]);
221 + } else if (regexResult = regexDamage.exec(value)) {
222 + damagePercent += parseInt(regexResult[1]);
223 + }
224 + }
225 + }
226 + })
227 + }
228 +
229 + return {
230 + majorArcane: majorArcane,
231 + majorPercent: majorPercent,
232 + attackPowerPercent: attackPowerPercent,
233 + damagePercent: damagePercent,
234 + weapon: weapon
235 + };
236 + } catch (error) {
237 + console.log(error);
238 + if (error.message == "private_character")
239 + return -1;
240 + else if (error.message == "game_checking")
241 + return -2;
242 + else
243 + return -999;
244 + }
245 +}
246 +
247 +module.exports = {
248 + crwalCharacterCode: crwalCharacterCode,
249 + getCharacterInfo: getCharacterInfo,
250 + analyzeEquipment: analyzeEquipment,
251 +}
...\ No newline at end of file ...\ No newline at end of file
1 -axios = require('axios'); 1 +const characterModel = require('../model/character');
2 -
3 -const crwalCharacterCode = async function(nickname, isReboot = false) {
4 - try {
5 - const resp = await axios.get("https://maplestory.nexon.com/Ranking/World/Total?c=" + encodeURI(nickname) + "&w=" + (isReboot ? "0" : "254"));
6 -
7 - const regex = new RegExp(`<dt><a href=\\"\\/Common\\/Character\\/Detail\\/[^\\?]+?\\?p=(.+?)\\"\\s+target=.+?\\/>${nickname}<\\/a><\\/dt>`);
8 - const regexResult = regex.exec(resp.data);
9 -
10 - if (!regexResult) {
11 - if (isReboot)
12 - return -2;
13 - else
14 - return await crwalCharacterCode(nickname, true);
15 - }
16 -
17 - return regexResult[1];
18 - } catch (error) {
19 - console.log(error);
20 - return -1;
21 - }
22 -}
23 -
24 -const getCharacterInfo = async function(nickname, characterCode) {
25 - try {
26 - const resp = await axios.get("https://maplestory.nexon.com/Common/Character/Detail/" + encodeURI(nickname) + "?p=" + characterCode);
27 -
28 - if (resp.data.indexOf("공개하지 않은 정보입니다.") >= 0) {
29 - throw new Error("private_character");
30 - }
31 -
32 - if (resp.data.indexOf("메이플스토리 게임 점검 중에는 이용하실 수 없습니다.") >= 0) {
33 - throw new Error("game_checking");
34 - }
35 -
36 - const character = {
37 - nickname: nickname,
38 - characterCode: characterCode,
39 - job: null,
40 - level: null,
41 - avatar: null,
42 - server: {
43 - icon: null,
44 - name: null
45 - },
46 - majorName: null,
47 - attackPowerName: null
48 - };
49 - const stats = {
50 - major: 0,
51 - minor: 0,
52 - majorHyper: 0,
53 - damageHyper: 0,
54 - criticalDamage: 0,
55 - bossAttackDamage: 0,
56 - ignoreGuard: 0,
57 - statAttackPower: 0
58 - };
59 -
60 - const { JSDOM } = require('jsdom');
61 - const dom = new JSDOM(resp.data);
62 - const $ = (require('jquery'))(dom.window);
63 -
64 - const jobModel = require('../model/job');
65 - const statModel = require('../model/stat');
66 -
67 - character.job = $(".tab01_con_wrap .table_style01:eq(0) tbody tr:eq(0) td:eq(1) span").text();
68 - character.level = parseInt($(".char_info dl:eq(0) dd").text().substring(3));
69 - character.avatar = $(".char_img img").attr("src");
70 - character.server = {
71 - name: $(".char_info dl:eq(2) dd").text(),
72 - icon: $(".char_info dl:eq(2) dd img").attr("src")
73 - };
74 - character.majorName = jobModel[character.job].major;
75 - character.attackPowerName = character.majorName == "INT" ? "마력" : "공격력";
76 -
77 - const $statInfo = $(".tab01_con_wrap .table_style01:eq(1)");
78 - $("tbody tr", $statInfo).each(function() {
79 - if ($("th", this).length == 1) {
80 - if ($("th span", this).text() == "하이퍼스탯") {
81 - const values = $("td span", this).html().split("<br>");
82 -
83 - const regexMajor = new RegExp(`${statModel[character.majorName].korean} (\\d+) 증가`);
84 - const regexDamage = new RegExp(`^데미지 (\\d+)% 증가`);
85 -
86 - let regexResult;
87 - for (let i = 0; i < values.length; i++) {
88 - if (regexResult = regexMajor.exec(values[i]))
89 - stats['majorHyper'] = parseInt(regexResult[1]);
90 - else if (regexResult = regexDamage.exec(values[i]))
91 - stats['damageHyper'] = parseInt(regexResult[1]);
92 - }
93 - }
94 - } else {
95 - for (let i = 0; i < 2; i++) {
96 - const statName = $(`th:eq(${i}) span`, this).text();
97 - const value = $(`td:eq(${i}) span`, this).text().replace(/\,/g, "");
98 -
99 - switch (statName) {
100 - case character.majorName:
101 - stats['major'] = parseInt(value);
102 - break;
103 - case jobModel[character.job].minor:
104 - stats['minor'] = parseInt(value);
105 - break;
106 - case "크리티컬 데미지":
107 - stats['criticalDamage'] = parseInt(value);
108 - break;
109 - case "보스공격력":
110 - stats['bossAttackDamage'] = parseInt(value);
111 - break;
112 - case "방어율무시":
113 - stats['ignoreGuard'] = parseInt(value);
114 - break;
115 - case "스탯공격력":
116 - stats['statAttackPower'] = parseInt(value.split(' ~ ')[1]);
117 - }
118 - }
119 - }
120 - });
121 -
122 - return {
123 - character: character,
124 - stats: stats
125 - };
126 - } catch (error) {
127 - console.log(error);
128 - if (error.message == "private_character")
129 - return -1;
130 - else if (error.message == "game_checking")
131 - return -2;
132 - else
133 - return -999;
134 - }
135 -}
136 -
137 -const analyzeEquipment = async function(nickname, characterCode, job) {
138 - try {
139 - const resp = await axios.get("https://maplestory.nexon.com/Common/Character/Detail/" + encodeURI(nickname) + "/Equipment?p=" + characterCode);
140 -
141 - if (resp.data.indexOf("공개하지 않은 정보입니다.") >= 0) {
142 - throw new Error("private_character");
143 - }
144 -
145 - if (resp.data.indexOf("메이플스토리 게임 점검 중에는 이용하실 수 없습니다.") >= 0) {
146 - throw new Error("game_checking");
147 - }
148 -
149 - const { JSDOM } = require('jsdom');
150 - const dom = new JSDOM(resp.data);
151 - const $ = (require('jquery'))(dom.window);
152 -
153 - // 아케인심볼 분석
154 - let majorArcane = 0;
155 - const arcaneURLs = [];
156 - $(".tab03_con_wrap .arcane_weapon_wrap .item_pot li span a").each(async function() {
157 - if (!!$(this).attr("href"))
158 - arcaneURLs.push("https://maplestory.nexon.com" + $(this).attr("href"));
159 - });
160 -
161 - for (let i = 0; i < arcaneURLs.length; i++) {
162 - const equipmentResp = await axios.get(arcaneURLs[i], {
163 - headers: {
164 - 'X-Requested-With': 'XMLHttpRequest'
165 - }
166 - });
167 -
168 - const equipmentDom = new JSDOM(equipmentResp.data.view);
169 - const $equipment = (require('jquery'))(equipmentDom.window);
170 -
171 - majorArcane += parseInt($equipment(".stet_info ul li:eq(2) .point_td font:eq(0)").text().substring(1));
172 - }
173 -
174 - // 장비 분석
175 - const jobModel = require('../model/job');
176 -
177 - let damagePercent = 0;
178 - let majorPercent = 0;
179 - let attackPowerPercent = 0;
180 - let weapon = undefined;
181 - const equipmentURLs = [];
182 - $(".tab01_con_wrap .weapon_wrap .item_pot li span a").each(async function() {
183 - equipmentURLs.push("https://maplestory.nexon.com" + $(this).attr("href"));
184 - });
185 -
186 - for (let i = 0; i < equipmentURLs.length; i++) {
187 - const equipmentResp = await axios.get(equipmentURLs[i], {
188 - headers: {
189 - 'X-Requested-With': 'XMLHttpRequest'
190 - }
191 - });
192 -
193 - const equipmentDom = new JSDOM(equipmentResp.data.view);
194 - const $equipment = (require('jquery'))(equipmentDom.window);
195 -
196 - const equipmentType = $equipment(".item_ability .ablilty02:eq(1) .job_name em").text();
197 - if (equipmentType.indexOf("손무기") >= 0 && equipmentType.indexOf("블레이드") < 0 && equipmentType.indexOf("대검") < 0) {
198 - weapon = equipmentType.split(" (")[0];
199 - }
200 -
201 - $equipment(".stet_info ul li").each(function() {
202 - const regexMajor1 = new RegExp(`${jobModel[job].major} : \\+(\\d+)%`);
203 - const regexMajor2 = new RegExp(`올스탯 : \\+(\\d+)%`);
204 - const regexAttackPower = (jobModel[job].major == "INT") ?
205 - new RegExp(`마력 : \\+(\\d+)%`) :
206 - new RegExp(`공격력 : \\+(\\d+)%`);
207 - const regexDamage = new RegExp(`^데미지 : \\+(\\d+)%`);
208 -
209 - if ($(this).find(".stet_th span").text() == "올스탯") {
210 - majorPercent += parseInt($(this).find(".point_td font:eq(0)").text().substring(1));
211 - } else if ($(this).find(".stet_th span").text().indexOf("잠재옵션") >= 0) {
212 - const values = $(this).find(".point_td").html().split("<br>");
213 - for (let j = 0; j < values.length; j++) {
214 - const value = values[j].trim();
215 - let regexResult;
216 -
217 - if (regexResult = (regexMajor1.exec(value) || regexMajor2.exec(value))) {
218 - majorPercent += parseInt(regexResult[1]);
219 - } else if (regexResult = regexAttackPower.exec(value)) {
220 - attackPowerPercent += parseInt(regexResult[1]);
221 - } else if (regexResult = regexDamage.exec(value)) {
222 - damagePercent += parseInt(regexResult[1]);
223 - }
224 - }
225 - }
226 - })
227 - }
228 -
229 - return {
230 - majorArcane: majorArcane,
231 - majorPercent: majorPercent,
232 - attackPowerPercent: attackPowerPercent,
233 - damagePercent: damagePercent,
234 - weapon: weapon
235 - };
236 - } catch (error) {
237 - console.log(error);
238 - if (error.message == "private_character")
239 - return -1;
240 - else if (error.message == "game_checking")
241 - return -2;
242 - else
243 - return -999;
244 - }
245 -}
246 2
247 const analyzeStats = function(characterInfo, analysisEquipment) { 3 const analyzeStats = function(characterInfo, analysisEquipment) {
248 const jobModel = require('../model/job'); 4 const jobModel = require('../model/job');
...@@ -401,7 +157,7 @@ module.exports = { ...@@ -401,7 +157,7 @@ module.exports = {
401 } 157 }
402 158
403 const nickname = req.query.nickname; 159 const nickname = req.query.nickname;
404 - const characterCode = await crwalCharacterCode(req.query.nickname); 160 + const characterCode = await characterModel.crwalCharacterCode(req.query.nickname);
405 161
406 if (characterCode == -1) { 162 if (characterCode == -1) {
407 res.status(500).send(); 163 res.status(500).send();
...@@ -411,7 +167,7 @@ module.exports = { ...@@ -411,7 +167,7 @@ module.exports = {
411 return; 167 return;
412 } 168 }
413 169
414 - const characterInfo = await getCharacterInfo(nickname, characterCode); 170 + const characterInfo = await characterModel.getCharacterInfo(nickname, characterCode);
415 if (characterInfo == -1) { 171 if (characterInfo == -1) {
416 // 접근 권한 설정 필요 172 // 접근 권한 설정 필요
417 res.status(403).send(); 173 res.status(403).send();
...@@ -425,7 +181,7 @@ module.exports = { ...@@ -425,7 +181,7 @@ module.exports = {
425 return; 181 return;
426 } 182 }
427 183
428 - const analysisEquipment = await analyzeEquipment(nickname, characterCode, characterInfo.character.job); 184 + const analysisEquipment = await characterModel.analyzeEquipment(nickname, characterCode, characterInfo.character.job);
429 if (analysisEquipment == -1) { 185 if (analysisEquipment == -1) {
430 // 접근 권한 설정 필요 186 // 접근 권한 설정 필요
431 res.status(403).send(); 187 res.status(403).send();
......