CN POE Export

Export CN POE data.

目前为 2023-12-15 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name CN POE Export
  3. // @namespace https://github.com/cn-poe-community/cn-poe-export-monkey
  4. // @version 0.0.13
  5. // @description Export CN POE data.
  6. // @author me1ting
  7. // @match https://poe.game.qq.com/my-account
  8. // @match https://poe.game.qq.com/account/view-profile/*
  9. // @match https://poe.game.qq.com/forum
  10. // @icon https://poecdn.game.qq.com/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvU2NvdXRpbmdSZXBvcnQiLCJ3IjoxLCJoIjoxLCJzY2FsZSI6MX1d/584635f3c8/ScoutingReport.png
  11. // @require https://unpkg.com/cn-poe-translator@0.2.6/dist/translator.global.js
  12. // @require https://unpkg.com/cn-poe-export-db@0.1.1/dist/db.global.js
  13. // @require https://unpkg.com/pob-building-creater@0.0.13/dist/creater.global.js
  14. // @require https://unpkg.com/pako@2.1.0/dist/pako_deflate.min.js
  15. // @require https://unpkg.com/axios@1.1.2/dist/axios.min.js
  16. // @require https://unpkg.com/vue@3.3.2/dist/vue.global.prod.js
  17. // @grant none
  18. // @license MIT
  19. // ==/UserScript==
  20.  
  21. (function(){
  22. const {computed, ref, reactive, onMounted, openBlock, createElementBlock, Fragment, createElementVNode, withDirectives, vModelText, unref, renderList, toDisplayString, vModelSelect, createCommentVNode, pushScopeId, popScopeId, createBlock, createApp } = Vue;
  23.  
  24. (function() {
  25. "use strict";
  26. try {
  27. if (typeof document != "undefined") {
  28. var elementStyle = document.createElement("style");
  29. elementStyle.appendChild(document.createTextNode("#exportContainer {\n position: fixed;\n bottom: 20px;\n left: 10px;\n z-index: 99999;\n}\n\n.line-container[data-v-69f05dbf] {\n display: flex;\n margin: 3px 0;\n min-height: 25px;\n}\n.line-container select[data-v-69f05dbf] {\n min-height: 25px;\n margin-right: 4px;\n min-width: 100px;\n}\n.line-container input[data-v-69f05dbf] {\n margin-right: 4px;\n}"));
  30. document.head.appendChild(elementStyle);
  31. }
  32. } catch (e) {
  33. console.error("vite-plugin-css-injected-by-js", e);
  34. }
  35. })();
  36. (function polyfill() {
  37. const relList = document.createElement("link").relList;
  38. if (relList && relList.supports && relList.supports("modulepreload")) {
  39. return;
  40. }
  41. for (const link of document.querySelectorAll('link[rel="modulepreload"]')) {
  42. processPreload(link);
  43. }
  44. new MutationObserver((mutations) => {
  45. for (const mutation of mutations) {
  46. if (mutation.type !== "childList") {
  47. continue;
  48. }
  49. for (const node of mutation.addedNodes) {
  50. if (node.tagName === "LINK" && node.rel === "modulepreload")
  51. processPreload(node);
  52. }
  53. }
  54. }).observe(document, { childList: true, subtree: true });
  55. function getFetchOpts(link) {
  56. const fetchOpts = {};
  57. if (link.integrity)
  58. fetchOpts.integrity = link.integrity;
  59. if (link.referrerPolicy)
  60. fetchOpts.referrerPolicy = link.referrerPolicy;
  61. if (link.crossOrigin === "use-credentials")
  62. fetchOpts.credentials = "include";
  63. else if (link.crossOrigin === "anonymous")
  64. fetchOpts.credentials = "omit";
  65. else
  66. fetchOpts.credentials = "same-origin";
  67. return fetchOpts;
  68. }
  69. function processPreload(link) {
  70. if (link.ep)
  71. return;
  72. link.ep = true;
  73. const fetchOpts = getFetchOpts(link);
  74. fetch(link.href, fetchOpts);
  75. }
  76. })();
  77. const main = "";
  78. const Panel_vue_vue_type_style_index_0_scoped_69f05dbf_lang = "";
  79. const _export_sfc = (sfc, props) => {
  80. const target = sfc.__vccOpts || sfc;
  81. for (const [key, val] of props) {
  82. target[key] = val;
  83. }
  84. return target;
  85. };
  86. const _withScopeId = (n) => (pushScopeId("data-v-69f05dbf"), n = n(), popScopeId(), n);
  87. const _hoisted_1 = { class: "line-container" };
  88. const _hoisted_2 = ["disabled"];
  89. const _hoisted_3 = { class: "line-container" };
  90. const _hoisted_4 = { key: 0 };
  91. const _hoisted_5 = ["value"];
  92. const _hoisted_6 = ["value"];
  93. const _hoisted_7 = ["disabled"];
  94. const _hoisted_8 = { key: 1 };
  95. const _hoisted_9 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ createElementVNode("select", { disabled: "" }, null, -1));
  96. const _hoisted_10 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ createElementVNode("select", { disabled: "" }, null, -1));
  97. const _hoisted_11 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ createElementVNode("button", { disabled: "" }, "导出", -1));
  98. const _hoisted_12 = [
  99. _hoisted_9,
  100. _hoisted_10,
  101. _hoisted_11
  102. ];
  103. const _hoisted_13 = { class: "line-container" };
  104. const _hoisted_14 = ["value"];
  105. const _hoisted_15 = ["disabled"];
  106. const realm = "pc";
  107. const _sfc_main$1 = {
  108. __name: "Panel",
  109. setup(__props) {
  110. const accountName = ref("");
  111. const characters = ref([]);
  112. const leagues = ref([]);
  113. const leagueMap = ref(/* @__PURE__ */ new Map());
  114. const currLeague = ref("");
  115. const currCharacters = ref([]);
  116. const currCharacter = ref("");
  117. const buildingCode = ref("");
  118. const state = reactive({
  119. accountName,
  120. realm,
  121. characters,
  122. leagues,
  123. leagueMap,
  124. currLeague,
  125. currCharacters,
  126. currCharacter,
  127. buildingCode
  128. });
  129. const getCharactersReady = computed(() => {
  130. return Boolean(state.accountName);
  131. });
  132. const selectReady = computed(() => {
  133. return state.characters.length > 0;
  134. });
  135. const exportReady = computed(() => {
  136. return state.characters.length > 0 && Boolean(state.currCharacter);
  137. });
  138. function selectNewLeague() {
  139. state.currCharacters = state.leagueMap.get(state.currLeague);
  140. state.currCharacter = state.currCharacters[0].name;
  141. }
  142. function getCharacters() {
  143. const url = "/character-window/get-characters";
  144. const realm2 = state.realm;
  145. const accountName2 = state.accountName;
  146. let form = new URLSearchParams();
  147. form.append("accountName", accountName2);
  148. form.append("realm", realm2);
  149. state.currLeague = "";
  150. state.currCharacter = "";
  151. axios.post(url, form).then((res) => {
  152. const characters2 = res.data;
  153. state.characters = characters2;
  154. let leagueMap2 = /* @__PURE__ */ new Map();
  155. for (const character of characters2) {
  156. const leagueName = character.league;
  157. let list = leagueMap2.get(leagueName);
  158. if (list === void 0) {
  159. list = [];
  160. leagueMap2.set(leagueName, list);
  161. }
  162. list.push(character);
  163. }
  164. state.leagueMap = leagueMap2;
  165. const leagues2 = Array.from(leagueMap2.keys());
  166. state.leagues = leagues2;
  167. if (leagues2.length > 0) {
  168. state.currLeague = leagues2[0];
  169. selectNewLeague();
  170. }
  171. }).catch((err) => {
  172. state.leagues = [];
  173. state.characters = [];
  174. console.log(err);
  175. alert(err);
  176. });
  177. }
  178. async function getItems() {
  179. const url = "/character-window/get-items";
  180. const realm2 = state.realm;
  181. const accountName2 = state.accountName;
  182. const character = state.currCharacter;
  183. let form = new URLSearchParams();
  184. form.append("accountName", accountName2);
  185. form.append("realm", realm2);
  186. form.append("character", character);
  187. const res = await axios.post(url, form);
  188. return res.data;
  189. }
  190. async function getPassiveSkills() {
  191. const url = "/character-window/get-passive-skills";
  192. const realm2 = state.realm;
  193. const accountName2 = state.accountName;
  194. const character = state.currCharacter;
  195. let params = new URLSearchParams();
  196. params.append("accountName", accountName2);
  197. params.append("realm", realm2);
  198. params.append("character", character);
  199. const res = await axios.get(url, { params });
  200. return res.data;
  201. }
  202. const factory = CnPoeTranslator.newBasicTranslatorFactory(CnPoeExportDb);
  203. const jsonTranslator = factory.getJsonTranslator();
  204. async function exportBuilding() {
  205. let items;
  206. let passiveSkills;
  207. try {
  208. items = await getItems();
  209. passiveSkills = await getPassiveSkills();
  210. } catch (err) {
  211. alert(`加载角色数据失败: ${err}`);
  212. }
  213. jsonTranslator.translateItems(items);
  214. jsonTranslator.translatePassiveSkills(passiveSkills);
  215. const building = BuildingCreater.transform(items, passiveSkills);
  216. const compressed = window.pako.deflate(building.toString());
  217. const code = btoa(String.fromCharCode.apply(null, compressed)).replaceAll("+", "-").replaceAll("/", "_");
  218. state.buildingCode = code;
  219. }
  220. function copyBuildingCode() {
  221. navigator.clipboard.writeText(state.buildingCode);
  222. }
  223. function getInitialAccountName() {
  224. let accountName2 = getAccountNameFromProfileLink(window.location.href);
  225. if (accountName2 !== null) {
  226. return accountName2;
  227. }
  228. const profileLinkNode = document.querySelector("#statusBar .profile-link a");
  229. if (profileLinkNode !== null) {
  230. accountName2 = getAccountNameFromProfileLink(profileLinkNode.href);
  231. if (accountName2 !== null) {
  232. return accountName2;
  233. }
  234. }
  235. return "";
  236. }
  237. const pattern = new RegExp("/account/view-profile/([^/?]+)");
  238. function getAccountNameFromProfileLink(link) {
  239. const match = pattern.exec(link);
  240. if (match) {
  241. return decodeURI(match[1]);
  242. }
  243. return null;
  244. }
  245. onMounted(() => {
  246. state.accountName = getInitialAccountName();
  247. });
  248. return (_ctx, _cache) => {
  249. return openBlock(), createElementBlock(Fragment, null, [
  250. createElementVNode("span", _hoisted_1, [
  251. withDirectives(createElementVNode("input", {
  252. type: "text",
  253. placeholder: "输入论坛账户名",
  254. maxlength: "50",
  255. "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => state.accountName = $event)
  256. }, null, 512), [
  257. [
  258. vModelText,
  259. state.accountName,
  260. void 0,
  261. { trim: true }
  262. ]
  263. ]),
  264. createElementVNode("button", {
  265. onClick: getCharacters,
  266. disabled: !getCharactersReady.value
  267. }, "开始", 8, _hoisted_2)
  268. ]),
  269. createElementVNode("span", _hoisted_3, [
  270. selectReady.value ? (openBlock(), createElementBlock("div", _hoisted_4, [
  271. selectReady.value ? withDirectives((openBlock(), createElementBlock("select", {
  272. key: 0,
  273. "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => state.currLeague = $event),
  274. onChange: selectNewLeague
  275. }, [
  276. (openBlock(true), createElementBlock(Fragment, null, renderList(leagues.value, (item) => {
  277. return openBlock(), createElementBlock("option", {
  278. key: item,
  279. value: item
  280. }, toDisplayString(item), 9, _hoisted_5);
  281. }), 128))
  282. ], 544)), [
  283. [vModelSelect, state.currLeague]
  284. ]) : createCommentVNode("", true),
  285. selectReady.value ? withDirectives((openBlock(), createElementBlock("select", {
  286. key: 1,
  287. "onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => state.currCharacter = $event)
  288. }, [
  289. (openBlock(true), createElementBlock(Fragment, null, renderList(state.currCharacters, (item) => {
  290. return openBlock(), createElementBlock("option", {
  291. key: item.name,
  292. value: item.name
  293. }, toDisplayString(item.name) + "," + toDisplayString(item.level) + "," + toDisplayString(item.class), 9, _hoisted_6);
  294. }), 128))
  295. ], 512)), [
  296. [vModelSelect, state.currCharacter]
  297. ]) : createCommentVNode("", true),
  298. selectReady.value ? (openBlock(), createElementBlock("button", {
  299. key: 2,
  300. disabled: !exportReady.value,
  301. onClick: exportBuilding
  302. }, "导出", 8, _hoisted_7)) : createCommentVNode("", true)
  303. ])) : (openBlock(), createElementBlock("div", _hoisted_8, _hoisted_12))
  304. ]),
  305. createElementVNode("span", _hoisted_13, [
  306. createElementVNode("input", {
  307. disabled: "",
  308. maxlength: "50",
  309. value: state.buildingCode
  310. }, null, 8, _hoisted_14),
  311. createElementVNode("button", {
  312. onClick: copyBuildingCode,
  313. disabled: !state.buildingCode
  314. }, "复制", 8, _hoisted_15)
  315. ])
  316. ], 64);
  317. };
  318. }
  319. };
  320. const Panel = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["__scopeId", "data-v-69f05dbf"]]);
  321. const _sfc_main = {
  322. __name: "Exporter",
  323. setup(__props) {
  324. return (_ctx, _cache) => {
  325. return openBlock(), createBlock(Panel);
  326. };
  327. }
  328. };
  329. const container = document.createElement("div");
  330. container.id = "exportContainer";
  331. document.body.appendChild(container);
  332. createApp(_sfc_main).mount("#exportContainer");
  333.  
  334. })();