init
This commit is contained in:
commit
d787e2f431
|
@ -0,0 +1,2 @@
|
||||||
|
extends:
|
||||||
|
- "@commitlint/config-conventional"
|
|
@ -0,0 +1,19 @@
|
||||||
|
.git
|
||||||
|
.env*
|
||||||
|
README.md
|
||||||
|
LICENSE
|
||||||
|
renovate.json
|
||||||
|
.gitignore
|
||||||
|
.dockerignore
|
||||||
|
bin
|
||||||
|
.golangci.yml
|
||||||
|
.pre-commit-config.yaml
|
||||||
|
build
|
||||||
|
k8s
|
||||||
|
.commitlintrc.yml
|
||||||
|
additional_envs.sh
|
||||||
|
skaffold.yml
|
||||||
|
.editorconfig
|
||||||
|
.woodpecker
|
||||||
|
.kpt-pipeline
|
||||||
|
tmp
|
|
@ -0,0 +1,5 @@
|
||||||
|
PATH_add bin
|
||||||
|
export GOBIN=$PWD/bin
|
||||||
|
if [ -f "additional_envs.sh" ]; then
|
||||||
|
source <(cat additional_envs.sh)
|
||||||
|
fi
|
|
@ -0,0 +1,5 @@
|
||||||
|
.idea
|
||||||
|
bin/*
|
||||||
|
additional_envs.sh
|
||||||
|
.kpt-pipeline
|
||||||
|
tmp
|
|
@ -0,0 +1,476 @@
|
||||||
|
run:
|
||||||
|
tests: true
|
||||||
|
timeout: 5m
|
||||||
|
|
||||||
|
linters:
|
||||||
|
disable-all: true
|
||||||
|
enable:
|
||||||
|
- gocyclo
|
||||||
|
- asasalint
|
||||||
|
- asciicheck
|
||||||
|
- bodyclose
|
||||||
|
- bidichk
|
||||||
|
- exportloopref
|
||||||
|
- errcheck
|
||||||
|
- gocritic
|
||||||
|
- gosec
|
||||||
|
- gofmt
|
||||||
|
- goimports
|
||||||
|
- gosimple
|
||||||
|
- govet
|
||||||
|
- ineffassign
|
||||||
|
- misspell
|
||||||
|
- nakedret
|
||||||
|
- prealloc
|
||||||
|
- staticcheck
|
||||||
|
- typecheck
|
||||||
|
- unconvert
|
||||||
|
- unused
|
||||||
|
- lll
|
||||||
|
- nestif
|
||||||
|
- thelper
|
||||||
|
- nonamedreturns
|
||||||
|
- tenv
|
||||||
|
- testpackage
|
||||||
|
- noctx
|
||||||
|
- tparallel
|
||||||
|
- usestdlibvars
|
||||||
|
- unconvert
|
||||||
|
- makezero
|
||||||
|
- grouper
|
||||||
|
- errname
|
||||||
|
- exhaustive
|
||||||
|
- tagliatelle
|
||||||
|
- contextcheck
|
||||||
|
- gocheckcompilerdirectives
|
||||||
|
- errname
|
||||||
|
- forcetypeassert
|
||||||
|
- durationcheck
|
||||||
|
- predeclared
|
||||||
|
- promlinter
|
||||||
|
- wastedassign
|
||||||
|
- testifylint
|
||||||
|
- inamedparam
|
||||||
|
- sloglint
|
||||||
|
- revive
|
||||||
|
|
||||||
|
linters-settings:
|
||||||
|
gocyclo:
|
||||||
|
min-complexity: 10
|
||||||
|
tagliatelle:
|
||||||
|
case:
|
||||||
|
rules:
|
||||||
|
json: camel
|
||||||
|
bun: snake
|
||||||
|
lll:
|
||||||
|
line-length: 120
|
||||||
|
govet:
|
||||||
|
enable:
|
||||||
|
- asmdecl
|
||||||
|
- assign
|
||||||
|
- atomic
|
||||||
|
- atomicalign
|
||||||
|
- bools
|
||||||
|
- buildtag
|
||||||
|
- cgocall
|
||||||
|
- composites
|
||||||
|
- copylocks
|
||||||
|
- deepequalerrors
|
||||||
|
- errorsas
|
||||||
|
- findcall
|
||||||
|
- framepointer
|
||||||
|
- httpresponse
|
||||||
|
- ifaceassert
|
||||||
|
- loopclosure
|
||||||
|
- lostcancel
|
||||||
|
- nilfunc
|
||||||
|
- nilness
|
||||||
|
- printf
|
||||||
|
- reflectvaluecompare
|
||||||
|
- shadow
|
||||||
|
- shift
|
||||||
|
- sigchanyzer
|
||||||
|
- sortslice
|
||||||
|
- stdmethods
|
||||||
|
- stringintconv
|
||||||
|
- structtag
|
||||||
|
- testinggoroutine
|
||||||
|
- tests
|
||||||
|
- unmarshal
|
||||||
|
- unreachable
|
||||||
|
- unsafeptr
|
||||||
|
- unusedresult
|
||||||
|
- unusedwrite
|
||||||
|
testifylint:
|
||||||
|
enable-all: true
|
||||||
|
sloglint:
|
||||||
|
attr-only: true
|
||||||
|
revive:
|
||||||
|
rules:
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#add-constant
|
||||||
|
- name: add-constant
|
||||||
|
severity: warning
|
||||||
|
disabled: true
|
||||||
|
arguments:
|
||||||
|
- maxLitCount: "3"
|
||||||
|
allowStrs: "\"\""
|
||||||
|
ignoreFuncs: os.Exit,wg.Add,make
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#argument-limit
|
||||||
|
- name: argument-limit
|
||||||
|
severity: warning
|
||||||
|
disabled: true
|
||||||
|
arguments: [4]
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#atomic
|
||||||
|
- name: atomic
|
||||||
|
severity: error
|
||||||
|
disabled: false
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#banned-characters
|
||||||
|
- name: banned-characters
|
||||||
|
severity: warning
|
||||||
|
disabled: true
|
||||||
|
arguments: []
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#bare-return
|
||||||
|
- name: bare-return
|
||||||
|
severity: warning
|
||||||
|
disabled: false
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#blank-imports
|
||||||
|
- name: blank-imports
|
||||||
|
severity: error
|
||||||
|
disabled: false
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#bool-literal-in-expr
|
||||||
|
- name: bool-literal-in-expr
|
||||||
|
severity: error
|
||||||
|
disabled: false
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#call-to-gc
|
||||||
|
- name: call-to-gc
|
||||||
|
severity: error
|
||||||
|
disabled: false
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#cognitive-complexity
|
||||||
|
- name: cognitive-complexity
|
||||||
|
severity: warning
|
||||||
|
disabled: true
|
||||||
|
arguments: [7]
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#comment-spacings
|
||||||
|
- name: comment-spacings
|
||||||
|
severity: warning
|
||||||
|
disabled: false
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#confusing-naming
|
||||||
|
- name: confusing-naming
|
||||||
|
severity: warning
|
||||||
|
disabled: false
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#confusing-results
|
||||||
|
- name: confusing-results
|
||||||
|
severity: warning
|
||||||
|
disabled: false
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#constant-logical-expr
|
||||||
|
- name: constant-logical-expr
|
||||||
|
severity: error
|
||||||
|
disabled: false
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#context-as-argument
|
||||||
|
- name: context-as-argument
|
||||||
|
severity: error
|
||||||
|
disabled: false
|
||||||
|
arguments:
|
||||||
|
- allowTypesBefore: "*testing.T,testing.TB"
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#context-keys-type
|
||||||
|
- name: context-keys-type
|
||||||
|
severity: error
|
||||||
|
disabled: false
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#cyclomatic
|
||||||
|
- name: cyclomatic
|
||||||
|
severity: warning
|
||||||
|
disabled: true
|
||||||
|
arguments: [10]
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#datarace
|
||||||
|
- name: datarace
|
||||||
|
severity: error
|
||||||
|
disabled: false
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#deep-exit
|
||||||
|
- name: deep-exit
|
||||||
|
severity: error
|
||||||
|
disabled: false
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#defer
|
||||||
|
- name: defer
|
||||||
|
severity: error
|
||||||
|
disabled: false
|
||||||
|
arguments:
|
||||||
|
- [call-chain, loop]
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#dot-imports
|
||||||
|
- name: dot-imports
|
||||||
|
severity: error
|
||||||
|
disabled: false
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#duplicated-imports
|
||||||
|
- name: duplicated-imports
|
||||||
|
severity: error
|
||||||
|
disabled: false
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#early-return
|
||||||
|
- name: early-return
|
||||||
|
severity: error
|
||||||
|
disabled: false
|
||||||
|
arguments:
|
||||||
|
- preserveScope
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#empty-block
|
||||||
|
- name: empty-block
|
||||||
|
severity: error
|
||||||
|
disabled: false
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#empty-lines
|
||||||
|
- name: empty-lines
|
||||||
|
severity: error
|
||||||
|
disabled: false
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#enforce-map-style
|
||||||
|
- name: enforce-map-style
|
||||||
|
severity: error
|
||||||
|
disabled: false
|
||||||
|
arguments:
|
||||||
|
- make
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#error-naming
|
||||||
|
- name: error-naming
|
||||||
|
severity: error
|
||||||
|
disabled: false
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#error-return
|
||||||
|
- name: error-return
|
||||||
|
severity: error
|
||||||
|
disabled: false
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#error-strings
|
||||||
|
- name: error-strings
|
||||||
|
severity: warning
|
||||||
|
disabled: false
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#errorf
|
||||||
|
- name: errorf
|
||||||
|
severity: error
|
||||||
|
disabled: false
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#exported
|
||||||
|
- name: exported
|
||||||
|
severity: warning
|
||||||
|
disabled: true
|
||||||
|
arguments:
|
||||||
|
- preserveScope
|
||||||
|
- checkPrivateReceivers
|
||||||
|
- sayRepetitiveInsteadOfStutters
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#file-header
|
||||||
|
- name: file-header
|
||||||
|
severity: warning
|
||||||
|
disabled: true
|
||||||
|
arguments:
|
||||||
|
- This is the text that must appear at the top of source files.
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#flag-parameter
|
||||||
|
- name: flag-parameter
|
||||||
|
severity: warning
|
||||||
|
disabled: false
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#function-result-limit
|
||||||
|
- name: function-result-limit
|
||||||
|
severity: warning
|
||||||
|
disabled: false
|
||||||
|
arguments: [4]
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#function-length
|
||||||
|
- name: function-length
|
||||||
|
severity: warning
|
||||||
|
disabled: true
|
||||||
|
arguments: [10, 0]
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#get-return
|
||||||
|
- name: get-return
|
||||||
|
severity: warning
|
||||||
|
disabled: true
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#identical-branches
|
||||||
|
- name: identical-branches
|
||||||
|
severity: error
|
||||||
|
disabled: false
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#if-return
|
||||||
|
- name: if-return
|
||||||
|
severity: error
|
||||||
|
disabled: false
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#increment-decrement
|
||||||
|
- name: increment-decrement
|
||||||
|
severity: error
|
||||||
|
disabled: false
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#indent-error-flow
|
||||||
|
- name: indent-error-flow
|
||||||
|
severity: warning
|
||||||
|
disabled: false
|
||||||
|
arguments:
|
||||||
|
- preserveScope
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#import-alias-naming
|
||||||
|
- name: import-alias-naming
|
||||||
|
severity: warning
|
||||||
|
disabled: false
|
||||||
|
arguments:
|
||||||
|
- ^[a-z][a-z0-9]{0,}$
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#imports-blacklist
|
||||||
|
- name: imports-blacklist
|
||||||
|
severity: error
|
||||||
|
disabled: false
|
||||||
|
arguments:
|
||||||
|
- reflect
|
||||||
|
- github.com/pkg/errors
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#import-shadowing
|
||||||
|
- name: import-shadowing
|
||||||
|
severity: error
|
||||||
|
disabled: false
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#line-length-limit
|
||||||
|
- name: line-length-limit
|
||||||
|
severity: warning
|
||||||
|
# lll is enabled
|
||||||
|
disabled: true
|
||||||
|
arguments: [80]
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#max-public-structs
|
||||||
|
- name: max-public-structs
|
||||||
|
severity: warning
|
||||||
|
disabled: true
|
||||||
|
arguments: [3]
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#modifies-parameter
|
||||||
|
- name: modifies-parameter
|
||||||
|
severity: warning
|
||||||
|
disabled: false
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#modifies-value-receiver
|
||||||
|
- name: modifies-value-receiver
|
||||||
|
severity: warning
|
||||||
|
disabled: false
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#nested-structs
|
||||||
|
- name: nested-structs
|
||||||
|
severity: error
|
||||||
|
disabled: false
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#optimize-operands-order
|
||||||
|
- name: optimize-operands-order
|
||||||
|
severity: warning
|
||||||
|
disabled: false
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#package-comments
|
||||||
|
- name: package-comments
|
||||||
|
severity: warning
|
||||||
|
disabled: true
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#range
|
||||||
|
- name: range
|
||||||
|
severity: warning
|
||||||
|
disabled: false
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#range-val-in-closure
|
||||||
|
- name: range-val-in-closure
|
||||||
|
severity: warning
|
||||||
|
disabled: false
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#range-val-address
|
||||||
|
- name: range-val-address
|
||||||
|
severity: warning
|
||||||
|
disabled: false
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#receiver-naming
|
||||||
|
- name: receiver-naming
|
||||||
|
severity: warning
|
||||||
|
disabled: false
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#redundant-import-alias
|
||||||
|
- name: redundant-import-alias
|
||||||
|
severity: error
|
||||||
|
disabled: false
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#redefines-builtin-id
|
||||||
|
- name: redefines-builtin-id
|
||||||
|
severity: error
|
||||||
|
disabled: false
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#string-of-int
|
||||||
|
- name: string-of-int
|
||||||
|
severity: warning
|
||||||
|
disabled: false
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#string-format
|
||||||
|
- name: string-format
|
||||||
|
severity: warning
|
||||||
|
disabled: true
|
||||||
|
arguments:
|
||||||
|
- - core.WriteError[1].Message
|
||||||
|
- /^([^A-Z]|$)/
|
||||||
|
- must not start with a capital letter
|
||||||
|
- - fmt.Errorf[0]
|
||||||
|
- /(^|[^\.!?])$/
|
||||||
|
- must not end in punctuation
|
||||||
|
- - panic
|
||||||
|
- /^[^\n]*$/
|
||||||
|
- must not contain line breaks
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#struct-tag
|
||||||
|
- name: struct-tag
|
||||||
|
arguments:
|
||||||
|
- json,inline
|
||||||
|
- bson,outline,gnu
|
||||||
|
severity: warning
|
||||||
|
# tagliatelle is enabled
|
||||||
|
disabled: true
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#superfluous-else
|
||||||
|
- name: superfluous-else
|
||||||
|
severity: error
|
||||||
|
disabled: false
|
||||||
|
arguments:
|
||||||
|
- preserveScope
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#time-equal
|
||||||
|
- name: time-equal
|
||||||
|
severity: error
|
||||||
|
disabled: false
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#time-naming
|
||||||
|
- name: time-naming
|
||||||
|
severity: error
|
||||||
|
disabled: false
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#var-naming
|
||||||
|
- name: var-naming
|
||||||
|
severity: error
|
||||||
|
disabled: false
|
||||||
|
arguments:
|
||||||
|
- [] # AllowList
|
||||||
|
- [] # DenyList
|
||||||
|
- - upperCaseConst: false
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#var-declaration
|
||||||
|
- name: var-declaration
|
||||||
|
severity: error
|
||||||
|
disabled: false
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unconditional-recursion
|
||||||
|
- name: unconditional-recursion
|
||||||
|
severity: error
|
||||||
|
disabled: false
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unexported-naming
|
||||||
|
- name: unexported-naming
|
||||||
|
severity: error
|
||||||
|
disabled: false
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unexported-return
|
||||||
|
- name: unexported-return
|
||||||
|
severity: error
|
||||||
|
disabled: false
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unhandled-error
|
||||||
|
- name: unhandled-error
|
||||||
|
severity: warning
|
||||||
|
disabled: false
|
||||||
|
arguments:
|
||||||
|
- fmt.Printf
|
||||||
|
- fmt.Println
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unnecessary-stmt
|
||||||
|
- name: unnecessary-stmt
|
||||||
|
severity: error
|
||||||
|
disabled: false
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unreachable-code
|
||||||
|
- name: unreachable-code
|
||||||
|
severity: error
|
||||||
|
disabled: false
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unused-parameter
|
||||||
|
- name: unused-parameter
|
||||||
|
severity: error
|
||||||
|
disabled: false
|
||||||
|
arguments:
|
||||||
|
- allowRegex: ^_
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unused-receiver
|
||||||
|
- name: unused-receiver
|
||||||
|
severity: error
|
||||||
|
disabled: true
|
||||||
|
arguments:
|
||||||
|
- allowRegex: ^_
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#useless-break
|
||||||
|
- name: useless-break
|
||||||
|
severity: error
|
||||||
|
disabled: false
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#waitgroup-by-value
|
||||||
|
- name: waitgroup-by-value
|
||||||
|
severity: warning
|
||||||
|
disabled: false
|
||||||
|
|
||||||
|
|
||||||
|
issues:
|
||||||
|
exclude-rules:
|
||||||
|
# Exclude some linters from running on tests files.
|
||||||
|
- path: _test\.go
|
||||||
|
linters:
|
||||||
|
- gosec
|
||||||
|
- gocyclo
|
||||||
|
- path: _test\.go
|
||||||
|
text: add-constant
|
||||||
|
- linters:
|
||||||
|
- lll
|
||||||
|
source: "^//go:generate "
|
|
@ -0,0 +1,2 @@
|
||||||
|
failure-threshold: error
|
||||||
|
format: tty
|
|
@ -0,0 +1,20 @@
|
||||||
|
repos:
|
||||||
|
- repo: https://github.com/alessandrojcm/commitlint-pre-commit-hook
|
||||||
|
rev: v9.10.0
|
||||||
|
hooks:
|
||||||
|
- id: commitlint
|
||||||
|
stages: [commit-msg]
|
||||||
|
additional_dependencies: ["@commitlint/config-conventional"]
|
||||||
|
- repo: https://github.com/golangci/golangci-lint
|
||||||
|
rev: v1.55.2
|
||||||
|
hooks:
|
||||||
|
- id: golangci-lint
|
||||||
|
- repo: https://github.com/hadolint/hadolint
|
||||||
|
rev: v2.12.0
|
||||||
|
hooks:
|
||||||
|
- id: hadolint
|
||||||
|
- repo: https://github.com/adrienverge/yamllint.git
|
||||||
|
rev: v1.33.0
|
||||||
|
hooks:
|
||||||
|
- id: yamllint
|
||||||
|
args: [--strict, -c=./.yamllint.yml]
|
|
@ -0,0 +1,41 @@
|
||||||
|
when:
|
||||||
|
- event: [cron]
|
||||||
|
cron: govulncheck
|
||||||
|
- event: [pull_request]
|
||||||
|
- event: push
|
||||||
|
branch:
|
||||||
|
- ${CI_REPO_DEFAULT_BRANCH}
|
||||||
|
|
||||||
|
variables:
|
||||||
|
- &go_image 'golang:1.21'
|
||||||
|
|
||||||
|
steps:
|
||||||
|
govulncheck:
|
||||||
|
image: *go_image
|
||||||
|
pull: true
|
||||||
|
commands:
|
||||||
|
- make generate
|
||||||
|
- go install golang.org/x/vuln/cmd/govulncheck@latest
|
||||||
|
- govulncheck ./...
|
||||||
|
|
||||||
|
notify:
|
||||||
|
image: deblan/woodpecker-email
|
||||||
|
settings:
|
||||||
|
from:
|
||||||
|
from_secret: email_from
|
||||||
|
from.name: Woodpecker
|
||||||
|
host:
|
||||||
|
from_secret: email_host
|
||||||
|
username:
|
||||||
|
from_secret: email_username
|
||||||
|
password:
|
||||||
|
from_secret: email_password
|
||||||
|
recipients:
|
||||||
|
- notifications@dwysokinski.me
|
||||||
|
recipients_only: true
|
||||||
|
subject:
|
||||||
|
"[govulncheck - {{ build.status }}] {{ repo.owner }}/{{ repo.name }}
|
||||||
|
({{ build.branch }} - {{ truncate build.commit 8 }})"
|
||||||
|
when:
|
||||||
|
status: [success, failure]
|
||||||
|
event: cron
|
|
@ -0,0 +1,71 @@
|
||||||
|
when:
|
||||||
|
- event: [pull_request]
|
||||||
|
- event: push
|
||||||
|
branch:
|
||||||
|
- ${CI_REPO_DEFAULT_BRANCH}
|
||||||
|
|
||||||
|
services:
|
||||||
|
database:
|
||||||
|
image: postgres:14
|
||||||
|
pull: true
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: twhelp
|
||||||
|
POSTGRES_PASSWORD: twhelp
|
||||||
|
|
||||||
|
variables:
|
||||||
|
- &go_image 'golang:1.21'
|
||||||
|
|
||||||
|
steps:
|
||||||
|
generate:
|
||||||
|
image: *go_image
|
||||||
|
pull: true
|
||||||
|
commands:
|
||||||
|
- go mod download
|
||||||
|
- make generate
|
||||||
|
|
||||||
|
test:
|
||||||
|
image: *go_image
|
||||||
|
group: test
|
||||||
|
pull: true
|
||||||
|
environment:
|
||||||
|
TESTS_POSTGRES_CONNECTION_STRING:
|
||||||
|
postgres://postgres:twhelp@database:5432/twhelp?sslmode=disable
|
||||||
|
commands:
|
||||||
|
- go test -race -coverprofile=coverage.txt -covermode=atomic ./...
|
||||||
|
|
||||||
|
lint:
|
||||||
|
image: golangci/golangci-lint:v1.55
|
||||||
|
pull: true
|
||||||
|
group: test
|
||||||
|
commands:
|
||||||
|
- golangci-lint run
|
||||||
|
|
||||||
|
check-go-mod:
|
||||||
|
image: *go_image
|
||||||
|
group: test
|
||||||
|
pull: true
|
||||||
|
commands:
|
||||||
|
- go mod tidy
|
||||||
|
- git diff --exit-code go.mod
|
||||||
|
|
||||||
|
hadolint:
|
||||||
|
group: test
|
||||||
|
image: hadolint/hadolint:2.12.0-debian
|
||||||
|
commands:
|
||||||
|
- hadolint build/docker/twhelp/prod/Dockerfile build/docker/twhelp/dev/Dockerfile
|
||||||
|
when:
|
||||||
|
- path:
|
||||||
|
- build/docker/twhelp/prod/Dockerfile
|
||||||
|
- build/docker/twhelp/dev/Dockerfile
|
||||||
|
|
||||||
|
yamllint:
|
||||||
|
group: test
|
||||||
|
image: cytopia/yamllint:1
|
||||||
|
pull: true
|
||||||
|
commands:
|
||||||
|
- yamllint --strict .
|
||||||
|
when:
|
||||||
|
- path:
|
||||||
|
include:
|
||||||
|
- "**/*.yaml"
|
||||||
|
- "**/*.yml"
|
|
@ -0,0 +1,15 @@
|
||||||
|
extends: default
|
||||||
|
|
||||||
|
rules:
|
||||||
|
line-length:
|
||||||
|
max: 120
|
||||||
|
level: error
|
||||||
|
document-start: disable
|
||||||
|
truthy:
|
||||||
|
level: error
|
||||||
|
comments:
|
||||||
|
min-spaces-from-content: 1
|
||||||
|
quoted-strings:
|
||||||
|
level: warning
|
||||||
|
required: only-when-needed
|
||||||
|
quote-type: double
|
|
@ -0,0 +1,30 @@
|
||||||
|
GOOS=$(shell go env GOOS)
|
||||||
|
GOARCH=$(shell go env GOARCH)
|
||||||
|
GOBIN=$(shell go env GOBIN)
|
||||||
|
ifeq ($(GOBIN),)
|
||||||
|
GOBIN := $(shell go env GOPATH)/bin
|
||||||
|
endif
|
||||||
|
OSARCH=$(shell uname -m)
|
||||||
|
GOLANGCI_LINT_PATH=$(GOBIN)/golangci-lint
|
||||||
|
|
||||||
|
.PHONY: install-git-hooks
|
||||||
|
install-git-hooks:
|
||||||
|
@echo "Installing git hooks..."
|
||||||
|
pre-commit install --hook-type pre-commit
|
||||||
|
pre-commit install --hook-type commit-msg
|
||||||
|
|
||||||
|
.PHONY: install-golangci-lint
|
||||||
|
install-golangci-lint:
|
||||||
|
@echo "Installing github.com/golangci/golangci-lint..."
|
||||||
|
@(test -f $(GOLANGCI_LINT_PATH) && echo "github.com/golangci/golangci-lint is already installed. Skipping...") || curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(GOBIN) v1.55.2
|
||||||
|
|
||||||
|
.PHONY: install-tools
|
||||||
|
install-tools: install-golangci-lint
|
||||||
|
|
||||||
|
.PHONY: install
|
||||||
|
install: install-tools install-git-hooks
|
||||||
|
|
||||||
|
.PHONY: generate
|
||||||
|
generate:
|
||||||
|
@echo "Running go generate..."
|
||||||
|
go generate ./...
|
|
@ -0,0 +1,17 @@
|
||||||
|
FROM golang:1.21 as builder
|
||||||
|
WORKDIR /app
|
||||||
|
COPY go.mod go.sum ./
|
||||||
|
RUN go mod download
|
||||||
|
COPY . .
|
||||||
|
# `skaffold debug` sets SKAFFOLD_GO_GCFLAGS to disable compiler optimizations
|
||||||
|
ARG SKAFFOLD_GO_GCFLAGS
|
||||||
|
RUN go build -gcflags="${SKAFFOLD_GO_GCFLAGS}" -o twhelp ./cmd/twhelp
|
||||||
|
|
||||||
|
FROM ubuntu:22.04
|
||||||
|
# Define GOTRACEBACK to mark this container as using the Go language runtime
|
||||||
|
# for `skaffold debug` (https://skaffold.dev/docs/workflows/debug/).
|
||||||
|
WORKDIR /root
|
||||||
|
ENV GOTRACEBACK=single
|
||||||
|
RUN apt update && apt install -y ca-certificates tzdata
|
||||||
|
COPY --from=builder /app/twhelp .
|
||||||
|
ENTRYPOINT ["./twhelp"]
|
|
@ -0,0 +1,22 @@
|
||||||
|
FROM golang:1.21.5-alpine3.19 AS builder
|
||||||
|
|
||||||
|
WORKDIR /twhelp
|
||||||
|
|
||||||
|
COPY ../../../../go.mod go.sum ./
|
||||||
|
RUN go mod download && apk --no-cache add make
|
||||||
|
|
||||||
|
COPY ../../../.. .
|
||||||
|
RUN make generate
|
||||||
|
ARG CI_COMMIT_TAG="development"
|
||||||
|
RUN CGO_ENABLED=0 go build -ldflags "-X main.version=${CI_COMMIT_TAG##v}" -trimpath -o twhelp ./cmd/twhelp
|
||||||
|
|
||||||
|
######## Start a new stage from scratch #######
|
||||||
|
FROM alpine:3.19
|
||||||
|
|
||||||
|
RUN apk --no-cache add ca-certificates tzdata
|
||||||
|
|
||||||
|
COPY --from=builder /twhelp/twhelp /usr/bin/
|
||||||
|
|
||||||
|
EXPOSE 9234/tcp
|
||||||
|
|
||||||
|
ENTRYPOINT ["twhelp"]
|
|
@ -0,0 +1,14 @@
|
||||||
|
module gitea.dwysokinski.me/twhelp/corev3
|
||||||
|
|
||||||
|
go 1.21
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/elliotchance/phpserialize v1.3.3
|
||||||
|
github.com/stretchr/testify v1.8.4
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
)
|
|
@ -0,0 +1,12 @@
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/elliotchance/phpserialize v1.3.3 h1:hV4QVmGdCiYgoBbw+ADt6fNgyZ2mYX0OgpnON1adTCM=
|
||||||
|
github.com/elliotchance/phpserialize v1.3.3/go.mod h1:gt7XX9+ETUcLXbtTKEuyrqW3lcLUAeS/AnGZ2e49TZs=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
@ -0,0 +1,42 @@
|
||||||
|
package tw
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Building struct {
|
||||||
|
MaxLevel int `xml:"max_level"`
|
||||||
|
MinLevel int `xml:"min_level"`
|
||||||
|
Wood int `xml:"wood"`
|
||||||
|
Stone int `xml:"stone"`
|
||||||
|
Iron int `xml:"iron"`
|
||||||
|
Pop int `xml:"pop"`
|
||||||
|
WoodFactor float64 `xml:"wood_factor"`
|
||||||
|
StoneFactor float64 `xml:"stone_factor"`
|
||||||
|
IronFactor float64 `xml:"iron_factor"`
|
||||||
|
PopFactor float64 `xml:"pop_factor"`
|
||||||
|
BuildTime float64 `xml:"build_time"`
|
||||||
|
BuildTimeFactor float64 `xml:"build_time_factor"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type BuildingInfo struct {
|
||||||
|
XMLName xml.Name `xml:"config"`
|
||||||
|
Text string `xml:",chardata"`
|
||||||
|
Main Building `xml:"main"`
|
||||||
|
Barracks Building `xml:"barracks"`
|
||||||
|
Stable Building `xml:"stable"`
|
||||||
|
Garage Building `xml:"garage"`
|
||||||
|
Watchtower Building `xml:"watchtower"`
|
||||||
|
Snob Building `xml:"snob"`
|
||||||
|
Smith Building `xml:"smith"`
|
||||||
|
Place Building `xml:"place"`
|
||||||
|
Statue Building `xml:"statue"`
|
||||||
|
Market Building `xml:"market"`
|
||||||
|
Wood Building `xml:"wood"`
|
||||||
|
Stone Building `xml:"stone"`
|
||||||
|
Iron Building `xml:"iron"`
|
||||||
|
Farm Building `xml:"farm"`
|
||||||
|
Storage Building `xml:"storage"`
|
||||||
|
Hide Building `xml:"hide"`
|
||||||
|
Wall Building `xml:"wall"`
|
||||||
|
}
|
|
@ -0,0 +1,804 @@
|
||||||
|
package tw
|
||||||
|
|
||||||
|
import (
|
||||||
|
"cmp"
|
||||||
|
"context"
|
||||||
|
"encoding/csv"
|
||||||
|
"encoding/xml"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"slices"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/elliotchance/phpserialize"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
endpointPlayers = "/map/player.txt"
|
||||||
|
endpointTribes = "/map/ally.txt"
|
||||||
|
endpointVillages = "/map/village.txt"
|
||||||
|
endpointPlayersODA = "/map/kill_att.txt"
|
||||||
|
endpointPlayersODD = "/map/kill_def.txt"
|
||||||
|
endpointPlayersODS = "/map/kill_sup.txt"
|
||||||
|
endpointPlayersOD = "/map/kill_all.txt"
|
||||||
|
endpointTribesODA = "/map/kill_att_tribe.txt"
|
||||||
|
endpointTribesODD = "/map/kill_def_tribe.txt"
|
||||||
|
endpointTribesOD = "/map/kill_all_tribe.txt"
|
||||||
|
endpointEnnoblements = "/map/conquer_extended.txt"
|
||||||
|
endpointInterface = "/interface.php"
|
||||||
|
endpointGetServers = "/backend/get_servers.php"
|
||||||
|
endpointGame = "/game.php"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
queryConfig = "func=get_config"
|
||||||
|
queryUnitInfo = "func=get_unit_info"
|
||||||
|
queryBuildingInfo = "func=get_building_info"
|
||||||
|
queryInterfaceEnnoblements = "func=get_conquer_extended&since=%d"
|
||||||
|
queryPlayerProfile = "screen=info_player&id=%d"
|
||||||
|
queryTribeProfile = "screen=info_ally&id=%d"
|
||||||
|
queryVillageProfile = "screen=info_village&id=%d"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
client *http.Client
|
||||||
|
userAgent string
|
||||||
|
ennoblementsUseInterfaceFunc func(since time.Time) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClient(opts ...ClientOption) *Client {
|
||||||
|
cfg := newClientConfig(opts...)
|
||||||
|
return &Client{
|
||||||
|
client: cfg.client,
|
||||||
|
userAgent: cfg.userAgent,
|
||||||
|
ennoblementsUseInterfaceFunc: cfg.ennoblementsUseInterfaceFunc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidServerKey = errors.New("invalid server key")
|
||||||
|
ErrInvalidServerURL = errors.New("invalid server URL")
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Client) GetOpenServers(ctx context.Context, rawBaseURL string) ([]Server, error) {
|
||||||
|
baseURL, err := url.ParseRequestURI(rawBaseURL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
u := buildURL(baseURL, endpointGetServers)
|
||||||
|
resp, err := c.get(ctx, u)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = resp.Body.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
b, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("%s: couldn't read response body: %w", u, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
m, err := phpserialize.UnmarshalAssociativeArray(b)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("%s: couldn't unmarshal response body: %w", u, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
servers := make([]Server, 0, len(m))
|
||||||
|
for key, val := range m {
|
||||||
|
keyStr, ok := key.(string)
|
||||||
|
if !ok || keyStr == "" {
|
||||||
|
return nil, fmt.Errorf("%s: parsing '%v': %w", u, key, ErrInvalidServerKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
urlStr, ok := val.(string)
|
||||||
|
if !ok || urlStr == "" {
|
||||||
|
return nil, fmt.Errorf("%s: parsing '%v': %w", u, val, ErrInvalidServerURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
servers = append(servers, Server{
|
||||||
|
Key: keyStr,
|
||||||
|
URL: urlStr,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return servers, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) GetServerConfig(ctx context.Context, rawBaseURL string) (ServerConfig, error) {
|
||||||
|
baseURL, err := url.ParseRequestURI(rawBaseURL)
|
||||||
|
if err != nil {
|
||||||
|
return ServerConfig{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var cfg ServerConfig
|
||||||
|
|
||||||
|
if err = c.getXML(ctx, buildURLWithQuery(baseURL, endpointInterface, queryConfig), &cfg); err != nil {
|
||||||
|
return ServerConfig{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return cfg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) GetBuildingInfo(ctx context.Context, rawBaseURL string) (BuildingInfo, error) {
|
||||||
|
baseURL, err := url.ParseRequestURI(rawBaseURL)
|
||||||
|
if err != nil {
|
||||||
|
return BuildingInfo{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var info BuildingInfo
|
||||||
|
|
||||||
|
if err = c.getXML(ctx, buildURLWithQuery(baseURL, endpointInterface, queryBuildingInfo), &info); err != nil {
|
||||||
|
return BuildingInfo{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return info, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) GetUnitInfo(ctx context.Context, rawBaseURL string) (UnitInfo, error) {
|
||||||
|
baseURL, err := url.ParseRequestURI(rawBaseURL)
|
||||||
|
if err != nil {
|
||||||
|
return UnitInfo{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var info UnitInfo
|
||||||
|
|
||||||
|
if err = c.getXML(ctx, buildURLWithQuery(baseURL, endpointInterface, queryUnitInfo), &info); err != nil {
|
||||||
|
return UnitInfo{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return info, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) GetTribes(ctx context.Context, rawBaseURL string) ([]Tribe, error) {
|
||||||
|
baseURL, err := url.ParseRequestURI(rawBaseURL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
od, err := c.getOD(ctx, baseURL, true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := c.get(ctx, buildURL(baseURL, endpointTribes))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = resp.Body.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
return tribeCSVParser{
|
||||||
|
r: resp.Body,
|
||||||
|
baseURL: baseURL,
|
||||||
|
od: od,
|
||||||
|
}.parse()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) GetPlayers(ctx context.Context, rawBaseURL string) ([]Player, error) {
|
||||||
|
baseURL, err := url.ParseRequestURI(rawBaseURL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
od, err := c.getOD(ctx, baseURL, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := c.get(ctx, buildURL(baseURL, endpointPlayers))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = resp.Body.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
return playerCSVParser{
|
||||||
|
r: resp.Body,
|
||||||
|
baseURL: baseURL,
|
||||||
|
od: od,
|
||||||
|
}.parse()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) getOD(ctx context.Context, baseURL *url.URL, tribe bool) (map[int]OpponentsDefeated, error) {
|
||||||
|
m := make(map[int]OpponentsDefeated)
|
||||||
|
urls := buildODURLs(baseURL, tribe)
|
||||||
|
|
||||||
|
for _, u := range urls {
|
||||||
|
if u == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
records, err := c.getSingleODFile(ctx, u)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, rec := range records {
|
||||||
|
od := m[rec.ID]
|
||||||
|
|
||||||
|
switch u {
|
||||||
|
case urls[0]:
|
||||||
|
od.RankTotal = rec.Rank
|
||||||
|
od.ScoreTotal = rec.Score
|
||||||
|
case urls[1]:
|
||||||
|
od.RankAtt = rec.Rank
|
||||||
|
od.ScoreAtt = rec.Score
|
||||||
|
case urls[2]:
|
||||||
|
od.RankDef = rec.Rank
|
||||||
|
od.ScoreDef = rec.Score
|
||||||
|
case urls[3]:
|
||||||
|
od.RankSup = rec.Rank
|
||||||
|
od.ScoreSup = rec.Score
|
||||||
|
}
|
||||||
|
|
||||||
|
m[rec.ID] = od
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) getSingleODFile(ctx context.Context, u *url.URL) ([]odRecord, error) {
|
||||||
|
resp, err := c.get(ctx, u)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = resp.Body.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
return odCSVParser{
|
||||||
|
r: resp.Body,
|
||||||
|
}.parse()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) GetVillages(ctx context.Context, rawBaseURL string) ([]Village, error) {
|
||||||
|
baseURL, err := url.ParseRequestURI(rawBaseURL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := c.get(ctx, buildURL(baseURL, endpointVillages))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = resp.Body.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
return villageCSVParser{
|
||||||
|
r: resp.Body,
|
||||||
|
baseURL: baseURL,
|
||||||
|
}.parse()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) GetEnnoblements(ctx context.Context, rawBaseURL string, since time.Time) ([]Ennoblement, error) {
|
||||||
|
baseURL, err := url.ParseRequestURI(rawBaseURL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
u := buildURL(baseURL, endpointEnnoblements)
|
||||||
|
if c.ennoblementsUseInterfaceFunc(since) {
|
||||||
|
u = buildURLWithQuery(baseURL, endpointInterface, fmt.Sprintf(queryInterfaceEnnoblements, since.Unix()))
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := c.get(ctx, u)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = resp.Body.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
return ennoblementCSVParser{
|
||||||
|
r: resp.Body,
|
||||||
|
since: since,
|
||||||
|
}.parse()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) getXML(ctx context.Context, u *url.URL, v any) error {
|
||||||
|
resp, err := c.get(ctx, u)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_, _ = io.Copy(io.Discard, resp.Body)
|
||||||
|
_ = resp.Body.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
if err = xml.NewDecoder(resp.Body).Decode(v); err != nil {
|
||||||
|
return fmt.Errorf("%s: %w", u, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) get(ctx context.Context, u *url.URL) (*http.Response, error) {
|
||||||
|
urlStr := u.String()
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, urlStr, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("%s: %w", urlStr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// headers
|
||||||
|
req.Header.Set("User-Agent", c.userAgent)
|
||||||
|
|
||||||
|
resp, err := c.client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("%s: %w", urlStr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
_, _ = io.Copy(io.Discard, resp.Body)
|
||||||
|
_ = resp.Body.Close()
|
||||||
|
return nil, fmt.Errorf("%s: got non-ok HTTP status: %d", urlStr, resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type tribeCSVParser struct {
|
||||||
|
r io.Reader
|
||||||
|
baseURL *url.URL
|
||||||
|
od map[int]OpponentsDefeated
|
||||||
|
}
|
||||||
|
|
||||||
|
const fieldsPerRecordTribe = 8
|
||||||
|
|
||||||
|
func (t tribeCSVParser) parse() ([]Tribe, error) {
|
||||||
|
csvR := newCSVReader(t.r, fieldsPerRecordTribe)
|
||||||
|
|
||||||
|
var tribes []Tribe
|
||||||
|
for {
|
||||||
|
rec, err := csvR.Read()
|
||||||
|
if errors.Is(err, io.EOF) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tribe, err := t.parseRecord(rec)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tribes = append(tribes, tribe)
|
||||||
|
}
|
||||||
|
|
||||||
|
slices.SortFunc(tribes, func(a, b Tribe) int {
|
||||||
|
return cmp.Compare(a.ID, b.ID)
|
||||||
|
})
|
||||||
|
|
||||||
|
return tribes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t tribeCSVParser) parseRecord(record []string) (Tribe, error) {
|
||||||
|
var err error
|
||||||
|
var tribe Tribe
|
||||||
|
|
||||||
|
// $id, $name, $tag, $members, $villages, $points, $all_points, $rank
|
||||||
|
|
||||||
|
tribe.ID, err = strconv.Atoi(record[0])
|
||||||
|
if err != nil {
|
||||||
|
return Tribe{}, ParseError{Err: err, Str: record[0], Field: "Tribe.ID"}
|
||||||
|
}
|
||||||
|
|
||||||
|
tribe.Name, err = url.QueryUnescape(record[1])
|
||||||
|
if err != nil {
|
||||||
|
return Tribe{}, ParseError{Err: err, Str: record[1], Field: "Tribe.Name"}
|
||||||
|
}
|
||||||
|
|
||||||
|
tribe.Tag, err = url.QueryUnescape(record[2])
|
||||||
|
if err != nil {
|
||||||
|
return Tribe{}, ParseError{Err: err, Str: record[2], Field: "Tribe.Tag"}
|
||||||
|
}
|
||||||
|
|
||||||
|
tribe.NumMembers, err = strconv.Atoi(record[3])
|
||||||
|
if err != nil {
|
||||||
|
return Tribe{}, ParseError{Err: err, Str: record[3], Field: "Tribe.NumMembers"}
|
||||||
|
}
|
||||||
|
|
||||||
|
tribe.NumVillages, err = strconv.Atoi(record[4])
|
||||||
|
if err != nil {
|
||||||
|
return Tribe{}, ParseError{Err: err, Str: record[4], Field: "Tribe.NumVillages"}
|
||||||
|
}
|
||||||
|
|
||||||
|
tribe.Points, err = strconv.Atoi(record[5])
|
||||||
|
if err != nil {
|
||||||
|
return Tribe{}, ParseError{Err: err, Str: record[5], Field: "Tribe.Points"}
|
||||||
|
}
|
||||||
|
|
||||||
|
tribe.AllPoints, err = strconv.Atoi(record[6])
|
||||||
|
if err != nil {
|
||||||
|
return Tribe{}, ParseError{Err: err, Str: record[6], Field: "Tribe.AllPoints"}
|
||||||
|
}
|
||||||
|
|
||||||
|
tribe.Rank, err = strconv.Atoi(record[7])
|
||||||
|
if err != nil {
|
||||||
|
return Tribe{}, ParseError{Err: err, Str: record[7], Field: "Tribe.Rank"}
|
||||||
|
}
|
||||||
|
|
||||||
|
tribe.OpponentsDefeated = t.od[tribe.ID]
|
||||||
|
|
||||||
|
tribe.ProfileURL = buildURLWithQuery(t.baseURL, endpointGame, fmt.Sprintf(queryTribeProfile, tribe.ID)).String()
|
||||||
|
|
||||||
|
return tribe, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type playerCSVParser struct {
|
||||||
|
r io.Reader
|
||||||
|
od map[int]OpponentsDefeated
|
||||||
|
baseURL *url.URL
|
||||||
|
}
|
||||||
|
|
||||||
|
const fieldsPerRecordPlayer = 6
|
||||||
|
|
||||||
|
func (p playerCSVParser) parse() ([]Player, error) {
|
||||||
|
csvR := newCSVReader(p.r, fieldsPerRecordPlayer)
|
||||||
|
|
||||||
|
var players []Player
|
||||||
|
for {
|
||||||
|
rec, err := csvR.Read()
|
||||||
|
if errors.Is(err, io.EOF) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
player, err := p.parseRecord(rec)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
players = append(players, player)
|
||||||
|
}
|
||||||
|
|
||||||
|
slices.SortFunc(players, func(a, b Player) int {
|
||||||
|
return cmp.Compare(a.ID, b.ID)
|
||||||
|
})
|
||||||
|
|
||||||
|
return players, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p playerCSVParser) parseRecord(record []string) (Player, error) {
|
||||||
|
var err error
|
||||||
|
var player Player
|
||||||
|
|
||||||
|
// $id, $name, $ally, $villages, $points, $rank
|
||||||
|
|
||||||
|
player.ID, err = strconv.Atoi(record[0])
|
||||||
|
if err != nil {
|
||||||
|
return Player{}, ParseError{Err: err, Str: record[0], Field: "Player.ID"}
|
||||||
|
}
|
||||||
|
|
||||||
|
player.Name, err = url.QueryUnescape(record[1])
|
||||||
|
if err != nil {
|
||||||
|
return Player{}, ParseError{Err: err, Str: record[1], Field: "Player.Name"}
|
||||||
|
}
|
||||||
|
|
||||||
|
player.TribeID, err = strconv.Atoi(record[2])
|
||||||
|
if err != nil {
|
||||||
|
return Player{}, ParseError{Err: err, Str: record[2], Field: "Player.TribeID"}
|
||||||
|
}
|
||||||
|
|
||||||
|
player.NumVillages, err = strconv.Atoi(record[3])
|
||||||
|
if err != nil {
|
||||||
|
return Player{}, ParseError{Err: err, Str: record[3], Field: "Player.NumVillages"}
|
||||||
|
}
|
||||||
|
|
||||||
|
player.Points, err = strconv.Atoi(record[4])
|
||||||
|
if err != nil {
|
||||||
|
return Player{}, ParseError{Err: err, Str: record[4], Field: "Player.Points"}
|
||||||
|
}
|
||||||
|
|
||||||
|
player.Rank, err = strconv.Atoi(record[5])
|
||||||
|
if err != nil {
|
||||||
|
return Player{}, ParseError{Err: err, Str: record[5], Field: "Player.Rank"}
|
||||||
|
}
|
||||||
|
|
||||||
|
player.OpponentsDefeated = p.od[player.ID]
|
||||||
|
|
||||||
|
player.ProfileURL = buildURLWithQuery(p.baseURL, endpointGame, fmt.Sprintf(queryPlayerProfile, player.ID)).String()
|
||||||
|
|
||||||
|
return player, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type villageCSVParser struct {
|
||||||
|
r io.Reader
|
||||||
|
baseURL *url.URL
|
||||||
|
}
|
||||||
|
|
||||||
|
const fieldsPerRecordVillage = 7
|
||||||
|
|
||||||
|
func (v villageCSVParser) parse() ([]Village, error) {
|
||||||
|
csvR := newCSVReader(v.r, fieldsPerRecordVillage)
|
||||||
|
|
||||||
|
var villages []Village
|
||||||
|
for {
|
||||||
|
rec, err := csvR.Read()
|
||||||
|
if errors.Is(err, io.EOF) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
village, err := v.parseRecord(rec)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
villages = append(villages, village)
|
||||||
|
}
|
||||||
|
|
||||||
|
slices.SortFunc(villages, func(a, b Village) int {
|
||||||
|
return cmp.Compare(a.ID, b.ID)
|
||||||
|
})
|
||||||
|
|
||||||
|
return villages, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v villageCSVParser) parseRecord(record []string) (Village, error) {
|
||||||
|
var err error
|
||||||
|
var village Village
|
||||||
|
|
||||||
|
// $id, $name, $x, $y, $player, $points, $bonus_id
|
||||||
|
|
||||||
|
village.ID, err = strconv.Atoi(record[0])
|
||||||
|
if err != nil {
|
||||||
|
return Village{}, ParseError{Err: err, Str: record[0], Field: "Village.ID"}
|
||||||
|
}
|
||||||
|
|
||||||
|
village.Name, err = url.QueryUnescape(record[1])
|
||||||
|
if err != nil {
|
||||||
|
return Village{}, ParseError{Err: err, Str: record[1], Field: "Village.Name"}
|
||||||
|
}
|
||||||
|
|
||||||
|
village.X, err = strconv.Atoi(record[2])
|
||||||
|
if err != nil {
|
||||||
|
return Village{}, ParseError{Err: err, Str: record[2], Field: "Village.X"}
|
||||||
|
}
|
||||||
|
|
||||||
|
village.Y, err = strconv.Atoi(record[3])
|
||||||
|
if err != nil {
|
||||||
|
return Village{}, ParseError{Err: err, Str: record[3], Field: "Village.Y"}
|
||||||
|
}
|
||||||
|
|
||||||
|
village.PlayerID, err = strconv.Atoi(record[4])
|
||||||
|
if err != nil {
|
||||||
|
return Village{}, ParseError{Err: err, Str: record[4], Field: "Village.PlayerID"}
|
||||||
|
}
|
||||||
|
|
||||||
|
village.Points, err = strconv.Atoi(record[5])
|
||||||
|
if err != nil {
|
||||||
|
return Village{}, ParseError{Err: err, Str: record[5], Field: "Village.Points"}
|
||||||
|
}
|
||||||
|
|
||||||
|
village.Bonus, err = strconv.Atoi(record[6])
|
||||||
|
if err != nil {
|
||||||
|
return Village{}, ParseError{Err: err, Str: record[6], Field: "Village.Bonus"}
|
||||||
|
}
|
||||||
|
|
||||||
|
village.Continent = v.buildContinent(record, village.X, village.Y)
|
||||||
|
|
||||||
|
village.ProfileURL = buildURLWithQuery(v.baseURL, endpointGame, fmt.Sprintf(queryVillageProfile, village.ID)).String()
|
||||||
|
|
||||||
|
return village, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v villageCSVParser) buildContinent(record []string, x, y int) string {
|
||||||
|
continent := "K"
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case x < 100 && y < 100:
|
||||||
|
continent += "0"
|
||||||
|
case x > 100 && y < 100:
|
||||||
|
continent += string(record[2][0])
|
||||||
|
case x < 100 && y > 100:
|
||||||
|
continent += string(record[3][0]) + "0"
|
||||||
|
default:
|
||||||
|
continent += string(record[3][0]) + string(record[2][0])
|
||||||
|
}
|
||||||
|
|
||||||
|
return continent
|
||||||
|
}
|
||||||
|
|
||||||
|
type odCSVParser struct {
|
||||||
|
r io.Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
type odRecord struct {
|
||||||
|
ID int
|
||||||
|
Rank int
|
||||||
|
Score int
|
||||||
|
}
|
||||||
|
|
||||||
|
const fieldsPerRecordOD = 3
|
||||||
|
|
||||||
|
func (o odCSVParser) parse() ([]odRecord, error) {
|
||||||
|
csvR := newCSVReader(o.r, fieldsPerRecordOD)
|
||||||
|
|
||||||
|
var odRecords []odRecord
|
||||||
|
for {
|
||||||
|
rec, err := csvR.Read()
|
||||||
|
if errors.Is(err, io.EOF) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
od, err := o.parseRecord(rec)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
odRecords = append(odRecords, od)
|
||||||
|
}
|
||||||
|
|
||||||
|
return odRecords, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o odCSVParser) parseRecord(record []string) (odRecord, error) {
|
||||||
|
var err error
|
||||||
|
var rec odRecord
|
||||||
|
|
||||||
|
// $rank, $id, $score
|
||||||
|
|
||||||
|
rec.Rank, err = strconv.Atoi(record[0])
|
||||||
|
if err != nil {
|
||||||
|
return odRecord{}, ParseError{Err: err, Str: record[0], Field: "odRecord.Rank"}
|
||||||
|
}
|
||||||
|
|
||||||
|
rec.ID, err = strconv.Atoi(record[1])
|
||||||
|
if err != nil {
|
||||||
|
return odRecord{}, ParseError{Err: err, Str: record[1], Field: "odRecord.ID"}
|
||||||
|
}
|
||||||
|
|
||||||
|
rec.Score, err = strconv.Atoi(record[2])
|
||||||
|
if err != nil {
|
||||||
|
return odRecord{}, ParseError{Err: err, Str: record[2], Field: "odRecord.Score"}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rec, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ennoblementCSVParser struct {
|
||||||
|
r io.Reader
|
||||||
|
since time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
const fieldsPerRecordEnnoblement = 7
|
||||||
|
|
||||||
|
func (e ennoblementCSVParser) parse() ([]Ennoblement, error) {
|
||||||
|
csvR := newCSVReader(e.r, fieldsPerRecordEnnoblement)
|
||||||
|
|
||||||
|
var ennoblements []Ennoblement
|
||||||
|
for {
|
||||||
|
rec, err := csvR.Read()
|
||||||
|
if errors.Is(err, io.EOF) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ennoblement, err := e.parseRecord(rec)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ennoblement.CreatedAt.Before(e.since) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ennoblements = append(ennoblements, ennoblement)
|
||||||
|
}
|
||||||
|
|
||||||
|
slices.SortFunc(ennoblements, func(a, b Ennoblement) int {
|
||||||
|
return a.CreatedAt.Compare(b.CreatedAt)
|
||||||
|
})
|
||||||
|
|
||||||
|
return ennoblements, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ennoblementCSVParser) parseRecord(record []string) (Ennoblement, error) {
|
||||||
|
var err error
|
||||||
|
var ennoblement Ennoblement
|
||||||
|
|
||||||
|
// $village_id, $unix_timestamp, $new_owner, $old_owner, $old_tribe_id, $new_tribe_id, $points
|
||||||
|
|
||||||
|
ennoblement.VillageID, err = strconv.Atoi(record[0])
|
||||||
|
if err != nil {
|
||||||
|
return Ennoblement{}, ParseError{Err: err, Str: record[0], Field: "Ennoblement.VillageID"}
|
||||||
|
}
|
||||||
|
|
||||||
|
ennoblement.CreatedAt, err = e.parseTimestamp(record[1])
|
||||||
|
if err != nil {
|
||||||
|
return Ennoblement{}, ParseError{Err: err, Str: record[1], Field: "Ennoblement.CreatedAt"}
|
||||||
|
}
|
||||||
|
|
||||||
|
ennoblement.NewOwnerID, err = strconv.Atoi(record[2])
|
||||||
|
if err != nil {
|
||||||
|
return Ennoblement{}, ParseError{Err: err, Str: record[2], Field: "Ennoblement.NewOwnerID"}
|
||||||
|
}
|
||||||
|
|
||||||
|
ennoblement.OldOwnerID, err = strconv.Atoi(record[3])
|
||||||
|
if err != nil {
|
||||||
|
return Ennoblement{}, ParseError{Err: err, Str: record[3], Field: "Ennoblement.OldOwnerID"}
|
||||||
|
}
|
||||||
|
|
||||||
|
ennoblement.OldTribeID, err = strconv.Atoi(record[4])
|
||||||
|
if err != nil {
|
||||||
|
return Ennoblement{}, ParseError{Err: err, Str: record[4], Field: "Ennoblement.OldTribeID"}
|
||||||
|
}
|
||||||
|
|
||||||
|
ennoblement.NewTribeID, err = strconv.Atoi(record[5])
|
||||||
|
if err != nil {
|
||||||
|
return Ennoblement{}, ParseError{Err: err, Str: record[5], Field: "Ennoblement.NewTribeID"}
|
||||||
|
}
|
||||||
|
|
||||||
|
ennoblement.Points, err = strconv.Atoi(record[6])
|
||||||
|
if err != nil {
|
||||||
|
return Ennoblement{}, ParseError{Err: err, Str: record[6], Field: "Ennoblement.Points"}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ennoblement, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ennoblementCSVParser) parseTimestamp(s string) (time.Time, error) {
|
||||||
|
timestamp, err := strconv.ParseInt(s, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return time.Time{}, err
|
||||||
|
}
|
||||||
|
return time.Unix(timestamp, 0), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCSVReader(r io.Reader, fieldsPerRecord int) *csv.Reader {
|
||||||
|
csvR := csv.NewReader(r)
|
||||||
|
csvR.Comma = ','
|
||||||
|
csvR.FieldsPerRecord = fieldsPerRecord
|
||||||
|
csvR.ReuseRecord = true
|
||||||
|
return csvR
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:revive
|
||||||
|
func buildODURLs(base *url.URL, tribe bool) [4]*url.URL {
|
||||||
|
// there is no endpoint to get opponents defeated as supporter for tribes :(
|
||||||
|
if tribe {
|
||||||
|
return [4]*url.URL{
|
||||||
|
buildURL(base, endpointTribesOD),
|
||||||
|
buildURL(base, endpointTribesODA),
|
||||||
|
buildURL(base, endpointTribesODD),
|
||||||
|
nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [4]*url.URL{
|
||||||
|
buildURL(base, endpointPlayersOD),
|
||||||
|
buildURL(base, endpointPlayersODA),
|
||||||
|
buildURL(base, endpointPlayersODD),
|
||||||
|
buildURL(base, endpointPlayersODS),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildURL(base *url.URL, path string) *url.URL {
|
||||||
|
return buildURLWithQuery(base, path, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildURLWithQuery(base *url.URL, path, query string) *url.URL {
|
||||||
|
return &url.URL{
|
||||||
|
Scheme: base.Scheme,
|
||||||
|
Host: base.Host,
|
||||||
|
Path: path,
|
||||||
|
RawQuery: query,
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
package tw
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultUserAgent = "tribalwarshelp/development"
|
||||||
|
defaultTimeout = 10 * time.Second
|
||||||
|
)
|
||||||
|
|
||||||
|
type EnnoblementsUseInterfaceFunc func(since time.Time) bool
|
||||||
|
|
||||||
|
type clientConfig struct {
|
||||||
|
userAgent string
|
||||||
|
client *http.Client
|
||||||
|
ennoblementsUseInterfaceFunc EnnoblementsUseInterfaceFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClientOption func(c *clientConfig)
|
||||||
|
|
||||||
|
func newClientConfig(opts ...ClientOption) *clientConfig {
|
||||||
|
cfg := &clientConfig{
|
||||||
|
userAgent: defaultUserAgent,
|
||||||
|
client: &http.Client{
|
||||||
|
Timeout: defaultTimeout,
|
||||||
|
},
|
||||||
|
ennoblementsUseInterfaceFunc: func(since time.Time) bool {
|
||||||
|
return since.After(time.Now().Add(-23 * time.Hour))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithUserAgent(ua string) ClientOption {
|
||||||
|
return func(cfg *clientConfig) {
|
||||||
|
cfg.userAgent = ua
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithHTTPClient(hc *http.Client) ClientOption {
|
||||||
|
return func(cfg *clientConfig) {
|
||||||
|
cfg.client = hc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithEnnoblementsUseInterfaceFunc takes a function that will be called on every Client.GetEnnoblements call
|
||||||
|
// to determine whether the client should use /interface.php?func=get_conquer_extended or /map/conquer_extended.txt
|
||||||
|
// The default function checks whether since is after now() - 23h,
|
||||||
|
// if so, it calls /map/conquer_extended.txt, otherwise /interface.php?func=get_conquer_extended.
|
||||||
|
func WithEnnoblementsUseInterfaceFunc(f EnnoblementsUseInterfaceFunc) ClientOption {
|
||||||
|
return func(cfg *clientConfig) {
|
||||||
|
cfg.ennoblementsUseInterfaceFunc = f
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,15 @@
|
||||||
|
package tw
|
||||||
|
|
||||||
|
type ParseError struct {
|
||||||
|
Field string
|
||||||
|
Str string
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ParseError) Error() string {
|
||||||
|
return e.Field + ": parsing '" + e.Str + "': " + e.Err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ParseError) Unwrap() error {
|
||||||
|
return e.Err
|
||||||
|
}
|
|
@ -0,0 +1,186 @@
|
||||||
|
package tw
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ServerConfigBuild struct {
|
||||||
|
Destroy int `xml:"destroy"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerConfigMisc struct {
|
||||||
|
KillRanking int `xml:"kill_ranking"`
|
||||||
|
Tutorial int `xml:"tutorial"`
|
||||||
|
TradeCancelTime int `xml:"trade_cancel_time"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerConfigCommands struct {
|
||||||
|
MillisArrival int `xml:"millis_arrival"`
|
||||||
|
CommandCancelTime int `xml:"command_cancel_time"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerConfigNewbie struct {
|
||||||
|
Days int `xml:"days"`
|
||||||
|
RatioDays int `xml:"ratio_days"`
|
||||||
|
Ratio int `xml:"ratio"`
|
||||||
|
RemoveNewbieVillages int `xml:"removeNewbieVillages"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type KnightNewItems int
|
||||||
|
|
||||||
|
func (k KnightNewItems) Int() int {
|
||||||
|
return int(k)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *KnightNewItems) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||||
|
var s string
|
||||||
|
|
||||||
|
if err := d.DecodeElement(&s, &start); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch strings.ToUpper(s) {
|
||||||
|
case "ON", "1":
|
||||||
|
*k = 1
|
||||||
|
case "OFF", "0", "":
|
||||||
|
*k = 0
|
||||||
|
default:
|
||||||
|
return xml.UnmarshalError(fmt.Sprintf(`KnightNewItems: parsing "%s": invalid syntax`, s))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerConfigGame struct {
|
||||||
|
BuildtimeFormula int `xml:"buildtime_formula"`
|
||||||
|
Knight int `xml:"knight"`
|
||||||
|
KnightNewItems KnightNewItems `xml:"knight_new_items"`
|
||||||
|
Archer int `xml:"archer"`
|
||||||
|
Tech int `xml:"tech"`
|
||||||
|
FarmLimit int `xml:"farm_limit"`
|
||||||
|
Church int `xml:"church"`
|
||||||
|
Watchtower int `xml:"watchtower"`
|
||||||
|
Stronghold int `xml:"stronghold"`
|
||||||
|
FakeLimit float64 `xml:"fake_limit"`
|
||||||
|
BarbarianRise float64 `xml:"barbarian_rise"`
|
||||||
|
BarbarianShrink int `xml:"barbarian_shrink"`
|
||||||
|
BarbarianMaxPoints int `xml:"barbarian_max_points"`
|
||||||
|
Scavenging int `xml:"scavenging"`
|
||||||
|
Hauls int `xml:"hauls"`
|
||||||
|
HaulsBase int `xml:"hauls_base"`
|
||||||
|
HaulsMax int `xml:"hauls_max"`
|
||||||
|
BaseProduction int `xml:"base_production"`
|
||||||
|
Event int `xml:"event"`
|
||||||
|
SuppressEvents int `xml:"suppress_events"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerConfigBuildings struct {
|
||||||
|
CustomMain int `xml:"custom_main"`
|
||||||
|
CustomFarm int `xml:"custom_farm"`
|
||||||
|
CustomStorage int `xml:"custom_storage"`
|
||||||
|
CustomPlace int `xml:"custom_place"`
|
||||||
|
CustomBarracks int `xml:"custom_barracks"`
|
||||||
|
CustomChurch int `xml:"custom_church"`
|
||||||
|
CustomSmith int `xml:"custom_smith"`
|
||||||
|
CustomWood int `xml:"custom_wood"`
|
||||||
|
CustomStone int `xml:"custom_stone"`
|
||||||
|
CustomIron int `xml:"custom_iron"`
|
||||||
|
CustomMarket int `xml:"custom_market"`
|
||||||
|
CustomStable int `xml:"custom_stable"`
|
||||||
|
CustomWall int `xml:"custom_wall"`
|
||||||
|
CustomGarage int `xml:"custom_garage"`
|
||||||
|
CustomHide int `xml:"custom_hide"`
|
||||||
|
CustomSnob int `xml:"custom_snob"`
|
||||||
|
CustomStatue int `xml:"custom_statue"`
|
||||||
|
CustomWatchtower int `xml:"custom_watchtower"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerConfigSnob struct {
|
||||||
|
Gold int `xml:"gold"`
|
||||||
|
CheapRebuild int `xml:"cheap_rebuild"`
|
||||||
|
Rise int `xml:"rise"`
|
||||||
|
MaxDist int `xml:"max_dist"`
|
||||||
|
Factor float64 `xml:"factor"`
|
||||||
|
CoinWood int `xml:"coin_wood"`
|
||||||
|
CoinStone int `xml:"coin_stone"`
|
||||||
|
CoinIron int `xml:"coin_iron"`
|
||||||
|
NoBarbConquer int `xml:"no_barb_conquer"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerConfigAlly struct {
|
||||||
|
NoHarm int `xml:"no_harm"`
|
||||||
|
NoOtherSupport int `xml:"no_other_support"`
|
||||||
|
NoOtherSupportType int `xml:"no_other_support_type"`
|
||||||
|
AllytimeSupport int `xml:"allytime_support"`
|
||||||
|
NoLeave int `xml:"no_leave"`
|
||||||
|
NoJoin int `xml:"no_join"`
|
||||||
|
Limit int `xml:"limit"`
|
||||||
|
FixedAllies int `xml:"fixed_allies"`
|
||||||
|
PointsMemberCount int `xml:"points_member_count"`
|
||||||
|
WarsMemberRequirement int `xml:"wars_member_requirement"`
|
||||||
|
WarsPointsRequirement int `xml:"wars_points_requirement"`
|
||||||
|
WarsAutoacceptDays int `xml:"wars_autoaccept_days"`
|
||||||
|
Levels int `xml:"levels"`
|
||||||
|
XpRequirements string `xml:"xp_requirements"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerConfigCoord struct {
|
||||||
|
MapSize int `xml:"map_size"`
|
||||||
|
Func int `xml:"func"`
|
||||||
|
EmptyVillages int `xml:"empty_villages"`
|
||||||
|
BonusVillages int `xml:"bonus_villages"`
|
||||||
|
BonusNew int `xml:"bonus_new"`
|
||||||
|
Inner int `xml:"inner"`
|
||||||
|
SelectStart int `xml:"select_start"`
|
||||||
|
VillageMoveWait int `xml:"village_move_wait"`
|
||||||
|
NobleRestart int `xml:"noble_restart"`
|
||||||
|
StartVillages int `xml:"start_villages"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerConfigSitter struct {
|
||||||
|
Allow int `xml:"allow"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerConfigSleep struct {
|
||||||
|
Active int `xml:"active"`
|
||||||
|
Delay int `xml:"delay"`
|
||||||
|
Min int `xml:"min"`
|
||||||
|
Max int `xml:"max"`
|
||||||
|
MinAwake int `xml:"min_awake"`
|
||||||
|
MaxAwake int `xml:"max_awake"`
|
||||||
|
WarnTime int `xml:"warn_time"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerConfigNight struct {
|
||||||
|
Active int `xml:"active"`
|
||||||
|
StartHour int `xml:"start_hour"`
|
||||||
|
EndHour int `xml:"end_hour"`
|
||||||
|
DefFactor float64 `xml:"def_factor"`
|
||||||
|
Duration int `xml:"duration"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerConfigWin struct {
|
||||||
|
Check int `xml:"check"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerConfig struct {
|
||||||
|
XMLName xml.Name `xml:"config"`
|
||||||
|
Speed float64 `xml:"speed"`
|
||||||
|
UnitSpeed float64 `xml:"unit_speed"`
|
||||||
|
Moral int `xml:"moral"`
|
||||||
|
Build ServerConfigBuild `xml:"build"`
|
||||||
|
Misc ServerConfigMisc `xml:"misc"`
|
||||||
|
Commands ServerConfigCommands `xml:"commands"`
|
||||||
|
Newbie ServerConfigNewbie `xml:"newbie"`
|
||||||
|
Game ServerConfigGame `xml:"game"`
|
||||||
|
Buildings ServerConfigBuildings `xml:"buildings"`
|
||||||
|
Snob ServerConfigSnob `xml:"snob"`
|
||||||
|
Ally ServerConfigAlly `xml:"ally"`
|
||||||
|
Coord ServerConfigCoord `xml:"coord"`
|
||||||
|
Sitter ServerConfigSitter `xml:"sitter"`
|
||||||
|
Sleep ServerConfigSleep `xml:"sleep"`
|
||||||
|
Night ServerConfigNight `xml:"night"`
|
||||||
|
Win ServerConfigWin `xml:"win"`
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
package tw_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gitea.dwysokinski.me/twhelp/corev3/internal/tw"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestKnightNewItems_UnmarshalXML(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
data string
|
||||||
|
expected tw.KnightNewItems
|
||||||
|
expectedErr error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
data: "<knight_new_items/>",
|
||||||
|
expected: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: "<knight_new_items>Off</knight_new_items>",
|
||||||
|
expected: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: "<knight_new_items>0</knight_new_items>",
|
||||||
|
expected: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: "<knight_new_items>On</knight_new_items>",
|
||||||
|
expected: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: "<knight_new_items>ON</knight_new_items>",
|
||||||
|
expected: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: "<knight_new_items>on</knight_new_items>",
|
||||||
|
expected: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: "<knight_new_items>1</knight_new_items>",
|
||||||
|
expected: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: "<knight_new_items>invalid value</knight_new_items>",
|
||||||
|
expectedErr: xml.UnmarshalError(`KnightNewItems: parsing "invalid value": invalid syntax`),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
tt := tt
|
||||||
|
|
||||||
|
t.Run(tt.data, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var res tw.KnightNewItems
|
||||||
|
require.ErrorIs(t, tt.expectedErr, xml.Unmarshal([]byte(tt.data), &res))
|
||||||
|
assert.Equal(t, tt.expected, res)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,241 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||||
|
<config>
|
||||||
|
<main>
|
||||||
|
<max_level>30</max_level>
|
||||||
|
<min_level>1</min_level>
|
||||||
|
<wood>90</wood>
|
||||||
|
<stone>80</stone>
|
||||||
|
<iron>70</iron>
|
||||||
|
<pop>5</pop>
|
||||||
|
<wood_factor>1.26</wood_factor>
|
||||||
|
<stone_factor>1.275</stone_factor>
|
||||||
|
<iron_factor>1.26</iron_factor>
|
||||||
|
<pop_factor>1.17</pop_factor>
|
||||||
|
<build_time>900</build_time>
|
||||||
|
<build_time_factor>1.2</build_time_factor>
|
||||||
|
</main>
|
||||||
|
<barracks>
|
||||||
|
<max_level>25</max_level>
|
||||||
|
<min_level>0</min_level>
|
||||||
|
<wood>200</wood>
|
||||||
|
<stone>170</stone>
|
||||||
|
<iron>90</iron>
|
||||||
|
<pop>7</pop>
|
||||||
|
<wood_factor>1.26</wood_factor>
|
||||||
|
<stone_factor>1.28</stone_factor>
|
||||||
|
<iron_factor>1.26</iron_factor>
|
||||||
|
<pop_factor>1.17</pop_factor>
|
||||||
|
<build_time>1800</build_time>
|
||||||
|
<build_time_factor>1.2</build_time_factor>
|
||||||
|
</barracks>
|
||||||
|
<stable>
|
||||||
|
<max_level>20</max_level>
|
||||||
|
<min_level>0</min_level>
|
||||||
|
<wood>270</wood>
|
||||||
|
<stone>240</stone>
|
||||||
|
<iron>260</iron>
|
||||||
|
<pop>8</pop>
|
||||||
|
<wood_factor>1.26</wood_factor>
|
||||||
|
<stone_factor>1.28</stone_factor>
|
||||||
|
<iron_factor>1.26</iron_factor>
|
||||||
|
<pop_factor>1.17</pop_factor>
|
||||||
|
<build_time>6000</build_time>
|
||||||
|
<build_time_factor>1.2</build_time_factor>
|
||||||
|
</stable>
|
||||||
|
<garage>
|
||||||
|
<max_level>15</max_level>
|
||||||
|
<min_level>0</min_level>
|
||||||
|
<wood>300</wood>
|
||||||
|
<stone>240</stone>
|
||||||
|
<iron>260</iron>
|
||||||
|
<pop>8</pop>
|
||||||
|
<wood_factor>1.26</wood_factor>
|
||||||
|
<stone_factor>1.28</stone_factor>
|
||||||
|
<iron_factor>1.26</iron_factor>
|
||||||
|
<pop_factor>1.17</pop_factor>
|
||||||
|
<build_time>6000</build_time>
|
||||||
|
<build_time_factor>1.2</build_time_factor>
|
||||||
|
</garage>
|
||||||
|
<watchtower>
|
||||||
|
<max_level>20</max_level>
|
||||||
|
<min_level>0</min_level>
|
||||||
|
<wood>12000</wood>
|
||||||
|
<stone>14000</stone>
|
||||||
|
<iron>10000</iron>
|
||||||
|
<pop>500</pop>
|
||||||
|
<wood_factor>1.17</wood_factor>
|
||||||
|
<stone_factor>1.17</stone_factor>
|
||||||
|
<iron_factor>1.18</iron_factor>
|
||||||
|
<pop_factor>1.18</pop_factor>
|
||||||
|
<build_time>13200</build_time>
|
||||||
|
<build_time_factor>1.2</build_time_factor>
|
||||||
|
</watchtower>
|
||||||
|
<snob>
|
||||||
|
<max_level>1</max_level>
|
||||||
|
<min_level>0</min_level>
|
||||||
|
<wood>15000</wood>
|
||||||
|
<stone>25000</stone>
|
||||||
|
<iron>10000</iron>
|
||||||
|
<pop>80</pop>
|
||||||
|
<wood_factor>2</wood_factor>
|
||||||
|
<stone_factor>2</stone_factor>
|
||||||
|
<iron_factor>2</iron_factor>
|
||||||
|
<pop_factor>1.17</pop_factor>
|
||||||
|
<build_time>586800</build_time>
|
||||||
|
<build_time_factor>1.2</build_time_factor>
|
||||||
|
</snob>
|
||||||
|
<smith>
|
||||||
|
<max_level>20</max_level>
|
||||||
|
<min_level>0</min_level>
|
||||||
|
<wood>220</wood>
|
||||||
|
<stone>180</stone>
|
||||||
|
<iron>240</iron>
|
||||||
|
<pop>20</pop>
|
||||||
|
<wood_factor>1.26</wood_factor>
|
||||||
|
<stone_factor>1.275</stone_factor>
|
||||||
|
<iron_factor>1.26</iron_factor>
|
||||||
|
<pop_factor>1.17</pop_factor>
|
||||||
|
<build_time>6000</build_time>
|
||||||
|
<build_time_factor>1.2</build_time_factor>
|
||||||
|
</smith>
|
||||||
|
<place>
|
||||||
|
<max_level>1</max_level>
|
||||||
|
<min_level>0</min_level>
|
||||||
|
<wood>10</wood>
|
||||||
|
<stone>40</stone>
|
||||||
|
<iron>30</iron>
|
||||||
|
<pop>0</pop>
|
||||||
|
<wood_factor>1.26</wood_factor>
|
||||||
|
<stone_factor>1.275</stone_factor>
|
||||||
|
<iron_factor>1.26</iron_factor>
|
||||||
|
<pop_factor>1.17</pop_factor>
|
||||||
|
<build_time>10860</build_time>
|
||||||
|
<build_time_factor>1.2</build_time_factor>
|
||||||
|
</place>
|
||||||
|
<statue>
|
||||||
|
<max_level>1</max_level>
|
||||||
|
<min_level>0</min_level>
|
||||||
|
<wood>220</wood>
|
||||||
|
<stone>220</stone>
|
||||||
|
<iron>220</iron>
|
||||||
|
<pop>10</pop>
|
||||||
|
<wood_factor>1.26</wood_factor>
|
||||||
|
<stone_factor>1.275</stone_factor>
|
||||||
|
<iron_factor>1.26</iron_factor>
|
||||||
|
<pop_factor>1.17</pop_factor>
|
||||||
|
<build_time>1500</build_time>
|
||||||
|
<build_time_factor>1.2</build_time_factor>
|
||||||
|
</statue>
|
||||||
|
<market>
|
||||||
|
<max_level>25</max_level>
|
||||||
|
<min_level>0</min_level>
|
||||||
|
<wood>100</wood>
|
||||||
|
<stone>100</stone>
|
||||||
|
<iron>100</iron>
|
||||||
|
<pop>20</pop>
|
||||||
|
<wood_factor>1.26</wood_factor>
|
||||||
|
<stone_factor>1.275</stone_factor>
|
||||||
|
<iron_factor>1.26</iron_factor>
|
||||||
|
<pop_factor>1.17</pop_factor>
|
||||||
|
<build_time>2700</build_time>
|
||||||
|
<build_time_factor>1.2</build_time_factor>
|
||||||
|
</market>
|
||||||
|
<wood>
|
||||||
|
<max_level>30</max_level>
|
||||||
|
<min_level>0</min_level>
|
||||||
|
<wood>50</wood>
|
||||||
|
<stone>60</stone>
|
||||||
|
<iron>40</iron>
|
||||||
|
<pop>5</pop>
|
||||||
|
<wood_factor>1.25</wood_factor>
|
||||||
|
<stone_factor>1.275</stone_factor>
|
||||||
|
<iron_factor>1.245</iron_factor>
|
||||||
|
<pop_factor>1.155</pop_factor>
|
||||||
|
<build_time>900</build_time>
|
||||||
|
<build_time_factor>1.2</build_time_factor>
|
||||||
|
</wood>
|
||||||
|
<stone>
|
||||||
|
<max_level>30</max_level>
|
||||||
|
<min_level>0</min_level>
|
||||||
|
<wood>65</wood>
|
||||||
|
<stone>50</stone>
|
||||||
|
<iron>40</iron>
|
||||||
|
<pop>10</pop>
|
||||||
|
<wood_factor>1.27</wood_factor>
|
||||||
|
<stone_factor>1.265</stone_factor>
|
||||||
|
<iron_factor>1.24</iron_factor>
|
||||||
|
<pop_factor>1.14</pop_factor>
|
||||||
|
<build_time>900</build_time>
|
||||||
|
<build_time_factor>1.2</build_time_factor>
|
||||||
|
</stone>
|
||||||
|
<iron>
|
||||||
|
<max_level>30</max_level>
|
||||||
|
<min_level>0</min_level>
|
||||||
|
<wood>75</wood>
|
||||||
|
<stone>65</stone>
|
||||||
|
<iron>70</iron>
|
||||||
|
<pop>10</pop>
|
||||||
|
<wood_factor>1.252</wood_factor>
|
||||||
|
<stone_factor>1.275</stone_factor>
|
||||||
|
<iron_factor>1.24</iron_factor>
|
||||||
|
<pop_factor>1.17</pop_factor>
|
||||||
|
<build_time>1080</build_time>
|
||||||
|
<build_time_factor>1.2</build_time_factor>
|
||||||
|
</iron>
|
||||||
|
<farm>
|
||||||
|
<max_level>30</max_level>
|
||||||
|
<min_level>1</min_level>
|
||||||
|
<wood>45</wood>
|
||||||
|
<stone>40</stone>
|
||||||
|
<iron>30</iron>
|
||||||
|
<pop>0</pop>
|
||||||
|
<wood_factor>1.3</wood_factor>
|
||||||
|
<stone_factor>1.32</stone_factor>
|
||||||
|
<iron_factor>1.29</iron_factor>
|
||||||
|
<pop_factor>1</pop_factor>
|
||||||
|
<build_time>1200</build_time>
|
||||||
|
<build_time_factor>1.2</build_time_factor>
|
||||||
|
</farm>
|
||||||
|
<storage>
|
||||||
|
<max_level>30</max_level>
|
||||||
|
<min_level>1</min_level>
|
||||||
|
<wood>60</wood>
|
||||||
|
<stone>50</stone>
|
||||||
|
<iron>40</iron>
|
||||||
|
<pop>0</pop>
|
||||||
|
<wood_factor>1.265</wood_factor>
|
||||||
|
<stone_factor>1.27</stone_factor>
|
||||||
|
<iron_factor>1.245</iron_factor>
|
||||||
|
<pop_factor>1.15</pop_factor>
|
||||||
|
<build_time>1020</build_time>
|
||||||
|
<build_time_factor>1.2</build_time_factor>
|
||||||
|
</storage>
|
||||||
|
<hide>
|
||||||
|
<max_level>10</max_level>
|
||||||
|
<min_level>0</min_level>
|
||||||
|
<wood>50</wood>
|
||||||
|
<stone>60</stone>
|
||||||
|
<iron>50</iron>
|
||||||
|
<pop>2</pop>
|
||||||
|
<wood_factor>1.25</wood_factor>
|
||||||
|
<stone_factor>1.25</stone_factor>
|
||||||
|
<iron_factor>1.25</iron_factor>
|
||||||
|
<pop_factor>1.17</pop_factor>
|
||||||
|
<build_time>1800</build_time>
|
||||||
|
<build_time_factor>1.2</build_time_factor>
|
||||||
|
</hide>
|
||||||
|
<wall>
|
||||||
|
<max_level>20</max_level>
|
||||||
|
<min_level>0</min_level>
|
||||||
|
<wood>50</wood>
|
||||||
|
<stone>100</stone>
|
||||||
|
<iron>20</iron>
|
||||||
|
<pop>5</pop>
|
||||||
|
<wood_factor>1.26</wood_factor>
|
||||||
|
<stone_factor>1.275</stone_factor>
|
||||||
|
<iron_factor>1.26</iron_factor>
|
||||||
|
<pop_factor>1.17</pop_factor>
|
||||||
|
<build_time>3600</build_time>
|
||||||
|
<build_time_factor>1.2</build_time_factor>
|
||||||
|
</wall>
|
||||||
|
</config>
|
|
@ -0,0 +1 @@
|
||||||
|
a:16:{s:5:"pl159";s:25:"https://pl159.plemiona.pl";s:5:"pl161";s:25:"https://pl161.plemiona.pl";s:5:"pl164";s:25:"https://pl164.plemiona.pl";s:4:"plc1";s:24:"https://plc1.plemiona.pl";s:4:"pls1";s:24:"https://pls1.plemiona.pl";s:4:"plp7";s:24:"https://plp7.plemiona.pl";s:5:"pl165";s:25:"https://pl165.plemiona.pl";s:5:"pl167";s:25:"https://pl167.plemiona.pl";s:5:"pl168";s:25:"https://pl168.plemiona.pl";s:4:"plp8";s:24:"https://plp8.plemiona.pl";s:5:"pl169";s:25:"https://pl169.plemiona.pl";s:5:"pl170";s:25:"https://pl170.plemiona.pl";s:5:"pl171";s:25:"https://pl171.plemiona.pl";s:5:"pl172";s:25:"https://pl172.plemiona.pl";s:5:"pl173";s:25:"https://pl173.plemiona.pl";s:5:"pl174";s:25:"https://pl174.plemiona.pl";}
|
|
@ -0,0 +1,125 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||||
|
<config>
|
||||||
|
<speed>4.5</speed>
|
||||||
|
<unit_speed>0.5</unit_speed>
|
||||||
|
<moral>1</moral>
|
||||||
|
<build>
|
||||||
|
<destroy>1</destroy>
|
||||||
|
</build>
|
||||||
|
<misc>
|
||||||
|
<kill_ranking>2</kill_ranking>
|
||||||
|
<tutorial>0</tutorial>
|
||||||
|
<trade_cancel_time>300</trade_cancel_time>
|
||||||
|
</misc>
|
||||||
|
<commands>
|
||||||
|
<millis_arrival>0</millis_arrival>
|
||||||
|
<command_cancel_time>600</command_cancel_time>
|
||||||
|
</commands>
|
||||||
|
<newbie>
|
||||||
|
<days>3</days>
|
||||||
|
<ratio_days>15</ratio_days>
|
||||||
|
<ratio>10</ratio>
|
||||||
|
<removeNewbieVillages>1</removeNewbieVillages>
|
||||||
|
</newbie>
|
||||||
|
<game>
|
||||||
|
<buildtime_formula>2</buildtime_formula>
|
||||||
|
<knight>0</knight>
|
||||||
|
<knight_new_items>On</knight_new_items>
|
||||||
|
<archer>1</archer>
|
||||||
|
<tech>2</tech>
|
||||||
|
<farm_limit>0</farm_limit>
|
||||||
|
<church>0</church>
|
||||||
|
<watchtower>0</watchtower>
|
||||||
|
<stronghold>0</stronghold>
|
||||||
|
<fake_limit>0.5</fake_limit>
|
||||||
|
<barbarian_rise>0.001</barbarian_rise>
|
||||||
|
<barbarian_shrink>1</barbarian_shrink>
|
||||||
|
<barbarian_max_points>800</barbarian_max_points>
|
||||||
|
<scavenging>1</scavenging>
|
||||||
|
<hauls>2</hauls>
|
||||||
|
<hauls_base>5000</hauls_base>
|
||||||
|
<hauls_max>1000000</hauls_max>
|
||||||
|
<base_production>75</base_production>
|
||||||
|
<event>0</event>
|
||||||
|
<suppress_events>1</suppress_events>
|
||||||
|
</game>
|
||||||
|
<buildings>
|
||||||
|
<custom_main>-1</custom_main>
|
||||||
|
<custom_farm>-1</custom_farm>
|
||||||
|
<custom_storage>-1</custom_storage>
|
||||||
|
<custom_place>-1</custom_place>
|
||||||
|
<custom_barracks>-1</custom_barracks>
|
||||||
|
<custom_church>-1</custom_church>
|
||||||
|
<custom_smith>-1</custom_smith>
|
||||||
|
<custom_wood>-1</custom_wood>
|
||||||
|
<custom_stone>-1</custom_stone>
|
||||||
|
<custom_iron>-1</custom_iron>
|
||||||
|
<custom_market>-1</custom_market>
|
||||||
|
<custom_stable>-1</custom_stable>
|
||||||
|
<custom_wall>-1</custom_wall>
|
||||||
|
<custom_garage>-1</custom_garage>
|
||||||
|
<custom_hide>-1</custom_hide>
|
||||||
|
<custom_snob>-1</custom_snob>
|
||||||
|
<custom_statue>-1</custom_statue>
|
||||||
|
<custom_watchtower>-1</custom_watchtower>
|
||||||
|
</buildings>
|
||||||
|
<snob>
|
||||||
|
<gold>1</gold>
|
||||||
|
<cheap_rebuild>0</cheap_rebuild>
|
||||||
|
<rise>2</rise>
|
||||||
|
<max_dist>50</max_dist>
|
||||||
|
<factor>1</factor>
|
||||||
|
<coin_wood>28000</coin_wood>
|
||||||
|
<coin_stone>30000</coin_stone>
|
||||||
|
<coin_iron>25000</coin_iron>
|
||||||
|
<no_barb_conquer>0</no_barb_conquer>
|
||||||
|
</snob>
|
||||||
|
<ally>
|
||||||
|
<no_harm>0</no_harm>
|
||||||
|
<no_other_support>0</no_other_support>
|
||||||
|
<no_other_support_type>0</no_other_support_type>
|
||||||
|
<allytime_support>0</allytime_support>
|
||||||
|
<no_leave></no_leave>
|
||||||
|
<no_join></no_join>
|
||||||
|
<limit>15</limit>
|
||||||
|
<fixed_allies>0</fixed_allies>
|
||||||
|
<wars_member_requirement>5</wars_member_requirement>
|
||||||
|
<wars_points_requirement>15000</wars_points_requirement>
|
||||||
|
<wars_autoaccept_days>7</wars_autoaccept_days>
|
||||||
|
<levels>1</levels>
|
||||||
|
<xp_requirements>v1</xp_requirements>
|
||||||
|
</ally>
|
||||||
|
<coord>
|
||||||
|
<map_size>1000</map_size>
|
||||||
|
<func>4</func>
|
||||||
|
<empty_villages>11</empty_villages>
|
||||||
|
<bonus_villages>1</bonus_villages>
|
||||||
|
<inner>550</inner>
|
||||||
|
<select_start>0</select_start>
|
||||||
|
<village_move_wait>336</village_move_wait>
|
||||||
|
<noble_restart>1</noble_restart>
|
||||||
|
<start_villages>1</start_villages>
|
||||||
|
</coord>
|
||||||
|
<sitter>
|
||||||
|
<allow>1</allow>
|
||||||
|
</sitter>
|
||||||
|
<sleep>
|
||||||
|
<active>0</active>
|
||||||
|
<delay>60</delay>
|
||||||
|
<min>6</min>
|
||||||
|
<max>10</max>
|
||||||
|
<min_awake>12</min_awake>
|
||||||
|
<max_awake>36</max_awake>
|
||||||
|
<warn_time>10</warn_time>
|
||||||
|
</sleep>
|
||||||
|
<night>
|
||||||
|
<active>2</active>
|
||||||
|
<start_hour>23</start_hour>
|
||||||
|
<end_hour>7</end_hour>
|
||||||
|
<def_factor>3.5</def_factor>
|
||||||
|
<duration>14</duration>
|
||||||
|
</night>
|
||||||
|
<win>
|
||||||
|
<check>3</check>
|
||||||
|
</win>
|
||||||
|
</config>
|
|
@ -0,0 +1,123 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||||
|
<config>
|
||||||
|
<spear>
|
||||||
|
<build_time>226.66666666667</build_time>
|
||||||
|
<pop>1</pop>
|
||||||
|
<speed>8</speed>
|
||||||
|
<attack>10</attack>
|
||||||
|
<defense>15</defense>
|
||||||
|
<defense_cavalry>45</defense_cavalry>
|
||||||
|
<defense_archer>20</defense_archer>
|
||||||
|
<carry>25</carry>
|
||||||
|
</spear>
|
||||||
|
<sword>
|
||||||
|
<build_time>333.33333333333</build_time>
|
||||||
|
<pop>1</pop>
|
||||||
|
<speed>9.7777777777778</speed>
|
||||||
|
<attack>25</attack>
|
||||||
|
<defense>50</defense>
|
||||||
|
<defense_cavalry>15</defense_cavalry>
|
||||||
|
<defense_archer>40</defense_archer>
|
||||||
|
<carry>15</carry>
|
||||||
|
</sword>
|
||||||
|
<axe>
|
||||||
|
<build_time>293.33333333333</build_time>
|
||||||
|
<pop>1</pop>
|
||||||
|
<speed>8</speed>
|
||||||
|
<attack>40</attack>
|
||||||
|
<defense>10</defense>
|
||||||
|
<defense_cavalry>5</defense_cavalry>
|
||||||
|
<defense_archer>10</defense_archer>
|
||||||
|
<carry>10</carry>
|
||||||
|
</axe>
|
||||||
|
<archer>
|
||||||
|
<build_time>400</build_time>
|
||||||
|
<pop>1</pop>
|
||||||
|
<speed>8</speed>
|
||||||
|
<attack>15</attack>
|
||||||
|
<defense>50</defense>
|
||||||
|
<defense_cavalry>40</defense_cavalry>
|
||||||
|
<defense_archer>5</defense_archer>
|
||||||
|
<carry>10</carry>
|
||||||
|
</archer>
|
||||||
|
<spy>
|
||||||
|
<build_time>200</build_time>
|
||||||
|
<pop>2</pop>
|
||||||
|
<speed>4</speed>
|
||||||
|
<attack>0</attack>
|
||||||
|
<defense>2</defense>
|
||||||
|
<defense_cavalry>1</defense_cavalry>
|
||||||
|
<defense_archer>2</defense_archer>
|
||||||
|
<carry>0</carry>
|
||||||
|
</spy>
|
||||||
|
<light>
|
||||||
|
<build_time>400</build_time>
|
||||||
|
<pop>4</pop>
|
||||||
|
<speed>4.4444444444444</speed>
|
||||||
|
<attack>130</attack>
|
||||||
|
<defense>30</defense>
|
||||||
|
<defense_cavalry>40</defense_cavalry>
|
||||||
|
<defense_archer>30</defense_archer>
|
||||||
|
<carry>80</carry>
|
||||||
|
</light>
|
||||||
|
<marcher>
|
||||||
|
<build_time>600</build_time>
|
||||||
|
<pop>5</pop>
|
||||||
|
<speed>4.4444444444444</speed>
|
||||||
|
<attack>120</attack>
|
||||||
|
<defense>40</defense>
|
||||||
|
<defense_cavalry>30</defense_cavalry>
|
||||||
|
<defense_archer>50</defense_archer>
|
||||||
|
<carry>50</carry>
|
||||||
|
</marcher>
|
||||||
|
<heavy>
|
||||||
|
<build_time>800</build_time>
|
||||||
|
<pop>6</pop>
|
||||||
|
<speed>4.8888888888889</speed>
|
||||||
|
<attack>150</attack>
|
||||||
|
<defense>200</defense>
|
||||||
|
<defense_cavalry>80</defense_cavalry>
|
||||||
|
<defense_archer>180</defense_archer>
|
||||||
|
<carry>50</carry>
|
||||||
|
</heavy>
|
||||||
|
<ram>
|
||||||
|
<build_time>1066.6666666667</build_time>
|
||||||
|
<pop>5</pop>
|
||||||
|
<speed>13.333333333333</speed>
|
||||||
|
<attack>2</attack>
|
||||||
|
<defense>20</defense>
|
||||||
|
<defense_cavalry>50</defense_cavalry>
|
||||||
|
<defense_archer>20</defense_archer>
|
||||||
|
<carry>0</carry>
|
||||||
|
</ram>
|
||||||
|
<catapult>
|
||||||
|
<build_time>1600</build_time>
|
||||||
|
<pop>8</pop>
|
||||||
|
<speed>13.333333333333</speed>
|
||||||
|
<attack>100</attack>
|
||||||
|
<defense>100</defense>
|
||||||
|
<defense_cavalry>50</defense_cavalry>
|
||||||
|
<defense_archer>100</defense_archer>
|
||||||
|
<carry>0</carry>
|
||||||
|
</catapult>
|
||||||
|
<snob>
|
||||||
|
<build_time>4000</build_time>
|
||||||
|
<pop>100</pop>
|
||||||
|
<speed>15.555555555556</speed>
|
||||||
|
<attack>30</attack>
|
||||||
|
<defense>100</defense>
|
||||||
|
<defense_cavalry>50</defense_cavalry>
|
||||||
|
<defense_archer>100</defense_archer>
|
||||||
|
<carry>0</carry>
|
||||||
|
</snob>
|
||||||
|
<militia>
|
||||||
|
<build_time>1</build_time>
|
||||||
|
<pop>0</pop>
|
||||||
|
<speed>0.016666666666667</speed>
|
||||||
|
<attack>0</attack>
|
||||||
|
<defense>15</defense>
|
||||||
|
<defense_cavalry>45</defense_cavalry>
|
||||||
|
<defense_archer>25</defense_archer>
|
||||||
|
<carry>0</carry>
|
||||||
|
</militia>
|
||||||
|
</config>
|
|
@ -0,0 +1,69 @@
|
||||||
|
package tw
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
Key string
|
||||||
|
URL string
|
||||||
|
}
|
||||||
|
|
||||||
|
type OpponentsDefeated struct {
|
||||||
|
RankAtt int
|
||||||
|
ScoreAtt int
|
||||||
|
RankDef int
|
||||||
|
ScoreDef int
|
||||||
|
RankSup int
|
||||||
|
ScoreSup int
|
||||||
|
RankTotal int
|
||||||
|
ScoreTotal int
|
||||||
|
}
|
||||||
|
|
||||||
|
type Tribe struct {
|
||||||
|
OpponentsDefeated
|
||||||
|
|
||||||
|
ID int
|
||||||
|
Name string
|
||||||
|
Tag string
|
||||||
|
NumMembers int
|
||||||
|
NumVillages int
|
||||||
|
Points int
|
||||||
|
AllPoints int
|
||||||
|
Rank int
|
||||||
|
ProfileURL string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Player struct {
|
||||||
|
OpponentsDefeated
|
||||||
|
|
||||||
|
ID int
|
||||||
|
Name string
|
||||||
|
NumVillages int
|
||||||
|
Points int
|
||||||
|
Rank int
|
||||||
|
TribeID int
|
||||||
|
ProfileURL string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Village struct {
|
||||||
|
ID int
|
||||||
|
Name string
|
||||||
|
Points int
|
||||||
|
X int
|
||||||
|
Y int
|
||||||
|
Continent string
|
||||||
|
Bonus int
|
||||||
|
PlayerID int
|
||||||
|
ProfileURL string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Ennoblement struct {
|
||||||
|
VillageID int
|
||||||
|
NewOwnerID int
|
||||||
|
NewTribeID int
|
||||||
|
OldOwnerID int
|
||||||
|
OldTribeID int
|
||||||
|
Points int
|
||||||
|
CreatedAt time.Time
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package tw
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Unit struct {
|
||||||
|
BuildTime float64 `xml:"build_time"`
|
||||||
|
Pop int `xml:"pop"`
|
||||||
|
Speed float64 `xml:"speed"`
|
||||||
|
Attack int `xml:"attack"`
|
||||||
|
Defense int `xml:"defense"`
|
||||||
|
DefenseCavalry int `xml:"defense_cavalry"`
|
||||||
|
DefenseArcher int `xml:"defense_archer"`
|
||||||
|
Carry int `xml:"carry"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UnitInfo struct {
|
||||||
|
XMLName xml.Name `xml:"config"`
|
||||||
|
Text string `xml:",chardata"`
|
||||||
|
Spear Unit `xml:"spear"`
|
||||||
|
Sword Unit `xml:"sword"`
|
||||||
|
Axe Unit `xml:"axe"`
|
||||||
|
Archer Unit `xml:"archer"`
|
||||||
|
Spy Unit `xml:"spy"`
|
||||||
|
Light Unit `xml:"light"`
|
||||||
|
Marcher Unit `xml:"marcher"`
|
||||||
|
Heavy Unit `xml:"heavy"`
|
||||||
|
Ram Unit `xml:"ram"` //nolint:revive
|
||||||
|
Catapult Unit `xml:"catapult"`
|
||||||
|
Knight Unit `xml:"knight"`
|
||||||
|
Snob Unit `xml:"snob"`
|
||||||
|
Militia Unit `xml:"militia"`
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||||
|
"labels": [
|
||||||
|
"dependencies"
|
||||||
|
],
|
||||||
|
"extends": [
|
||||||
|
"config:base",
|
||||||
|
":semanticCommits",
|
||||||
|
":semanticCommitTypeAll(chore)"
|
||||||
|
],
|
||||||
|
"postUpdateOptions": [
|
||||||
|
"gomodTidy"
|
||||||
|
],
|
||||||
|
"ignorePaths": [
|
||||||
|
".woodpecker/*"
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
apiVersion: skaffold/v3
|
||||||
|
kind: Config
|
||||||
|
profiles:
|
||||||
|
- name: dev
|
||||||
|
build:
|
||||||
|
tagPolicy:
|
||||||
|
customTemplate:
|
||||||
|
template: latest
|
||||||
|
artifacts:
|
||||||
|
- image: twhelp
|
||||||
|
hooks:
|
||||||
|
before:
|
||||||
|
- command: [ "sh", "-c", "make generate" ]
|
||||||
|
os: [ darwin, linux ]
|
||||||
|
context: .
|
||||||
|
docker:
|
||||||
|
dockerfile: ./build/docker/twhelp/dev/Dockerfile
|
||||||
|
manifests:
|
||||||
|
kustomize:
|
||||||
|
paths:
|
||||||
|
- k8s/overlays/dev
|
||||||
|
deploy:
|
||||||
|
kubectl: {}
|
||||||
|
- name: resources
|
||||||
|
deploy:
|
||||||
|
helm:
|
||||||
|
releases:
|
||||||
|
- name: twhelpdb
|
||||||
|
repo: https://charts.bitnami.com/bitnami
|
||||||
|
remoteChart: postgresql
|
||||||
|
version: 13.2.24
|
||||||
|
wait: true
|
||||||
|
setValues:
|
||||||
|
image.tag: 14.8.0-debian-11-r4
|
||||||
|
auth.username: twhelp
|
||||||
|
auth.password: twhelp
|
||||||
|
auth.database: twhelp
|
||||||
|
- name: twhelprmq
|
||||||
|
repo: https://charts.bitnami.com/bitnami
|
||||||
|
remoteChart: rabbitmq
|
||||||
|
version: 12.5.6
|
||||||
|
wait: true
|
||||||
|
setValues:
|
||||||
|
auth.username: twhelp
|
||||||
|
auth.password: twhelp
|
Loading…
Reference in New Issue