Add auto training
This commit is contained in:
		| @@ -4,8 +4,14 @@ import { Task } from '../Storage/TaskQueue'; | ||||
| import { trimPrefix } from '../utils'; | ||||
| import { AbortTaskError } from '../Errors'; | ||||
|  | ||||
| const HARD = 0; | ||||
| const NORMAL = 3; | ||||
| const CONFIG = [ | ||||
|     { level: 0, health: 60 }, | ||||
|     { level: 1, health: 50 }, | ||||
|     { level: 2, health: 40 }, | ||||
|     { level: 3, health: 30 }, | ||||
|     { level: 4, health: 30 }, | ||||
|     { level: 5, health: 30 }, | ||||
| ]; | ||||
|  | ||||
| interface Adventure { | ||||
|     el: HTMLElement; | ||||
| @@ -28,19 +34,20 @@ export class SendOnAdventureAction extends ActionController { | ||||
|         console.log('HERO', hero); | ||||
|  | ||||
|         if (easiest && hero.health) { | ||||
|             if (easiest.level === NORMAL && hero.health >= 30) { | ||||
|                 return jQuery(easiest.el) | ||||
|                     .find('td.goTo .gotoAdventure') | ||||
|                     .trigger('click'); | ||||
|             } | ||||
|             if (easiest.level === HARD && hero.health >= 50) { | ||||
|                 return jQuery(easiest.el) | ||||
|             this.checkConfig(easiest, Number(hero.health)); | ||||
|         } | ||||
|  | ||||
|         throw new AbortTaskError(task.id, 'No suitable adventure'); | ||||
|     } | ||||
|  | ||||
|     private checkConfig(adventure: Adventure, health: number) { | ||||
|         for (let conf of CONFIG) { | ||||
|             if (adventure.level === conf.level && health >= conf.health) { | ||||
|                 return jQuery(adventure.el) | ||||
|                     .find('td.goTo .gotoAdventure') | ||||
|                     .trigger('click'); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         throw new AbortTaskError(task.id, 'No suitable adventure'); | ||||
|     } | ||||
|  | ||||
|     private findAdventures(): Array<Adventure> { | ||||
|   | ||||
							
								
								
									
										57
									
								
								src/Action/TrainTrooperAction.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								src/Action/TrainTrooperAction.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | ||||
| import { ActionController, registerAction } from './ActionController'; | ||||
| import { Args } from '../Common'; | ||||
| import { ActionError, TryLaterError } from '../Errors'; | ||||
| import { Task } from '../Storage/TaskQueue'; | ||||
| import { getNumber, toNumber } from '../utils'; | ||||
|  | ||||
| @registerAction | ||||
| export class TrainTrooperAction extends ActionController { | ||||
|     async run(args: Args, task: Task): Promise<any> { | ||||
|         const troopId = this.getTroopId(args, task); | ||||
|         const trainCount = this.getTrainCount(args, task); | ||||
|  | ||||
|         const block = jQuery(`#nonFavouriteTroops .innerTroopWrapper.troop${troopId}`); | ||||
|         if (block.length !== 1) { | ||||
|             throw new ActionError(task.id, `Troop block not found`); | ||||
|         } | ||||
|  | ||||
|         const countLink = block.find('.cta a'); | ||||
|         if (countLink.length !== 1) { | ||||
|             throw new ActionError(task.id, `Link with max count not found`); | ||||
|         } | ||||
|  | ||||
|         const maxCount = getNumber(countLink.text()); | ||||
|         if (maxCount < trainCount) { | ||||
|             throw new TryLaterError(task.id, 10 * 60, `Max count ${maxCount} less then need ${trainCount}`); | ||||
|         } | ||||
|  | ||||
|         const input = block.find(`input[name="t${troopId}"]`); | ||||
|         if (input.length !== 1) { | ||||
|             throw new ActionError(task.id, `Input element not found`); | ||||
|         } | ||||
|  | ||||
|         const trainButton = jQuery('.startTraining.green').first(); | ||||
|         if (trainButton.length !== 1) { | ||||
|             throw new ActionError(task.id, 'Train button not found'); | ||||
|         } | ||||
|  | ||||
|         input.val(trainCount); | ||||
|         trainButton.trigger('click'); | ||||
|     } | ||||
|  | ||||
|     private getTroopId(args: Args, task: Task): number { | ||||
|         const troopId = toNumber(args.troopId); | ||||
|         if (troopId === undefined) { | ||||
|             throw new ActionError(task.id, `Troop id must be a number, given "${args.troopId}"`); | ||||
|         } | ||||
|         return troopId; | ||||
|     } | ||||
|  | ||||
|     private getTrainCount(args: Args, task: Task): number { | ||||
|         const trainCount = toNumber(args.trainCount); | ||||
|         if (trainCount === undefined) { | ||||
|             throw new ActionError(task.id, `Train count must be a number, given "${args.trainCount}"`); | ||||
|         } | ||||
|         return trainCount; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										89
									
								
								src/Dashboard/BuildPage.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								src/Dashboard/BuildPage.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,89 @@ | ||||
| import { elClassId, split, uniqId } from '../utils'; | ||||
| import { Command } from '../Common'; | ||||
| import { UpgradeBuildingTask } from '../Task/UpgradeBuildingTask'; | ||||
| import { Scheduler } from '../Scheduler'; | ||||
| import { TrainTroopTask } from '../Task/TrainTroopTask'; | ||||
|  | ||||
| const QUARTERS_ID = 19; | ||||
|  | ||||
| export class BuildPage { | ||||
|     private scheduler: Scheduler; | ||||
|     private readonly buildId: number; | ||||
|     constructor(scheduler: Scheduler, buildId: number) { | ||||
|         this.scheduler = scheduler; | ||||
|         this.buildId = buildId; | ||||
|     } | ||||
|  | ||||
|     run() { | ||||
|         const buildTypeId = elClassId(jQuery('#build').attr('class') || '', 'gid'); | ||||
|         this.log('BUILD PAGE DETECTED', 'ID', this.buildId, 'TYPE', buildTypeId); | ||||
|         this.createUpgradeButton(); | ||||
|         if (buildTypeId === QUARTERS_ID) { | ||||
|             this.createTrainTroopButton(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private createUpgradeButton() { | ||||
|         const id = uniqId(); | ||||
|         jQuery('.upgradeButtonsContainer .section1').append( | ||||
|             `<div style="padding: 8px"><a id="${id}" href="#">В очередь</a></div>` | ||||
|         ); | ||||
|         jQuery(`#${id}`).on('click', evt => { | ||||
|             evt.preventDefault(); | ||||
|             this.onScheduleBuilding(this.buildId); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     private onScheduleBuilding(id: number) { | ||||
|         const queueItem = new Command(UpgradeBuildingTask.name, { | ||||
|             id, | ||||
|         }); | ||||
|         this.scheduler.scheduleTask(queueItem); | ||||
|         const n = new Notification(`Building ${id} scheduled`); | ||||
|         setTimeout(() => n && n.close(), 4000); | ||||
|     } | ||||
|  | ||||
|     private createTrainTroopButton() { | ||||
|         const troopBlocks = jQuery('#nonFavouriteTroops .action.troop:not(.empty) .innerTroopWrapper'); | ||||
|         troopBlocks.each((idx, el) => { | ||||
|             const troopId = elClassId(jQuery(el).attr('class') || '', 'troop'); | ||||
|             console.log('TROOP', troopId); | ||||
|             if (troopId) { | ||||
|                 const id = uniqId(); | ||||
|                 jQuery(el) | ||||
|                     .find('.details') | ||||
|                     .append(`<div style="padding: 8px 0"><a id="${id}" href="#">Обучить</a></div>`); | ||||
|                 jQuery(`#${id}`).on('click', evt => { | ||||
|                     evt.preventDefault(); | ||||
|                     this.onTrainTroopClick(this.buildId, troopId, el); | ||||
|                 }); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     private onTrainTroopClick(buildId: Number, troopId: Number, el: HTMLElement) { | ||||
|         console.log('TRAIN TROOPERS', 'TROOP ID', troopId, 'BUILDING ID', buildId); | ||||
|         const input = jQuery(el).find(`input[name="t${troopId}"]`); | ||||
|         const count = Number(input.val()); | ||||
|         if (!isNaN(count) && count > 0) { | ||||
|             console.log('PREPARE TO TRAIN', count, 'TROOPERS'); | ||||
|             for (let n of split(count)) { | ||||
|                 this.scheduler.scheduleTask( | ||||
|                     new Command(TrainTroopTask.name, { | ||||
|                         buildId, | ||||
|                         troopId, | ||||
|                         trainCount: n, | ||||
|                     }) | ||||
|                 ); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private log(...args) { | ||||
|         console.log('BUILD PAGE:', ...args); | ||||
|     } | ||||
|  | ||||
|     private logError(...args) { | ||||
|         console.error(...args); | ||||
|     } | ||||
| } | ||||
| @@ -1,9 +1,9 @@ | ||||
| import * as URLParse from 'url-parse'; | ||||
| import { markPage, uniqId, waitForLoad } from './utils'; | ||||
| import { Scheduler } from './Scheduler'; | ||||
| import { UpgradeBuildingTask } from './Task/UpgradeBuildingTask'; | ||||
| import { Command } from './Common'; | ||||
| import { TaskQueueRenderer } from './TaskQueueRenderer'; | ||||
| import { elClassId, markPage, waitForLoad } from '../utils'; | ||||
| import { Scheduler } from '../Scheduler'; | ||||
| import { UpgradeBuildingTask } from '../Task/UpgradeBuildingTask'; | ||||
| import { TaskQueueRenderer } from '../TaskQueueRenderer'; | ||||
| import { BuildPage } from './BuildPage'; | ||||
| 
 | ||||
| export class Dashboard { | ||||
|     private readonly version: string; | ||||
| @@ -33,15 +33,7 @@ export class Dashboard { | ||||
|         } | ||||
| 
 | ||||
|         if (p.pathname === '/build.php') { | ||||
|             this.log('BUILD PAGE DETECTED'); | ||||
|             const id = uniqId(); | ||||
|             jQuery('.upgradeButtonsContainer .section1').append( | ||||
|                 `<div style="padding: 8px"><a id="${id}" href="#">В очередь</a></div>` | ||||
|             ); | ||||
|             jQuery(`#${id}`).on('click', evt => { | ||||
|                 evt.preventDefault(); | ||||
|                 this.onScheduleBuilding(p.query.id || ''); | ||||
|             }); | ||||
|             new BuildPage(this.scheduler, Number(p.query.id)).run(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @@ -50,29 +42,24 @@ export class Dashboard { | ||||
|         new TaskQueueRenderer().render(this.scheduler.getTaskItems()); | ||||
|     } | ||||
| 
 | ||||
|     private onScheduleBuilding(id: string) { | ||||
|         const queueItem = new Command(UpgradeBuildingTask.name, { | ||||
|             id, | ||||
|         }); | ||||
|         this.scheduler.scheduleTask(queueItem); | ||||
|         const n = new Notification(`Building ${id} scheduled`); | ||||
|         setTimeout(() => n && n.close(), 4000); | ||||
|     } | ||||
| 
 | ||||
|     private showSlotIds(prefix: string) { | ||||
|         const tasks = this.scheduler.getTaskItems(); | ||||
|         jQuery('.level.colorLayer').each((idx, el) => { | ||||
|             let num = ''; | ||||
|             el.classList.forEach(cls => { | ||||
|                 if (cls.startsWith(prefix)) { | ||||
|                     num = cls.substr(prefix.length); | ||||
|                 } | ||||
|             }); | ||||
|             const t = jQuery(el) | ||||
|             const buildId = elClassId(jQuery(el).attr('class') || '', prefix); | ||||
|             const oldLabel = jQuery(el) | ||||
|                 .find('.labelLayer') | ||||
|                 .text(); | ||||
|             jQuery(el) | ||||
|                 .find('.labelLayer') | ||||
|                 .text(num + ':' + t); | ||||
|                 .text(buildId + ':' + oldLabel); | ||||
|             const inQueue = tasks.find( | ||||
|                 t => t.cmd.name === UpgradeBuildingTask.name && Number(t.cmd.args.id) === Number(buildId) | ||||
|             ); | ||||
|             if (inQueue !== undefined) { | ||||
|                 jQuery(el).css({ | ||||
|                     'background-image': 'linear-gradient(to top, #f00, #f00 100%)', | ||||
|                 }); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
| @@ -14,17 +14,11 @@ export class ModeDetector { | ||||
|  | ||||
|     private isAutoByLocation(): boolean { | ||||
|         const p = new URLParse(window.location.href, true); | ||||
|         console.log('PARSED LOCATION', p); | ||||
|         if (p.query['auto-management'] !== undefined) { | ||||
|             console.log('AUTO MANAGEMENT ON'); | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|         return p.query['auto-management'] !== undefined; | ||||
|     } | ||||
|  | ||||
|     private isAutoBySession(): boolean { | ||||
|         const k = sessionStorage.getItem(SESSION_KEY); | ||||
|         return k === SESSION_VALUE; | ||||
|         const sessionKey = sessionStorage.getItem(SESSION_KEY); | ||||
|         return sessionKey === SESSION_VALUE; | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										18
									
								
								src/Task/TrainTroopTask.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/Task/TrainTroopTask.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| import { Args, Command } from '../Common'; | ||||
| import { Task } from '../Storage/TaskQueue'; | ||||
| import { TaskController, registerTask } from './TaskController'; | ||||
| import { GoToPageAction } from '../Action/GoToPageAction'; | ||||
| import { CompleteTaskAction } from '../Action/CompleteTaskAction'; | ||||
| import { TrainTrooperAction } from '../Action/TrainTrooperAction'; | ||||
|  | ||||
| @registerTask | ||||
| export class TrainTroopTask extends TaskController { | ||||
|     async run(task: Task) { | ||||
|         const args: Args = { ...task.cmd.args, taskId: task.id }; | ||||
|         this.scheduler.scheduleActions([ | ||||
|             new Command(GoToPageAction.name, { ...args, path: '/build.php?id=' + args.buildId }), | ||||
|             new Command(TrainTrooperAction.name, args), | ||||
|             new Command(CompleteTaskAction.name, args), | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
| @@ -1,6 +1,6 @@ | ||||
| import { ModeDetector } from './ModeDetector'; | ||||
| import { Scheduler } from './Scheduler'; | ||||
| import { Dashboard } from './Dashboard'; | ||||
| import { Dashboard } from './Dashboard/Dashboard'; | ||||
| import TxtVersion from '!!raw-loader!./version.txt'; | ||||
|  | ||||
| console.log('TRAVIAN AUTOMATION', TxtVersion); | ||||
|   | ||||
							
								
								
									
										37
									
								
								src/utils.ts
									
									
									
									
									
								
							
							
						
						
									
										37
									
								
								src/utils.ts
									
									
									
									
									
								
							| @@ -34,6 +34,43 @@ export function trimPrefix(text: string, prefix: string): string { | ||||
|     return text.startsWith(prefix) ? text.substr(prefix.length) : text; | ||||
| } | ||||
|  | ||||
| export function elClassId(classes: string, prefix: string): number | undefined { | ||||
|     let result: number | undefined = undefined; | ||||
|     classes.split(/\s/).forEach(part => { | ||||
|         if (part.startsWith(prefix)) { | ||||
|             result = Number(part.substr(prefix.length)); | ||||
|             if (isNaN(result)) { | ||||
|                 result = undefined; | ||||
|             } | ||||
|         } | ||||
|     }); | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| export function* split(n: number) { | ||||
|     let c = n; | ||||
|     while (c > 0) { | ||||
|         const next = 2 + Math.floor(Math.random() * 3); | ||||
|         if (next < c) { | ||||
|             yield next; | ||||
|             c -= next; | ||||
|         } else { | ||||
|             yield c; | ||||
|             c = 0; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| export function toNumber(value: any): number | undefined { | ||||
|     const converted = Number(value); | ||||
|     return isNaN(converted) ? undefined : converted; | ||||
| } | ||||
|  | ||||
| export function getNumber(value: any, def: number = 0): number { | ||||
|     const converted = toNumber(value); | ||||
|     return converted === undefined ? def : converted; | ||||
| } | ||||
|  | ||||
| export function markPage(text: string, version: string) { | ||||
|     jQuery('body').append( | ||||
|         '<div style="' + | ||||
|   | ||||
		Reference in New Issue
	
	Block a user