initial auto-xiv integration
continuous-integration/drone/push Build is passing Details

This commit is contained in:
MashPotato 2023-01-28 00:42:47 -07:00
parent 10e32fecfb
commit 80a21402bb
22 changed files with 607 additions and 5 deletions

2
.gitignore vendored
View File

@ -1,4 +1,4 @@
garlandtools-dump/item/*
garlandtools-dump/item/*
garlandtools-dump/output-data.json
garlandtools-dump/garlandtools-dump.exe
gilgetter.exe

265
auto-xiv/autoxiv.ahk Normal file
View File

@ -0,0 +1,265 @@
#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
; #Warn ; Enable warnings to assist with detecting common errors.
SendMode Event ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory.
CoordMode, Mouse, Screen
^PgUp::
retainerGetMaterials()
ExitApp
return
; End will kill the script entirely
^End::
ExitApp
return
retainerGetMaterials() {
ImageSearch, retainerListX, retainerListY, 530, 430, 1260, 1000, *150 images\retainernames.png
if (ErrorLevel != 0) {
WriteLog("ImageSearch for retainernames.png returned error level " + ErrorLevel)
return
}
currentRetainer := 2
MouseMove, retainerListX+72, retainerListY+60+((currentRetainer-1)*36), 3
Sleep 300
MouseClick ; Click retainer
MouseClick
Sleep 2500
Click 1200, 250 ; Close dialog
MouseClick
Sleep 1000
ImageSearch, entrustWithdrawX, entrustWithdrawY, 940, 512, 1680, 700, *150 images\entrustwithdraw.png
if (ErrorLevel != 0) {
WriteLog("ImageSearch for entrustwithdraw.png returned error level " + ErrorLevel)
return
}
MouseMove, entrustWithdrawX+144, entrustWithdrawY+30, 3
Sleep 300
MouseClick ; Click entrust/withdraw
MouseClick
Sleep 400
ImageSearch, retainerInventoryX, retainerInventoryY, 1750, 10, 2560, 215, *150 images\retainerinventory.png
if (ErrorLevel != 0) {
WriteLog("ImageSearch for retainerinventory.png returned error level " + ErrorLevel)
return
}
; loop through items for this specific retainer
MouseMove, retainerInventoryX, retainerInventoryY, 3
; container 1
; container 2
; item
row := 4
col := 4 ; 69 in between
MouseMove, retainerInventoryX+404+(col-1)*69, retainerInventoryY+133+(row-1)*69, 3
Sleep 300
MouseClick, Right ;
Sleep 100
MouseClick, Right
Sleep 100
; containers 3-4
MouseMove, retainerInventoryX+362, retainerInventoryY+68, 3
Sleep 300
MouseClick ; Click the 2 button to switch to containers 3-4
MouseClick
; item
row := 4
col := 4 ; 69 in between
MouseMove, retainerInventoryX+46+(col-1)*69, retainerInventoryY+133+(row-1)*69, 3
Sleep 300
MouseClick, Right ;
Sleep 100
MouseClick, Right
Sleep 100
; item
row := 3
col := 1 ; 69 in between
MouseMove, retainerInventoryX+46+(col-1)*69, retainerInventoryY+133+(row-1)*69, 3
Sleep 300
MouseClick, Right ;
Sleep 100
MouseClick, Right
Sleep 100
; item
row := 3
col := 4 ; 69 in between
MouseMove, retainerInventoryX+46+(col-1)*69, retainerInventoryY+133+(row-1)*69, 3
Sleep 300
MouseClick, Right ;
Sleep 100
MouseClick, Right
Sleep 100
; item
row := 3
col := 5 ; 69 in between
MouseMove, retainerInventoryX+46+(col-1)*69, retainerInventoryY+133+(row-1)*69, 3
Sleep 300
MouseClick, Right ;
Sleep 100
MouseClick, Right
Sleep 100
; item
row := 4
col := 4 ; 69 in between
MouseMove, retainerInventoryX+46+(col-1)*69, retainerInventoryY+133+(row-1)*69, 3
Sleep 300
MouseClick, Right ;
Sleep 100
MouseClick, Right
Sleep 100
; item
row := 5
col := 3 ; 69 in between
MouseMove, retainerInventoryX+46+(col-1)*69, retainerInventoryY+133+(row-1)*69, 3
Sleep 300
MouseClick, Right ;
Sleep 100
MouseClick, Right
Sleep 100
; container 5
MouseMove, retainerInventoryX+607, retainerInventoryY+68, 3
Sleep 300
MouseClick ; Click the 2 button to switch to containers 3-4
MouseClick
}
example() {
MouseClick ; Click retainer
MouseClick
Sleep 2500
Click 1200, 250 ; Close dialog
MouseClick
Sleep 1000
ImageSearch, outX, outY, 530, 430, 1660, 900, *150 images\sellitems.png
if (ErrorLevel = 0) {
MouseMove, outX+72, outY+20, 2
Sleep 200
MouseClick ; Click sellitems
MouseClick
Sleep 400
highestCursorY := 0 ; used to determine if we've reached the bottom of the list
Loop { ; loop through all items posted for sale
clipboard := "" ; clear the clipboard / pasted price
Sleep 500
Send, {Numpad2} ; Move cursor to first/next item
Sleep 300
ImageSearch, outX, outY, 2050, 60, 2180, 620, *8 images\selected.png
if (ErrorLevel = 0) {
; check if we have hit the bottom of the list and moved back up to the top
if (outY < highestCursorY-15) { ; -15 to allow for tolerance in image search function
break
}
; handle case where only 1 item is posted by checking if highlighted item is near expected top of list area and it hasn't moved
if (outY < 200) && (outY < highestCursorY+15) { ; +15 to allow for tolerance in image search function
break
}
highestCursorY = %outY% ; update highest cursory
Send, {Numpad0} ; Select the item
Sleep 100
Send, {Numpad0} ; Select Adjust Price
if (currentRetainer <= numNQRetainers) { ; If it's an HQ retainer, hold shift so penny pincher copies HQ price
Send, {Shift down}
}
Sleep 75
Send, {Numpad8} ; Move cursor to Compare Prices
Sleep 85
Send, {Numpad0} ; Select compare prices
Sleep 850
Send, {Esc} ; Close compare prices
Sleep 200
if (currentRetainer <= numNQRetainers) { ; If it's an HQ retainer, release shift
Send, {Shift up}
}
if (clipboard = "") { ; If nobody else has listed the item, just keep the same price. Also stop-gap in case dalamud / penny pincher isn't loaded correctly
Send, {Esc} ; Close the item
Sleep 200
Continue ; Move on to next iteration
}
ImageSearch, outX, outY, 1760, 360, 2220, 600, *150 images\askingprice.png
if (ErrorLevel = 0) {
MouseMove, outX+300, outY+15, 3
Sleep 200
MouseClick ; Click asking price input field
Sleep 200
SendRaw %clipboard% ; Update price
Sleep 175
Send, {Enter} ; unlock cursor from numerical field
Sleep 150
Send, {Numpad2} ; move cursor down to confirm button
Sleep 150
Send, {Numpad2}
Sleep 150
Send, {Numpad2}
Sleep 150
Send, {Numpad0} ; Select confirm button
}
} else {
break ; there are no items listed for sale
}
}
; Quit retainer
Send, {Esc}
Sleep 200
Send, {Esc}
Sleep 200
Send, {Esc}
Sleep 200
Send, {Esc}
Sleep 200
Send, {Esc}
Sleep 1000
Click 1200, 250 ; Close dialog
MouseClick
Sleep 2100
}
}
WriteLog(text) {
FileAppend, % A_NowUTC ": " text "`n", logfile.txt ; can provide a full path to write to another directory
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

BIN
auto-xiv/images/assign.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 395 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 873 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 792 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -1,10 +1,13 @@
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"log"
"math"
"net/http"
"sort"
"strconv"
@ -85,9 +88,20 @@ func staleItemsPageHandler(w http.ResponseWriter, _ *http.Request) {
func craftingCheckoutPageHandler(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" {
type AutoXivItem struct {
Row int
Col int
}
type Retainer struct {
Name string
ItemsToGet []inventorytools.Item
RetainerNumber int
Name string
ItemsToGet []inventorytools.Item
ContainerOne []AutoXivItem
ContainerTwo []AutoXivItem
ContainerThree []AutoXivItem
ContainerFour []AutoXivItem
ContainerFive []AutoXivItem
}
type CraftingItem struct {
@ -137,7 +151,9 @@ func craftingCheckoutPageHandler(w http.ResponseWriter, r *http.Request) {
}
// Materials in retainer inventory and subtract
retainerNumber := 0
for _, retainer := range inventories.Retainers {
retainerNumber++
itemsToGet := make([]inventorytools.Item, 0)
for _, item := range retainer.RetainerBags {
if _, ok := craftingMaterialMap[item.ID]; ok {
@ -160,7 +176,7 @@ func craftingCheckoutPageHandler(w http.ResponseWriter, r *http.Request) {
}
}
if len(itemsToGet) > 0 {
pageData.Retainers = append(pageData.Retainers, Retainer{Name: retainer.Name, ItemsToGet: itemsToGet})
pageData.Retainers = append(pageData.Retainers, Retainer{Name: retainer.Name, RetainerNumber: retainerNumber, ItemsToGet: itemsToGet})
}
}
@ -192,6 +208,39 @@ func craftingCheckoutPageHandler(w http.ResponseWriter, r *http.Request) {
log.Printf("failed to execute template: %v", err)
}
// solve auto-xiv data for template
for i, retainer := range pageData.Retainers {
for _, item := range retainer.ItemsToGet {
autoXivItem := AutoXivItem{}
autoXivItem.Row = int(math.Ceil(float64(item.SortedSlotIndex) / 5))
autoXivItem.Col = item.SortedSlotIndex % 5
if autoXivItem.Col == 0 {
autoXivItem.Col = 5
}
if item.SortedContainer == 1 {
pageData.Retainers[i].ContainerOne = append(pageData.Retainers[i].ContainerOne, autoXivItem)
} else if item.SortedContainer == 2 {
pageData.Retainers[i].ContainerTwo = append(pageData.Retainers[i].ContainerTwo, autoXivItem)
} else if item.SortedContainer == 3 {
pageData.Retainers[i].ContainerThree = append(pageData.Retainers[i].ContainerThree, autoXivItem)
} else if item.SortedContainer == 4 {
pageData.Retainers[i].ContainerFour = append(pageData.Retainers[i].ContainerFour, autoXivItem)
} else if item.SortedContainer == 5 {
pageData.Retainers[i].ContainerFive = append(pageData.Retainers[i].ContainerFive, autoXivItem)
}
}
}
withdrawMaterialsAhk = bytes.NewBuffer(nil)
err = t.ExecuteTemplate(withdrawMaterialsAhk, "withdrawmaterials.ahk", pageData)
if err != nil {
log.Printf("failed to execute withdrawmaterials.ahk template: %v", err)
}
} else if r.Method == "POST" {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
@ -242,6 +291,22 @@ func craftingCheckoutPageHandler(w http.ResponseWriter, r *http.Request) {
}
}
// http - GET /auto-xiv
//
// Downloads the .ahk file for withdrawing items
func autoXivHandler(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Disposition", "attachment; filename=withdraw-items.ahk")
b, err := io.ReadAll(withdrawMaterialsAhk)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
log.Printf("failed to read withdrawMaterialsAhk to bytes: %v", err)
}
w.Write(b)
}
// http - GET /full-price-update
// Requests a full price update if criteria is met
func fullPriceUpdateHandler(w http.ResponseWriter, _ *http.Request) {

View File

@ -26,7 +26,7 @@ func init() {
if err != nil {
log.Fatal("Getting appdata directory failed:", err)
}
inventoriesFilePath = fmt.Sprintf("%s/XIVLauncher/pluginConfigs/InventoryTools/inventories.json", appDataDir)
inventoriesFilePath = fmt.Sprintf("%s/XIVLauncher/pluginConfigs/GilGetterPlugin/inventories.json", appDataDir)
setRetainers() // Create your own definitions that satisfy the retainer struct - see README.md
}

View File

@ -1,6 +1,7 @@
package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
@ -32,6 +33,7 @@ var (
priceUpdateProgress int // an integer from 0-100 indicating percentage of any ongoing Universalis update. If 100, then the update is complete.
world, datacenter, universalisPriceEndpoint string // filter Universalis to this world name
priceUpdateErrors map[int]bool // if an item ID exists in here, then there was an error when updating its price. Gets reset after priceUpdateQueue is drained.
withdrawMaterialsAhk *bytes.Buffer // contains the processed ahk template of items that need to be withdrawn for crafting
retainers []string // contains name of retainers
craftingCheckoutItemIDs []int // The item IDs which were selected on the crafting checkout page, used for shopping list
@ -186,6 +188,8 @@ func init() {
lastFullPriceUpdateTime = time.Now()
itemSlice = make([]MarketItem, 0)
withdrawMaterialsAhk = bytes.NewBuffer(nil)
}
// HTTP routing
@ -200,6 +204,9 @@ func main() {
// Serve public static files such as css, js, images
http.Handle("/public/", http.StripPrefix("/public/", http.FileServer(http.Dir("public"))))
// Download .ahk file
http.HandleFunc("/auto-xiv", autoXivHandler)
// Page Handlers
// Anything that is responsible for the base elements of a viewable web page
http.HandleFunc("/", homePageHandler)

View File

@ -68,6 +68,7 @@
</table>
<h2 class="ui header">Materials From Retainers</h2>
<a href="/auto-xiv">Auto XIV</a>
{{ range .Retainers }}
<h3 class="ui header">{{ .Name }}</h4>
<table class="ui compact celled striped table">

View File

@ -0,0 +1,264 @@
#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
; #Warn ; Enable warnings to assist with detecting common errors.
SendMode Event ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory.
CoordMode, Mouse, Screen
^PgUp::
retainerGetMaterials()
ExitApp
return
; End will kill the script entirely
^End::
ExitApp
return
retainerGetMaterials() {
ImageSearch, retainerListX, retainerListY, 530, 430, 1260, 1000, *150 images\retainernames.png
if (ErrorLevel != 0) {
WriteLog("ImageSearch for retainernames.png returned error level " + ErrorLevel)
return
}
{{ range .Retainers }}
currentRetainer := {{ .RetainerNumber }}
MouseMove, retainerListX+72, retainerListY+60+((currentRetainer-1)*36), 3
Sleep 300
MouseClick ; Click retainer
MouseClick
Sleep 2500
Click 1200, 250 ; Close dialog
MouseClick
Sleep 1000
ImageSearch, entrustWithdrawX, entrustWithdrawY, 940, 512, 1680, 700, *150 images\entrustwithdraw.png
if (ErrorLevel != 0) {
WriteLog("ImageSearch for entrustwithdraw.png returned error level " + ErrorLevel)
return
}
MouseMove, entrustWithdrawX+144, entrustWithdrawY+30, 3
Sleep 300
MouseClick ; Click entrust/withdraw
MouseClick
Sleep 400
ImageSearch, retainerInventoryX, retainerInventoryY, 1750, 10, 2560, 215, *150 images\retainerinventory.png
if (ErrorLevel != 0) {
WriteLog("ImageSearch for retainerinventory.png returned error level " + ErrorLevel)
return
}
; loop through items for this specific retainer
MouseMove, retainerInventoryX, retainerInventoryY, 3
; container 1 - offset 46
{{ range .ContainerOne }}
row := {{ .Row }}
col := {{ .Col }}
MouseMove, retainerInventoryX+46+(col-1)*69, retainerInventoryY+133+(row-1)*69, 3
Sleep 300
MouseClick, Right ;
Sleep 100
MouseClick, Right
Sleep 100
{{ end }}
; container 2 - offset 404
{{ range .ContainerTwo }}
row := {{ .Row }}
col := {{ .Col }}
MouseMove, retainerInventoryX+404+(col-1)*69, retainerInventoryY+133+(row-1)*69, 3
Sleep 300
MouseClick, Right ;
Sleep 100
MouseClick, Right
Sleep 100
{{ end }}
; select containers 3-4
MouseMove, retainerInventoryX+362, retainerInventoryY+68, 3
Sleep 300
MouseClick ; Click the 2 button to switch to containers 3-4
MouseClick
Sleep 20
; container 3 - offset 46
{{ range .ContainerThree }}
row := {{ .Row }}
col := {{ .Col }}
MouseMove, retainerInventoryX+46+(col-1)*69, retainerInventoryY+133+(row-1)*69, 3
Sleep 300
MouseClick, Right ;
Sleep 100
MouseClick, Right
Sleep 100
{{ end }}
; container 4 - offset 404
{{ range .ContainerFour }}
row := {{ .Row }}
col := {{ .Col }}
MouseMove, retainerInventoryX+404+(col-1)*69, retainerInventoryY+133+(row-1)*69, 3
Sleep 300
MouseClick, Right ;
Sleep 100
MouseClick, Right
Sleep 100
{{ end }}
; select container 5
MouseMove, retainerInventoryX+607, retainerInventoryY+68, 3
Sleep 300
MouseClick ; Click the 2 button to switch to containers 3-4
MouseClick
Sleep 20
; container 5 - offset 46
{{ range .ContainerFive }}
row := {{ .Row }}
col := {{ .Col }}
MouseMove, retainerInventoryX+46+(col-1)*69, retainerInventoryY+133+(row-1)*69, 3
Sleep 300
MouseClick, Right ;
Sleep 100
MouseClick, Right
Sleep 100
{{ end }}
; Quit retainer
Sleep 200
Send, {Esc}
Sleep 200
Send, {Esc}
Sleep 200
Send, {Esc}
Sleep 200
Send, {Esc}
Sleep 200
Send, {Esc}
Sleep 1000
Click 1200, 250 ; Close dialog
MouseClick
Sleep 2100
{{ end }}
}
example() {
MouseClick ; Click retainer
MouseClick
Sleep 2500
Click 1200, 250 ; Close dialog
MouseClick
Sleep 1000
ImageSearch, outX, outY, 530, 430, 1660, 900, *150 images\sellitems.png
if (ErrorLevel = 0) {
MouseMove, outX+72, outY+20, 2
Sleep 200
MouseClick ; Click sellitems
MouseClick
Sleep 400
highestCursorY := 0 ; used to determine if we've reached the bottom of the list
Loop { ; loop through all items posted for sale
clipboard := "" ; clear the clipboard / pasted price
Sleep 500
Send, {Numpad2} ; Move cursor to first/next item
Sleep 300
ImageSearch, outX, outY, 2050, 60, 2180, 620, *8 images\selected.png
if (ErrorLevel = 0) {
; check if we have hit the bottom of the list and moved back up to the top
if (outY < highestCursorY-15) { ; -15 to allow for tolerance in image search function
break
}
; handle case where only 1 item is posted by checking if highlighted item is near expected top of list area and it hasn't moved
if (outY < 200) && (outY < highestCursorY+15) { ; +15 to allow for tolerance in image search function
break
}
highestCursorY = %outY% ; update highest cursory
Send, {Numpad0} ; Select the item
Sleep 100
Send, {Numpad0} ; Select Adjust Price
if (currentRetainer <= numNQRetainers) { ; If it's an HQ retainer, hold shift so penny pincher copies HQ price
Send, {Shift down}
}
Sleep 75
Send, {Numpad8} ; Move cursor to Compare Prices
Sleep 85
Send, {Numpad0} ; Select compare prices
Sleep 850
Send, {Esc} ; Close compare prices
Sleep 200
if (currentRetainer <= numNQRetainers) { ; If it's an HQ retainer, release shift
Send, {Shift up}
}
if (clipboard = "") { ; If nobody else has listed the item, just keep the same price. Also stop-gap in case dalamud / penny pincher isn't loaded correctly
Send, {Esc} ; Close the item
Sleep 200
Continue ; Move on to next iteration
}
ImageSearch, outX, outY, 1760, 360, 2220, 600, *150 images\askingprice.png
if (ErrorLevel = 0) {
MouseMove, outX+300, outY+15, 3
Sleep 200
MouseClick ; Click asking price input field
Sleep 200
SendRaw %clipboard% ; Update price
Sleep 175
Send, {Enter} ; unlock cursor from numerical field
Sleep 150
Send, {Numpad2} ; move cursor down to confirm button
Sleep 150
Send, {Numpad2}
Sleep 150
Send, {Numpad2}
Sleep 150
Send, {Numpad0} ; Select confirm button
}
} else {
break ; there are no items listed for sale
}
}
; Quit retainer
Send, {Esc}
Sleep 200
Send, {Esc}
Sleep 200
Send, {Esc}
Sleep 200
Send, {Esc}
Sleep 200
Send, {Esc}
Sleep 1000
Click 1200, 250 ; Close dialog
MouseClick
Sleep 2100
}
}
WriteLog(text) {
FileAppend, % A_NowUTC ": " text "`n", logfile.txt ; can provide a full path to write to another directory
}