Travian Report Archive

Automatically saves reports locally on browser when you open them and exports as CSV for analysis in excel etc. currently supports raids, and scouting(resources and troops)

  1. // ==UserScript==
  2. // @name Travian Report Archive
  3. // @namespace https://gf.qytechs.cn/en/scripts/367709-travian-report-archive
  4. // @version 1.0.2
  5. // @description Automatically saves reports locally on browser when you open them and exports as CSV for analysis in excel etc. currently supports raids, and scouting(resources and troops)
  6. // @author Bruno Robert
  7. // @liscence GPL(v3)
  8. // @include *://*.travian.*
  9. // @include *://*/*.travian.*
  10. // @exclude *://*.travian*.*/hilfe.php*
  11. // @exclude *://*.travian*.*/log*.php*
  12. // @exclude *://*.travian*.*/index.php*
  13. // @exclude *://*.travian*.*/anleitung.php*
  14. // @exclude *://*.travian*.*/impressum.php*
  15. // @exclude *://*.travian*.*/anmelden.php*
  16. // @exclude *://*.travian*.*/gutscheine.php*
  17. // @exclude *://*.travian*.*/spielregeln.php*
  18. // @exclude *://*.travian*.*/links.php*
  19. // @exclude *://*.travian*.*/geschichte.php*
  20. // @exclude *://*.travian*.*/tutorial.php*
  21. // @exclude *://*.travian*.*/manual.php*
  22. // @exclude *://*.travian*.*/ajax.php*
  23. // @exclude *://*.travian*.*/ad/*
  24. // @exclude *://*.travian*.*/chat/*
  25. // @exclude *://forum.travian*.*
  26. // @exclude *://board.travian*.*
  27. // @exclude *://shop.travian*.*
  28. // @exclude *://*.travian*.*/activate.php*
  29. // @exclude *://*.travian*.*/support.php*
  30. // @exclude *://help.travian*.*
  31. // @exclude *://*.answers.travian*.*
  32. // @exclude *.css
  33. // @exclude *.js
  34. // ==/UserScript==
  35.  
  36. // ----- Getters and Setters -----
  37.  
  38. /**
  39. *
  40. * get the parsed value of localStorage.reportList
  41. *
  42. * @returns {any} parsed value of localStorage.reportList
  43. */
  44. function getReportList() {
  45. return JSON.parse(localStorage.reportList);
  46. }
  47.  
  48. /**
  49. *
  50. * Stringify the input and store it in localStorage.reportList
  51. *
  52. * @param reportList value parse and set
  53. */
  54. function setReportList(reportList) {
  55. localStorage.reportList = JSON.stringify(reportList);
  56. }
  57.  
  58. /**
  59. * returns true if localStorage.reportList exists
  60. * @returns {boolean} true if localStorage.reportList exists
  61. */
  62. function doesReportListExist() {
  63. if(localStorage.reportList){
  64. return true;
  65. }
  66. return false;
  67. }
  68.  
  69. // ----- Global variables -----
  70. let lang = "fr";//current language used
  71. const supportedLanguages = ["fr"];//list of languages that are configured
  72. const reportSubjects = {//the strings to look for in report subjects to determine the type of report
  73. "fr": [" pille ", " espionne ", " espionne ", " attaque ", " defend ", " explore ", " livre ", " Oasis "]
  74. };
  75.  
  76. // ----- Tools -----
  77.  
  78. /**
  79. *
  80. * Will add "element" to "list" if "element" isn't already present in "list"
  81. *
  82. * @param {[]}list the list to add to
  83. * @param element element to add to the list
  84. * @returns {[]}
  85. */
  86. function addToListWithoutDup(list, element) {
  87. let inList = false;
  88. for(let i = 0; i < list.length; i++) {
  89. if(list[i] === element) {
  90. inList = true;
  91. }
  92. }
  93. if(!inList) {
  94. list = list.concat([element]);
  95. }
  96. return list;
  97. }
  98.  
  99. // ----- Crawlers -----
  100.  
  101. /**
  102. *
  103. * Gathers the data from a report page through HTML. It returns this data in the form of an object (dict)
  104. *
  105. * @returns {{resourcesStolen: *[], resourcesInBase: *[], atk: number[], atk_loss: number[], def: number[], def_loss: number[], def_assist: *[], def_assist_loss: *[], reportType: number, atkClass: number, dateTime: number[], reportId: string | *, attacker: {name: string, profile_url: string, village_name: string, village_url: string}, defender: {name: string, profile_url: string, village_name: string, village_url: string}, defClass: number}}
  106. */
  107. function gatherResourceData() {
  108. //gets all the info from a report page and returns a dict containing the data
  109. let resourcesStolen = [-1, -1, -1, -1, -1];//wood, clay, iron, wheat, maxCarry; This contains the amount of ressources stolen; Instanciazed because one of the two will not be used
  110. let resourcesInBase = [-1, -1, -1, -1, -1];//wood, clay, iron, wheat, hidout size; This contains the amout of ressources stashed in base and hidout size used in scout reports
  111. let atk = [11];//attackers troops
  112. let atk_loss = [11];//attacker losses
  113. let def = [11];//defending troops (also stationned troop if report is a spy)
  114. let def_loss = [11];//defenders losses
  115. let def_assist = [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1];//defending assistance; These gusy are instanciated because sometimes there is no assist
  116. let def_assist_loss = [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1];//assistences losses
  117. let reportType = 0;// 0 = not set; 1 = pillage; 2 = scout; 3 = other (hero quest);
  118. let atkClass = 0;//0 = not set; 1 = teutons; 2 = gaul; 3 = roman
  119. let defClass = 0;
  120. let dateTime;
  121. let dateTimeTemp = [];//d, m, y, h, m, s (array)
  122. let attacker = {
  123. name : "",
  124. profile_url: "",
  125. village_name: "",
  126. village_url: ""
  127. };
  128. let defender = {
  129. name : "",
  130. profile_url: "",
  131. village_name: "",
  132. village_url: ""
  133. };
  134. let reportId;
  135.  
  136. //-----Determining what kind of report it is -----
  137. let subject = document.getElementById('subject');
  138. //let textHeader = subject.getElementsByClassName('header text');
  139. let subjectText = subject.innerHTML;//the text written in the subject container
  140. if(subjectText.includes(" espionne ")) {
  141. reportType = 2;//if the report type is spying
  142. } else if(subjectText.includes(" pille ")) {
  143. reportType = 1;//if the report type is pillage
  144. } else {
  145. reportType = 3;
  146. }
  147.  
  148. //-----Getting the Date and Time -----
  149. let dateTimeBlock = document.getElementByClass('time');
  150. //textHeader = dateTimeBlock.getElementsByClassName('header text');
  151. let dateTimeText = dateTimeBlock.innerText;
  152. dateTimeTemp[0] = Number(dateTimeText.split('.')[0]);
  153. dateTimeTemp[1] = Number(dateTimeText.split('.')[1]);
  154. dateTimeTemp[2] = Number(dateTimeText.split('.')[2].split(',')[0]);
  155. dateTimeTemp[3] = Number(dateTimeText.split('.')[2].split(',')[1].split(':')[0].trim());
  156. dateTimeTemp[4] = Number(dateTimeText.split('.')[2].split(',')[1].split(':')[1]);
  157. dateTimeTemp[5] = Number(dateTimeText.split('.')[2].split(',')[1].split(':')[2]);
  158.  
  159. dateTime = toDateObj(dateTimeTemp);
  160.  
  161. //-----Getting the players and villages -----
  162.  
  163. let headlines = document.getElementsByClassName('troopHeadline');//0 = Attacker; 1 = Defender
  164. attacker.name = headlines[0].getElementsByClassName('player')[0].innerText;
  165. attacker.profile_url = headlines[0].getElementsByClassName('player')[0].href;
  166. attacker.village_name = headlines[0].getElementsByClassName('village')[0].innerText;
  167. attacker.village_url = headlines[0].getElementsByClassName('village')[0].href;
  168.  
  169. defender.name = headlines[1].getElementsByClassName('player')[0].innerText;
  170. defender.profile_url = headlines[1].getElementsByClassName('player')[0].href;
  171. defender.village_name = headlines[1].getElementsByClassName('village')[0].innerText;
  172. defender.village_url = headlines[1].getElementsByClassName('village')[0].href;
  173.  
  174. //-----Getting the report id -----
  175. reportId = window.location.href.split('id=')[1].split('&')[0];
  176.  
  177. //contains unit info
  178. let unitBlocks = document.getElementsByClassName('units');//0: head; 1 = atk troops; 2 = atk losses; 3 = head; 4 = def troops; 5 = def losses; 6 = head; 7 = def_assist; 8 = def_assist_loss;
  179. //contains resource info
  180. let goodsBlocks = document.getElementsByClassName('goods');//
  181.  
  182. //attempting to detect your class
  183. if(unitBlocks[0].getElementsByTagName('td')[0].firstChild.className == "unit u11") {
  184. console.log("Teuton Detected");
  185. atkClass = 1;
  186. } else if(unitBlocks[0].getElementsByTagName('td')[0].firstChild.className == "unit u21") {
  187. console.log("Gauls Detected");
  188. atkClass = 2;
  189. } else {
  190. console.log("Romans Detected");
  191. atkClass = 3;
  192. }
  193.  
  194. //attempting to detect defender class
  195. if(unitBlocks[3].getElementsByTagName('td')[0].firstChild.className == "unit u11") {
  196. console.log("Defender Teuton Detected");
  197. defClass = 1;
  198. } else if(unitBlocks[3].getElementsByTagName('td')[0].firstChild.className == "unit u21") {
  199. console.log("Defender Gauls Detected");
  200. defClass = 2;
  201. } else {
  202. console.log("Defender Romans Detected");
  203. defClass = 3;
  204. }
  205.  
  206. console.log("gathering troop data");
  207. //getting the data on number of troops
  208. for(let i = 0; i < 11; i++) {
  209.  
  210. atk[i] = unitBlocks[1].getElementsByTagName('td')[i].innerText;
  211. atk_loss[i] = unitBlocks[2].getElementsByTagName('td')[i].innerText;
  212. def[i] = unitBlocks[4].getElementsByTagName('td')[i].innerText;
  213. def_loss[i] = unitBlocks[5].getElementsByTagName('td')[i].innerText;
  214.  
  215. }
  216.  
  217. //Checking if there is an assistance
  218. if(unitBlocks[7] !== undefined && unitBlocks[8] !== undefined && unitBlocks[9] !== undefined) {
  219. for(let i = 0; i < 11; i++) {
  220. def_assist[i] = unitBlocks[7].getElementsByTagName('td')[i].innerText;
  221. def_assist_loss[i] = unitBlocks[8].getElementsByTagName('td')[i].innerText;
  222. }
  223. }
  224.  
  225. if(reportType === 1) {//pillage
  226. console.log('getting data on ressources');
  227. //getting data on ressources
  228. for(let i = 0; i < 4; i++) {
  229. resourcesStolen[i] = goodsBlocks[0].getElementsByClassName('rArea')[i].innerText;
  230. }
  231. let maxCarry = goodsBlocks[0].getElementsByClassName('carry')[0].innerText;
  232. resourcesStolen[4] = maxCarry.split('/')[1];//maximum carry power
  233.  
  234. } else if(reportType === 2){//scouting
  235. console.log('getting data on ressources');
  236. //getting data on ressources
  237. for(let i = 0; i < 4; i++) {
  238. resourcesInBase[i] = goodsBlocks[0].getElementsByClassName('rArea')[i].innerText;
  239. }
  240. resourcesInBase[4] = goodsBlocks[1].getElementsByClassName('rArea')[0].innerText;//maximum carry power
  241. } else { //hero quest or any other
  242. console.log('report type is invalid');
  243. }
  244.  
  245. //----- Creating the data object -----
  246. let data = {
  247. "resourcesStolen": resourcesStolen,
  248. "resourcesInBase": resourcesInBase,
  249. "atk": atk,
  250. "atk_loss": atk_loss,
  251. "def": def,
  252. "def_loss": def_loss,
  253. "def_assist": def_assist,
  254. "def_assist_loss": def_assist_loss,
  255. "reportType": reportType,
  256. "atkClass": atkClass,
  257. "dateTime": dateTime,
  258. "reportId": reportId,
  259. "attacker": attacker,// name, profile_url, village_name, village_url
  260. "defender": defender,// name, profile_url, village_name, village_url
  261. "defClass": defClass
  262. };
  263. console.log('gatherResourceData() is complete');
  264. return data;
  265. }
  266.  
  267. /**
  268. * Determines the report type by reading the report
  269. * 0 = raid, 1 = scout resources + troops, 2 = scout troops + defenses, 3 = attack, 4 = defense, 5 = adventure, 6 = trade, 7 = unknown/error, -1 = default value (nothing)
  270. * @returns {number} the report type
  271. */
  272. function getReportType() {
  273. //-----Determining what kind of report it is -----
  274. let reportType = -1;//0 = raid, 1 = scout resources + troops, 2 = scout troops + defenses, 3 = attack, 4 = defense, 5 = adventure, 6 = trade, 7 = unknown/error, -1 = default value (nothing)
  275. let subject = document.getElementsByClassName('subject');
  276. //let textHeader = subject.getElementsByClassName('header text');
  277. let subjectText = subject[0].innerHTML;//the text written in the subject container
  278. if(subjectText.includes(reportSubjects[lang][0])) {//raid
  279. reportType = 0;
  280. } else if(subjectText.includes(reportSubjects[lang][1])) {//scout ...
  281. let infos = document.getElementsByClassName('infos');
  282. if(!infos.length){
  283. reportType = 1;//scout resources + troops
  284. } else {
  285. reportType = 2;//scout troops + defenses
  286.  
  287. }
  288. } else if(subjectText.includes(reportSubjects[lang][3])){ //attack
  289. reportType = 3;
  290. } else if(subjectText.includes(reportSubjects[lang][4])) {//defense
  291. reportType = 4;
  292. } else if(subjectText.includes(reportSubjects[lang][5])) {//adventure
  293. reportType = 5;
  294. } else if(subjectText.includes(reportSubjects[lang][6])) {//trade
  295. reportType = 6;
  296. } else {//unknown/error/trades
  297. reportType = 7;
  298. }
  299.  
  300. return reportType;
  301. }
  302.  
  303. /**
  304. * Gets the date of the report
  305. * @returns {Date} date of the report
  306. */
  307. function getReportDateTime() {
  308. //-----Getting the Date and Time -----
  309. let dateTimeTemp = [];
  310. let dateTimeBlock = document.getElementsByClassName('time')[0];
  311. //let textHeader = dateTimeBlock.getElementsByClassName('header text');
  312. let dateTimeText = dateTimeBlock.innerText;
  313. dateTimeTemp[0] = Number(dateTimeText.split('.')[0]);
  314. dateTimeTemp[1] = Number(dateTimeText.split('.')[1]);
  315. dateTimeTemp[2] = Number(dateTimeText.split('.')[2].split(',')[0]);
  316. dateTimeTemp[3] = Number(dateTimeText.split('.')[2].split(',')[1].split(':')[0].trim());
  317. dateTimeTemp[4] = Number(dateTimeText.split('.')[2].split(',')[1].split(':')[1]);
  318. dateTimeTemp[5] = Number(dateTimeText.split('.')[2].split(',')[1].split(':')[2]);
  319.  
  320. let dateTime = new Date((2000 + dateTimeTemp[2]), dateTimeTemp[1], dateTimeTemp[0], dateTimeTemp[3], dateTimeTemp[4], dateTimeTemp[5], 0);
  321. return dateTime;
  322. }
  323.  
  324. /**
  325. * Gets info on attacker:
  326. * Name, profile URL, villageName, village URL
  327. * @returns {{}}
  328. */
  329. function getAttackerInfo() {
  330. let attacker = {};
  331. let headlines = document.getElementsByClassName('troopHeadline');//0 = Attacker; 1 = Defender
  332. attacker.name = headlines[0].getElementsByClassName('player')[0].innerText;
  333. attacker.profile_url = headlines[0].getElementsByClassName('player')[0].href;
  334. attacker.village_name = headlines[0].getElementsByClassName('village')[0].innerText;
  335. attacker.village_url = headlines[0].getElementsByClassName('village')[0].href;
  336.  
  337. return attacker;
  338. }
  339.  
  340. /**
  341. * Gets info on the defender
  342. * Name, profile URL, villageName, village URL
  343. * @returns {{}}
  344. */
  345. function getDefenderInfo() {
  346. let defender = {};
  347. let headlines = document.getElementsByClassName('troopHeadline');//0 = Attacker; 1 = Defender
  348. defender.name = headlines[1].getElementsByClassName('player')[0].innerText;
  349. defender.profile_url = headlines[1].getElementsByClassName('player')[0].href;
  350. defender.village_name = headlines[1].getElementsByClassName('village')[0].innerText;
  351. defender.village_url = headlines[1].getElementsByClassName('village')[0].href;
  352.  
  353. return defender;
  354. }
  355.  
  356. /**
  357. * Generates and returns the reportID
  358. * @returns {string} reportId
  359. */
  360. function getReportId() {
  361. return window.location.href.split('id=')[1].split('&')[0];
  362. }
  363.  
  364. /**
  365. * Gets and returns the class of the attacker
  366. * @returns {number} class of the attacker
  367. */
  368. function getAttackerClass(verbose = false) {
  369. //contains unit info
  370. let unitBlocks = document.getElementsByClassName('units');//0: head; 1 = atk troops; 2 = atk losses; 3 = head; 4 = def troops; 5 = def losses; 6 = head; 7 = def_assist; 8 = def_assist_loss;
  371. let atkClass;
  372. //attempting to detect class
  373. if(unitBlocks[0].getElementsByTagName('td')[0].firstChild.className === "unit u11") {
  374. if (verbose) {
  375. console.log("Teuton Detected");
  376. }
  377. atkClass = 1;
  378. } else if(unitBlocks[0].getElementsByTagName('td')[0].firstChild.className === "unit u21") {
  379. if (verbose) {
  380. console.log("Gauls Detected");
  381. }
  382. atkClass = 2;
  383. } else {
  384. if (verbose) {
  385. console.log("Romans Detected");
  386. }
  387. atkClass = 3;
  388. }
  389.  
  390. return atkClass;
  391. }
  392.  
  393. /**
  394. * Gets and returns the class of the defender
  395. * @returns {number} class of the defender
  396. */
  397. function getDefenderClass(verbose = false) {
  398. //TODO: add nature
  399. //contains unit info
  400. let unitBlocks = document.getElementsByClassName('units');//0: head; 1 = atk troops; 2 = atk losses; 3 = head; 4 = def troops; 5 = def losses; 6 = head; 7 = def_assist; 8 = def_assist_loss;
  401. let defClass;
  402. //attempting to detect defender class
  403. if(unitBlocks[3].getElementsByTagName('td')[0].firstChild.className === "unit u11") {
  404. if (verbose) {
  405. console.log("Defender Teuton Detected");
  406. }
  407. defClass = 1;
  408. } else if(unitBlocks[3].getElementsByTagName('td')[0].firstChild.className === "unit u21") {
  409. if (verbose) {
  410. console.log("Defender Gauls Detected");
  411. }
  412. defClass = 2;
  413. } else {
  414. if (verbose) {
  415. console.log("Defender Romans Detected");
  416. }
  417. defClass = 3;
  418. }
  419.  
  420. return defClass;
  421. }
  422.  
  423. /**
  424. * Gets all the troops involved in the report (atk, def and assist)
  425. * @returns {{atk: Array, atk_loss: Array, def: Array, def_loss: Array, assist: Array, assist_loss: Array}}
  426. */
  427. function getTroops() {
  428. //TODO: check weather or not this works with multiple assists
  429. //contains unit info
  430. let unitBlocks = document.getElementsByClassName('units');//0: head; 1 = atk troops; 2 = atk losses; 3 = head; 4 = def troops; 5 = def losses; 6 = head; 7 = def_assist; 8 = def_assist_loss;
  431. let maxTroopClasses = 11;
  432.  
  433. //oasis only have 10 types of animals so we check if this is an oasis
  434. let subject = document.getElementsByClassName('subject');
  435. //let textHeader = subject.getElementsByClassName('header text');
  436. let subjectText = subject[0].innerHTML;//the text written in the subject container
  437. if(subjectText.includes(reportSubjects[lang][7])){
  438. maxTroopClasses = 10;
  439. }
  440. let troops = {
  441. atk: [],
  442. atk_loss: [],
  443. def: [],
  444. def_loss: [],
  445. assist: [],
  446. assist_loss: []
  447. };
  448. //getting the data on number of troops
  449. for(let i = 0; i < maxTroopClasses; i++) {
  450.  
  451. troops.atk[i] = unitBlocks[1].getElementsByTagName('td')[i].innerText;
  452. troops.atk_loss[i] = unitBlocks[2].getElementsByTagName('td')[i].innerText;
  453. troops.def[i] = unitBlocks[4].getElementsByTagName('td')[i].innerText;
  454. troops.def_loss[i] = unitBlocks[5].getElementsByTagName('td')[i].innerText;
  455.  
  456. }
  457. //Checking if there is an assistance
  458. if(unitBlocks[7] !== undefined && unitBlocks[8] !== undefined && unitBlocks[9] !== undefined) {
  459. for(let i = 0; i < 11; i++) {
  460. troops.assist[i] = unitBlocks[7].getElementsByTagName('td')[i].innerText;
  461. troops.assist_loss[i] = unitBlocks[8].getElementsByTagName('td')[i].innerText;
  462. }
  463. }
  464. return troops
  465. }
  466.  
  467. /**
  468. * Gets the quantity of raided resources
  469. * @returns {number[]} raided resources
  470. */
  471. function getRaidedResources() {
  472. //contains resource info
  473. let goodsBlocks = document.getElementsByClassName('goods');//
  474. let resourcesStolen = [0, 0, 0, 0, 0];//W, C, I, Crop, maxCarry
  475. //getting data on resources
  476. for(let i = 0; i < 4; i++) {
  477. resourcesStolen[i] = Number(goodsBlocks[0].getElementsByClassName('value')[i].innerText);
  478. }
  479. let maxCarry = goodsBlocks[0].getElementsByClassName('carry')[0].innerText;
  480. resourcesStolen[4] = Number(maxCarry.split('/')[1]);//maximum carry power
  481. return resourcesStolen;
  482. }
  483.  
  484. /**
  485. * Gets and returns the quantity of resources in scouted village
  486. * @returns {number[]}
  487. */
  488. function getScoutedResources() {
  489. //contains resource info
  490. let goodsBlocks = document.getElementsByClassName('goods');//
  491. let resourcesInBase = [0, 0, 0, 0, 0];
  492. //getting data on resources
  493. for(let i = 0; i < 4; i++) {
  494. resourcesInBase[i] = Number(goodsBlocks[0].getElementsByClassName('rArea')[i].innerText);
  495. }
  496. resourcesInBase[4] = Number(goodsBlocks[1].getElementsByClassName('rArea')[0].innerText);//cranny
  497. return resourcesInBase;
  498. }
  499.  
  500. /**
  501. * Analyses the report and returns the data from the report page ("*berichte.php*")
  502. * @returns {{reportId: string, reportType: number, reportDate: Date, attackerClass: number, defenderClass: number, attackerInfo: {}, defenderInfo: {}, troops: {atk: Array, atk_loss: Array, def: Array, def_loss: Array, assist: Array, assist_loss: Array}, raidedResources: *, scoutedResources: *}}
  503. */
  504. function readReport() {
  505. //TODO: check if these work for scout troop + defense, attack, defense, adventure and trade
  506. let reportType = getReportType();
  507. let reportDate = getReportDateTime();
  508. let attackerInfo = getAttackerInfo();
  509. let defenderInfo = getDefenderInfo();
  510. let reportId = getReportId();
  511. let attackerClass = getAttackerClass();
  512. let defenderClass = getDefenderClass();
  513. let troops = getTroops();
  514. let raidedResources = [0, 0, 0, 0, 0];
  515. let scoutedResources = [0, 0, 0, 0, 0];
  516. if(reportType === 0) {
  517. raidedResources = getRaidedResources();
  518. } else if(reportType === 1){
  519. scoutedResources = getScoutedResources();
  520. }
  521.  
  522. let data = {
  523. reportId: reportId,//
  524. reportType: reportType,//
  525. reportDate: reportDate,//
  526. attackerClass: attackerClass,//
  527. defenderClass: defenderClass,//
  528. attackerInfo: attackerInfo,//
  529. defenderInfo: defenderInfo,//
  530. troops: troops,
  531. raidedResources: raidedResources,
  532. scoutedResources: scoutedResources
  533. };
  534. return data;
  535. }
  536.  
  537. // ----- General functions -----
  538. /**
  539. * When given a report will check if it exists in db, if not it will add it.
  540. * @param report
  541. */
  542. function saveReport(report) {
  543. let localReportList;
  544.  
  545. //initialise the local copy of localStorage.ReportList
  546. if(doesReportListExist()){
  547. localReportList = getReportList();
  548. } else {
  549. localReportList = {};
  550. }
  551.  
  552. //check if the report id already exists
  553. if(localReportList[report.reportId]){
  554. console.log('report is already saved');
  555. //TODO: Write this on the report
  556. } else {
  557. localReportList[report.reportId] = report;
  558. console.log('report is now saved');
  559. //TODO: Write this on the report
  560. }
  561.  
  562.  
  563. setReportList(localReportList);
  564. }
  565.  
  566. /**
  567. * takes a array and returns is as a csv row (no newline)
  568. * @param arary
  569. * @param culumnDelimiter
  570. * @returns {*}
  571. */
  572. function arrayToCSV({array = null, columnDelimiter = ','} = {}) {
  573. if(array === null || !array.length){
  574. return null;
  575. }
  576. let out = "";
  577. out += array[0];
  578. for(let x = 1; x < array.length; x++){
  579. out += columnDelimiter + array[x];
  580. }
  581. return out;
  582. }
  583.  
  584. /**
  585. * Gets all the reports and turns them into a csv (string)
  586. * @returns {string} reports in CSV form
  587. */
  588. function toCSV() {
  589. let output = "";
  590. let rowDelimiter = '\n';
  591. const head = [
  592. "Report ID","Report Type","Date",
  593. "Raided Wood","Raided Clay","Raided Iron","Raided Cereal","Cary Capacity",
  594. "Scouted Wood","Scouted Clay","Scouted Iron","Scouted Cereal","Scouted Cranny",
  595.  
  596. "Attacker Name","Attacker Village Name","Attacker Class","Attacker Profile URL","Attacker Village URL",
  597. "Attacker troop 1","Attacker troop 2","Attacker troop 3","Attacker troop 4","Attacker troop 5","Attacker troop 6",
  598. "Attacker troop 7","Attacker troop 8","Attacker troop 9","Attacker troop 10","Attacker troop 11",
  599. "Attacker troop 1 Loss","Attacker troop 2 Loss","Attacker troop 3 Loss","Attacker troop 4 Loss",
  600. "Attacker troop 5 Loss","Attacker troop 6 Loss","Attacker troop 7 Loss","Attacker troop 8 Loss",
  601. "Attacker troop 9 Loss","Attacker troop 10 Loss","Attacker troop 11 Loss",
  602.  
  603. "Defender Name","Defender Village Name","Defender Class","Defender Profile URL","Defender Village URL",
  604. "Defender troop 1","Defender troop 2","Defender troop 3","Defender troop 4","Defender troop 5","Defender troop 6",
  605. "Defender troop 7","Defender troop 8","Defender troop 9","Defender troop 10","Defender troop 11",
  606. "Defender troop 1 Loss","Defender troop 2 Loss","Defender troop 3 Loss","Defender troop 4 Loss",
  607. "Defender troop 5 Loss","Defender troop 6 Loss","Defender troop 7 Loss","Defender troop 8 Loss",
  608. "Defender troop 9 Loss","Defender troop 10 Loss","Defender troop 11 Loss",
  609.  
  610. "Assistance Class",
  611. "Assistance troop 1","Assistance troop 2","Assistance troop 3","Assistance troop 4","Assistance troop 5",
  612. "Assistance troop 6","Assistance troop 7","Assistance troop 8","Assistance troop 9","Assistance troop 10",
  613. "Assistance troop 11",
  614. "Assistance troop 1 Loss","Assistance troop 2 Loss","Assistance troop 3 Loss","Assistance troop 4 Loss",
  615. "Assistance troop 5 Loss","Assistance troop 6 Loss","Assistance troop 7 Loss","Assistance troop 8 Loss",
  616. "Assistance troop 9 Loss","Assistance troop 10 Loss","Assistance troop 11 Loss"
  617. ];
  618.  
  619. output = arrayToCSV({array: head}) + rowDelimiter;
  620.  
  621. if(doesReportListExist()){
  622. let reportList = getReportList();
  623. for(report in reportList){
  624. let cr = reportList[report];//current report
  625. let row = [
  626. cr.reportId, cr.reportType, cr.reportDate,
  627. cr.raidedResources[0],cr.raidedResources[1],cr.raidedResources[2],cr.raidedResources[3], cr.raidedResources[4],
  628. cr.scoutedResources[0],cr.scoutedResources[1],cr.scoutedResources[2],cr.scoutedResources[3], cr.scoutedResources[4],
  629.  
  630. cr.attackerInfo.name,cr.attackerInfo.village_name,cr.attackerClass,cr.attackerInfo.profile_url,cr.attackerInfo.village_url,
  631. cr.troops.atk[0],cr.troops.atk[1],cr.troops.atk[2],cr.troops.atk[3],cr.troops.atk[4],cr.troops.atk[5],
  632. cr.troops.atk[6],cr.troops.atk[7],cr.troops.atk[8],cr.troops.atk[9],cr.troops.atk[10],
  633. cr.troops.atk_loss[0],cr.troops.atk_loss[1],cr.troops.atk_loss[2],cr.troops.atk_loss[3],cr.troops.atk_loss[4],cr.troops.atk_loss[5],
  634. cr.troops.atk_loss[6],cr.troops.atk_loss[7],cr.troops.atk_loss[8],cr.troops.atk_loss[9],cr.troops.atk_loss[10],
  635.  
  636. cr.defenderInfo.name,cr.defenderInfo.village_name,cr.defenderClass,cr.defenderInfo.profile_url,cr.defenderInfo.village_url,
  637. cr.troops.def[0],cr.troops.def[1],cr.troops.def[2],cr.troops.def[3],cr.troops.def[4],cr.troops.def[5],
  638. cr.troops.def[6],cr.troops.def[7],cr.troops.def[8],cr.troops.def[9],cr.troops.def[10],
  639. cr.troops.def_loss[0],cr.troops.def_loss[1],cr.troops.def_loss[2],cr.troops.def_loss[3],cr.troops.def_loss[4],cr.troops.def_loss[5],
  640. cr.troops.def_loss[6],cr.troops.def_loss[7],cr.troops.def_loss[8],cr.troops.def_loss[9],cr.troops.def_loss[10],
  641.  
  642. "Assistance Class",//TODO: fix this
  643.  
  644. cr.troops.assist[0],cr.troops.assist[1],cr.troops.assist[2],cr.troops.assist[3],cr.troops.assist[4],cr.troops.assist[5],
  645. cr.troops.assist[6],cr.troops.assist[7],cr.troops.assist[8],cr.troops.assist[9],cr.troops.assist[10],
  646. cr.troops.assist_loss[0],cr.troops.assist_loss[1],cr.troops.assist_loss[2],cr.troops.assist_loss[3],cr.troops.assist_loss[4],cr.troops.assist_loss[5],
  647. cr.troops.assist_loss[6],cr.troops.assist_loss[7],cr.troops.assist_loss[8],cr.troops.assist_loss[9],cr.troops.assist_loss[10]
  648. ];
  649. output += arrayToCSV({array: row}) + rowDelimiter;
  650. }
  651. } else {
  652. alert('There is nothing to export!');
  653. }
  654. return output;
  655. }
  656.  
  657. /**
  658. * Instantly starts the download of the csv
  659. * @param filename (optionnal)
  660. */
  661. function startDownload({filename = 'export.csv'} = {}) {
  662. let data, link;
  663. let csv = toCSV();
  664. if(csv === null) return;
  665. if (!csv.match(/^data:text\/csv/i)) {
  666. csv = 'data:text/csv;charset=utf-8,' + csv;
  667. }
  668. data = encodeURI(csv);
  669.  
  670. link = document.createElement('a');
  671. link.setAttribute('href', data);
  672. link.setAttribute('download', filename);
  673. link.click();
  674. }
  675.  
  676. /**
  677. * Creates a menu item in the top right menu of travian
  678. * @param name name of the button (becomes the button class if you want to use CSS)
  679. * @param imageSRC image link to be displayed on the button
  680. * @param onClickFunction function that will execute onclick
  681. */
  682. function createMenuItem({name = "link name", imageSRC = "", onClickFunction = function() {} } = {}) {
  683. let newMenuItem = document.createElement('li');
  684. let itemLink = document.createElement('a');
  685. let itemImg = document.createElement('img');
  686. newMenuItem.className = name;
  687. itemLink.onclick = onClickFunction;
  688. itemImg.alt = name;
  689. itemImg.src = imageSRC;
  690. itemImg.backgroundImage = "none";
  691.  
  692. itemLink.appendChild(itemImg);
  693. newMenuItem.appendChild(itemLink);
  694. let menuHook = document.getElementById('outOfGame');
  695. menuHook.insertBefore(newMenuItem, menuHook.lastChild);
  696. }
  697.  
  698. // ----- Main ------
  699.  
  700. if(window.location.href.includes('berichte.php?id=')) {
  701. if(getReportType() === 0 || getReportType() === 1){
  702. let data = readReport();
  703. saveReport(data);
  704. } else {
  705. console.log('report type not supported yet: ' + getReportType());
  706. }
  707. }
  708.  
  709. createMenuItem({name:"export", imageSRC:"https://image.flaticon.com/icons/svg/214/214289.svg", onClickFunction: function() {startDownload();}});

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址