917 lines
25 KiB
Go
917 lines
25 KiB
Go
package main
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"encoding/json"
|
|
"sort"
|
|
"time"
|
|
|
|
"golang.org/x/net/context"
|
|
"golang.org/x/oauth2"
|
|
|
|
"io/ioutil"
|
|
"net/http"
|
|
|
|
"errors"
|
|
"flag"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"strconv"
|
|
|
|
"github.com/BurntSushi/toml"
|
|
"github.com/go-openapi/strfmt"
|
|
"github.com/gregjones/httpcache"
|
|
"github.com/gregjones/httpcache/leveldbcache"
|
|
"github.com/leekchan/accounting"
|
|
"github.com/logrusorgru/aurora"
|
|
|
|
ESI "./client"
|
|
|
|
ESIClones "./client/clones"
|
|
ESILocation "./client/location"
|
|
ESIPlanetaryInteraction "./client/planetary_interaction"
|
|
ESISkills "./client/skills"
|
|
ESIUniverse "./client/universe"
|
|
ESIWallet "./client/wallet"
|
|
|
|
"database/sql"
|
|
|
|
_ "github.com/mattn/go-sqlite3"
|
|
|
|
httptransport "github.com/go-openapi/runtime/client"
|
|
)
|
|
|
|
// Character - Structure to save the verification data.
|
|
type Character struct {
|
|
CharacterID int32
|
|
CharacterName string
|
|
ExpiresOn string
|
|
}
|
|
|
|
type configurationFile struct {
|
|
ClientID string
|
|
ClientSecret string
|
|
}
|
|
|
|
var (
|
|
googleOauthConfig = &oauth2.Config{
|
|
RedirectURL: "http://localhost:3000/callback",
|
|
ClientID: "CLIENTKEY",
|
|
ClientSecret: "SECRETKEY",
|
|
Scopes: []string{
|
|
"esi-skills.read_skillqueue.v1",
|
|
"esi-skills.read_skills.v1",
|
|
"esi-planets.manage_planets.v1",
|
|
"esi-wallet.read_character_wallet.v1",
|
|
"esi-location.read_location.v1",
|
|
"esi-clones.read_clones.v1",
|
|
},
|
|
Endpoint: oauth2.Endpoint{
|
|
AuthURL: "https://login.eveonline.com/oauth/authorize/",
|
|
TokenURL: "https://login.eveonline.com/oauth/token/",
|
|
},
|
|
}
|
|
// Some random string, random for each request
|
|
oauthStateString = "random"
|
|
|
|
defaultDateFormat = "_2 Jan 2006, 15:04"
|
|
|
|
cfgFilePath = flag.String("config", "configuration.toml", "Path to the configuration file.")
|
|
cacheDBPath = flag.String("cache", "cache.db", "Path to the cache sqlite database.")
|
|
insecureFlag = flag.Bool("insecure", false, "Do not check the HTTPS certificate")
|
|
)
|
|
|
|
var ctx = context.Background()
|
|
var messages = make(chan *oauth2.Token)
|
|
|
|
func main() {
|
|
flag.Parse()
|
|
|
|
logfilename := fmt.Sprintf("log-%s.log", time.Now().Format("2006-01-02_15_04_05"))
|
|
f, err := os.OpenFile(logfilename, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
|
if err != nil {
|
|
log.Fatalf("error opening file: %v", err)
|
|
}
|
|
defer f.Close()
|
|
|
|
log.SetOutput(f)
|
|
|
|
cErr := readConfigurationFile()
|
|
if cErr != nil {
|
|
fmt.Println("Missing configuration file.")
|
|
log.Fatal(cErr)
|
|
}
|
|
|
|
cToken := getDatabaseToken()
|
|
|
|
if cToken == nil {
|
|
cToken = getNewAuthorizationToken()
|
|
}
|
|
|
|
ldb, ldbErr := leveldbcache.New("cache.ldb")
|
|
if ldbErr != nil {
|
|
fmt.Println("Unable to initialize the LevelDB cache.")
|
|
log.Fatal(ldbErr)
|
|
}
|
|
cachingTransport := httpcache.NewTransport(ldb)
|
|
|
|
if *insecureFlag {
|
|
proxiedTransport := &http.Transport{
|
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
|
Proxy: http.ProxyFromEnvironment,
|
|
}
|
|
cachingTransport.Transport = proxiedTransport
|
|
}
|
|
|
|
cachingClient := &http.Client{Transport: cachingTransport}
|
|
|
|
ctx := context.WithValue(context.TODO(), oauth2.HTTPClient, cachingClient)
|
|
client := googleOauthConfig.Client(ctx, cToken)
|
|
|
|
m, err := getCharacterInfo(client)
|
|
if err != nil {
|
|
log.Println(err)
|
|
cToken = getNewAuthorizationToken()
|
|
client = googleOauthConfig.Client(ctx, cToken)
|
|
m, err = getCharacterInfo(client)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
transport := httptransport.NewWithClient("esi.tech.ccp.is", "/latest", []string{"https"}, client)
|
|
//transport.Debug = true
|
|
|
|
swaggerclient := ESI.New(transport, strfmt.Default)
|
|
|
|
charInfoChan := make(chan string)
|
|
cloneInfoChan := make(chan string)
|
|
planetInfoChan := make(chan string)
|
|
skillsInfoChan := make(chan string)
|
|
|
|
go printCharacterInformation(charInfoChan, swaggerclient, m)
|
|
go printClonesInformation(cloneInfoChan, swaggerclient, m)
|
|
go printCharacterPlanets(planetInfoChan, swaggerclient, m)
|
|
go printCharacterSkillQueue(skillsInfoChan, swaggerclient, m)
|
|
|
|
for msg := range charInfoChan {
|
|
fmt.Print(msg)
|
|
}
|
|
|
|
fmt.Printf("\n\nClones\n")
|
|
for msg := range cloneInfoChan {
|
|
fmt.Print(msg)
|
|
}
|
|
|
|
fmt.Printf("\n\nPlanetary interaction\n")
|
|
for msg := range planetInfoChan {
|
|
fmt.Print(msg)
|
|
}
|
|
|
|
fmt.Printf("\n\nSkill queue\n")
|
|
for msg := range skillsInfoChan {
|
|
fmt.Print(msg)
|
|
}
|
|
}
|
|
|
|
func readConfigurationFile() error {
|
|
var config configurationFile
|
|
if _, err := toml.DecodeFile(*cfgFilePath, &config); err != nil {
|
|
log.Println(err)
|
|
return err
|
|
}
|
|
|
|
if config.ClientID == "" || config.ClientSecret == "" {
|
|
log.Println("Missing ClientID or ClientSecret configuration option in configuration file" + *cfgFilePath + ".")
|
|
return errors.New("Missing ClientID or ClientSecret in configuration file " + *cfgFilePath + ".")
|
|
}
|
|
|
|
googleOauthConfig.ClientID = config.ClientID
|
|
googleOauthConfig.ClientSecret = config.ClientSecret
|
|
|
|
return nil
|
|
}
|
|
|
|
func getCharacterInfo(client *http.Client) (*Character, error) {
|
|
|
|
req, _ := http.NewRequest("GET", "https://login.eveonline.com/oauth/verify", nil)
|
|
response, errDo := client.Do(req)
|
|
if errDo != nil {
|
|
return nil, errDo
|
|
}
|
|
|
|
defer response.Body.Close()
|
|
contents, _ := ioutil.ReadAll(response.Body)
|
|
|
|
var m Character
|
|
errJSON := json.Unmarshal(contents, &m)
|
|
if errJSON != nil {
|
|
log.Printf("Unmarshalling error: %s\n", errJSON)
|
|
return nil, errJSON
|
|
}
|
|
|
|
if m.CharacterID == 0 || m.CharacterName == "" {
|
|
log.Printf("Invalid return value from verify call: %s", contents)
|
|
return nil, errors.New("Invalid token")
|
|
}
|
|
|
|
return &m, nil
|
|
}
|
|
|
|
func printCharacterSkillQueue(outChan chan string, swaggerclient *ESI.App, m *Character) {
|
|
callParam := ESISkills.NewGetCharactersCharacterIDSkillqueueParams()
|
|
callParam.WithCharacterID(m.CharacterID)
|
|
|
|
skillqueueresp, skillerr := swaggerclient.Skills.GetCharactersCharacterIDSkillqueue(callParam, nil)
|
|
if skillerr != nil {
|
|
log.Fatalf("Got error on GetCharactersCharacterIDSkillqueue: %T %s", skillerr, skillerr)
|
|
}
|
|
|
|
skillqueue := skillqueueresp.Payload
|
|
|
|
now := time.Now()
|
|
|
|
snIds := make([]int32, 0, len(skillqueue))
|
|
for _, skill := range skillqueue {
|
|
snIds = append(snIds, *skill.SkillID)
|
|
}
|
|
skillNames := getUniverseNames(swaggerclient, &snIds)
|
|
|
|
maxSkillLength := 0
|
|
for _, skill := range skillqueue {
|
|
name := skillNames[*skill.SkillID]
|
|
if maxSkillLength < len(name) {
|
|
maxSkillLength = len(name)
|
|
}
|
|
}
|
|
|
|
for _, skill := range skillqueue {
|
|
// element is the element from someSlice for where we are
|
|
name := skillNames[*skill.SkillID]
|
|
|
|
finishDate := time.Time(skill.FinishDate)
|
|
startDate := time.Time(skill.StartDate)
|
|
|
|
skillDuration := finishDate.Sub(startDate)
|
|
finishDuration := finishDate.Sub(now)
|
|
|
|
maxSkillFormat := fmt.Sprintf("%d", maxSkillLength)
|
|
|
|
// see https://github.com/ccpgames/esi-issues/issues/113
|
|
// The queue is only updated when the user logs in with the client
|
|
// we thus need to do the computations and filtering ourselves
|
|
if finishDate.Before(time.Now()) {
|
|
outChan <- fmt.Sprintf("✔ %"+maxSkillFormat+"s %d\n",
|
|
name,
|
|
*skill.FinishedLevel,
|
|
)
|
|
continue
|
|
}
|
|
|
|
if startDate.Before(time.Now()) {
|
|
outChan <- fmt.Sprintf("%s %"+maxSkillFormat+"s %d, %s - ends on %s (%s)\n",
|
|
aurora.Green("➠").Bold(),
|
|
name,
|
|
*skill.FinishedLevel,
|
|
formatDuration(skillDuration, 2),
|
|
time.Time(skill.FinishDate).Format(defaultDateFormat),
|
|
formatDuration(finishDuration, 3),
|
|
)
|
|
continue
|
|
}
|
|
|
|
outChan <- fmt.Sprintf(" %"+maxSkillFormat+"s %d, %s - ends on %s (%s)\n",
|
|
name,
|
|
*skill.FinishedLevel,
|
|
formatDuration(skillDuration, 2),
|
|
time.Time(skill.FinishDate).Format(defaultDateFormat),
|
|
formatDuration(finishDuration, 3),
|
|
)
|
|
}
|
|
|
|
close(outChan)
|
|
}
|
|
|
|
func formatDuration(duration time.Duration, daysLength int32) string {
|
|
days := int32(duration.Hours()) / 24
|
|
hours := int32(duration.Minutes()) / 60 % 24
|
|
minutes := int32(duration.Minutes()) % 60
|
|
|
|
return fmt.Sprintf("%"+fmt.Sprintf("%d", daysLength)+"dd %02d:%02d", days, hours, minutes)
|
|
}
|
|
|
|
func printCharacterInformation(outChan chan string, swaggerclient *ESI.App, m *Character) {
|
|
callParam := ESIWallet.NewGetCharactersCharacterIDWalletsParams()
|
|
callParam.WithCharacterID(m.CharacterID)
|
|
|
|
esiresponse, esierr := swaggerclient.Wallet.GetCharactersCharacterIDWallets(callParam, nil)
|
|
if esierr != nil {
|
|
fmt.Println("Error while getting the wallet information")
|
|
log.Fatalf("Got error on GetCharactersCharacterIDWallets: %T %s", esierr, esierr)
|
|
}
|
|
|
|
wallets := esiresponse.Payload
|
|
|
|
ac := accounting.Accounting{Symbol: "ISK ", Precision: 0, Thousand: "'"}
|
|
|
|
outChan <- fmt.Sprintf("Name: %s\n", m.CharacterName)
|
|
|
|
for _, wallet := range wallets {
|
|
if wallet.Balance > 0 {
|
|
outChan <- fmt.Sprintf("Wallet: %s\n", ac.FormatMoney(wallet.Balance/100))
|
|
}
|
|
}
|
|
|
|
posCallParam := ESILocation.NewGetCharactersCharacterIDLocationParams()
|
|
posCallParam.WithCharacterID(m.CharacterID)
|
|
|
|
posresponse, poserr := swaggerclient.Location.GetCharactersCharacterIDLocation(posCallParam, nil)
|
|
if poserr != nil {
|
|
fmt.Println("Error while getting the current character location.")
|
|
log.Fatalf("Got error on GetCharactersCharacterIDLocation: %s", poserr)
|
|
}
|
|
|
|
position := posresponse.Payload
|
|
|
|
itemIds := make([]int32, 0)
|
|
itemIds = append(itemIds, *position.SolarSystemID)
|
|
if position.StationID != nil {
|
|
itemIds = append(itemIds, *position.StationID)
|
|
}
|
|
|
|
universeNames := getUniverseNames(swaggerclient, &itemIds)
|
|
|
|
stationName := ""
|
|
if position.StationID != nil {
|
|
stationName = universeNames[*position.StationID]
|
|
}
|
|
|
|
outChan <- fmt.Sprintf("Currently in %s - %s\n",
|
|
universeNames[*position.SolarSystemID],
|
|
stationName,
|
|
)
|
|
|
|
close(outChan)
|
|
}
|
|
|
|
func printClonesInformation(outChan chan string, swaggerclient *ESI.App, m *Character) {
|
|
cloCallParams := ESIClones.NewGetCharactersCharacterIDClonesParams()
|
|
cloCallParams.WithCharacterID(m.CharacterID)
|
|
|
|
cloresponse, cloerr := swaggerclient.Clones.GetCharactersCharacterIDClones(cloCallParams, nil)
|
|
if cloerr != nil {
|
|
fmt.Println("Error while getting the current character clones.")
|
|
log.Fatalf("Got error on GetCharactersCharacterIDClones: %s", cloerr)
|
|
}
|
|
|
|
clones := cloresponse.Payload.JumpClones
|
|
|
|
for _, clone := range clones {
|
|
sInfo, _ := getStructureStationInfo(swaggerclient, clone.LocationType, clone.LocationID)
|
|
|
|
outChan <- fmt.Sprintf(" %s, %s, %s\n ",
|
|
sInfo.Name,
|
|
sInfo.System.ConstellationName,
|
|
sInfo.System.RegionName,
|
|
)
|
|
|
|
implantNames := getUniverseNames(swaggerclient, &clone.Implants)
|
|
for _, implant := range clone.Implants {
|
|
implantName := implantNames[implant]
|
|
outChan <- fmt.Sprintf(" %s, ", implantName)
|
|
}
|
|
|
|
outChan <- "\n"
|
|
}
|
|
|
|
close(outChan)
|
|
}
|
|
|
|
type structureInfo struct {
|
|
ID int64
|
|
Name string
|
|
SystemID int32
|
|
System solarSystemInfo
|
|
}
|
|
|
|
func getStructureStationInfo(swaggerclient *ESI.App, typeID string, ID int64) (*structureInfo, error) {
|
|
locationName := getCachedDataSub64(ID, "name")
|
|
locationSystem := getCachedDataIntSub64(ID, "system")
|
|
|
|
if locationName != "" && locationSystem > 0 {
|
|
var sInfo structureInfo
|
|
|
|
sInfo.ID = ID
|
|
sInfo.Name = locationName
|
|
sInfo.System = getSolarSystemInformation(swaggerclient, locationSystem)
|
|
|
|
return &sInfo, nil
|
|
}
|
|
|
|
if typeID == "station" {
|
|
stacallParams := ESIUniverse.NewGetUniverseStationsStationIDParams()
|
|
stacallParams.WithStationID(int32(ID))
|
|
|
|
staresponse, staerr := swaggerclient.Universe.GetUniverseStationsStationID(stacallParams)
|
|
if staerr != nil {
|
|
log.Printf("Got error on GetCharactersCharacterIDClones: %s", staerr)
|
|
return nil, staerr
|
|
}
|
|
|
|
staInfo := staresponse.Payload
|
|
|
|
var sInfo structureInfo
|
|
|
|
sInfo.ID = int64(*staInfo.StationID)
|
|
sInfo.Name = *staInfo.Name
|
|
sInfo.SystemID = *staInfo.SystemID
|
|
sInfo.System = getSolarSystemInformation(swaggerclient, sInfo.SystemID)
|
|
|
|
putCacheDataSub64(sInfo.ID, "name", sInfo.Name)
|
|
putCacheDataIntSub64(sInfo.ID, "system", sInfo.SystemID)
|
|
|
|
return &sInfo, nil
|
|
}
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
type byPIPinType []*ESIPlanetaryInteraction.PinsItems0
|
|
|
|
func (s byPIPinType) Len() int {
|
|
return len(s)
|
|
}
|
|
func (s byPIPinType) Swap(i, j int) {
|
|
s[i], s[j] = s[j], s[i]
|
|
}
|
|
func (s byPIPinType) Less(i, j int) bool {
|
|
a := s[i]
|
|
b := s[j]
|
|
|
|
if *a.TypeID != *b.TypeID {
|
|
return *a.TypeID < *b.TypeID
|
|
}
|
|
|
|
if a.ExtractorDetails != nil {
|
|
return a.ExtractorDetails.ProductTypeID < a.ExtractorDetails.ProductTypeID
|
|
}
|
|
|
|
if a.SchematicID > 0 {
|
|
return a.SchematicID < b.SchematicID
|
|
}
|
|
|
|
return *a.PinID < *b.PinID
|
|
}
|
|
|
|
func printCharacterPlanets(outChan chan string, swaggerclient *ESI.App, m *Character) {
|
|
|
|
callParam := ESIPlanetaryInteraction.NewGetCharactersCharacterIDPlanetsParams()
|
|
callParam.WithCharacterID(m.CharacterID)
|
|
|
|
esiresponse, esierr := swaggerclient.PlanetaryInteraction.GetCharactersCharacterIDPlanets(callParam, nil)
|
|
if esierr != nil {
|
|
fmt.Println("Error while getting the planetary interaction information.")
|
|
log.Fatalf("Got error on GetCharactersCharacterIDPlanets: %T %s", esierr, esierr)
|
|
}
|
|
|
|
planets := esiresponse.Payload
|
|
|
|
now := time.Now()
|
|
|
|
for _, planet := range planets {
|
|
pcallParam := ESIPlanetaryInteraction.NewGetCharactersCharacterIDPlanetsPlanetIDParams()
|
|
pcallParam.WithCharacterID(m.CharacterID).WithPlanetID(*planet.PlanetID)
|
|
|
|
planetSystemInfo := getSolarSystemInformation(swaggerclient, *planet.SolarSystemID)
|
|
|
|
planetName := getPlanetInformation(swaggerclient, *planet.PlanetID)
|
|
|
|
outChan <- fmt.Sprintf("Planet %s, %s, %s - Type %s, Lvl %d - Updated %s\n",
|
|
planetName.PlanetName,
|
|
planetSystemInfo.ConstellationName,
|
|
planetSystemInfo.RegionName,
|
|
*planet.PlanetType,
|
|
*planet.UpgradeLevel,
|
|
time.Time(*planet.LastUpdate).Format(defaultDateFormat),
|
|
)
|
|
|
|
pesiresponse, pesierr := swaggerclient.PlanetaryInteraction.GetCharactersCharacterIDPlanetsPlanetID(pcallParam, nil)
|
|
if pesierr != nil {
|
|
fmt.Println(" Error while getting the planetary interaction information")
|
|
log.Printf("Error on CharactersCharacterIDPlanetsPlanetID: %s\n", pesierr)
|
|
continue
|
|
}
|
|
|
|
pins := pesiresponse.Payload.Pins
|
|
sort.Sort(byPIPinType(pins))
|
|
|
|
ptIds := make([]int32, 0, len(pins))
|
|
for _, pin := range pins {
|
|
if pin.ExtractorDetails != nil {
|
|
ptIds = append(ptIds, pin.ExtractorDetails.ProductTypeID)
|
|
}
|
|
}
|
|
pinNames := getUniverseNames(swaggerclient, &ptIds)
|
|
|
|
for _, pin := range pins {
|
|
if pin.ExtractorDetails != nil {
|
|
status := fmt.Sprint(aurora.Red("✘").Bold())
|
|
statuscomment := fmt.Sprint(aurora.Red("expired").Bold())
|
|
duration := now.Sub(time.Time(pin.ExpiryTime))
|
|
if time.Time(pin.ExpiryTime).After(now) {
|
|
status = fmt.Sprint(aurora.Green("✔").Bold())
|
|
statuscomment = "expires"
|
|
duration = time.Time(pin.ExpiryTime).Sub(now)
|
|
}
|
|
|
|
outChan <- fmt.Sprintf(" %s Extractor %4ds cycle, %s, %d per cycle, %s %s (%s)\n",
|
|
status,
|
|
pin.ExtractorDetails.CycleTime,
|
|
pinNames[pin.ExtractorDetails.ProductTypeID],
|
|
pin.ExtractorDetails.QtyPerCycle,
|
|
statuscomment,
|
|
time.Time(pin.ExpiryTime).Format(defaultDateFormat),
|
|
formatDuration(duration, 2),
|
|
)
|
|
} else if pin.SchematicID != 0 {
|
|
|
|
// Get the schematic from ESI and cache it
|
|
schematicInfo, serr := getSchematicsInformation(swaggerclient, pin.SchematicID)
|
|
if serr != nil {
|
|
log.Printf("Error on getSchematicsInformation: %T, %s\n", serr, serr)
|
|
outChan <- fmt.Sprintf(" ✔ Factory ????? cycle, ?????\n")
|
|
} else {
|
|
outChan <- fmt.Sprintf(" ✔ Factory %4ds cycle, %s\n",
|
|
schematicInfo.CycleTime,
|
|
schematicInfo.SchematicName,
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
close(outChan)
|
|
}
|
|
|
|
func getUniverseNames(swaggerclient *ESI.App, itemIds *[]int32) map[int32]string {
|
|
itemNames := make(map[int32]string)
|
|
itemMissingNames := make(map[int32]string)
|
|
|
|
for _, itemID := range *itemIds {
|
|
itemName := getCachedDataSub(itemID, "name")
|
|
if itemName != "" {
|
|
itemNames[itemID] = itemName
|
|
} else {
|
|
itemMissingNames[itemID] = ""
|
|
}
|
|
}
|
|
|
|
if len(itemMissingNames) > 0 {
|
|
snIds := make([]int32, 0, len(itemMissingNames))
|
|
for itemID := range itemMissingNames {
|
|
snIds = append(snIds, itemID)
|
|
}
|
|
|
|
sncallParam := ESIUniverse.NewPostUniverseNamesParams()
|
|
sncallParam.WithIds(snIds)
|
|
|
|
skillNameResp, skillNameErr := swaggerclient.Universe.PostUniverseNames(sncallParam)
|
|
if skillNameErr != nil {
|
|
log.Printf("Error on PostUniverseNames on items: %v\n", snIds)
|
|
log.Printf("Error on PostUniverseNames: %s\n", skillNameErr)
|
|
} else {
|
|
for _, searchResult := range skillNameResp.Payload {
|
|
itemName := searchResult.Name
|
|
itemID := searchResult.ID
|
|
|
|
putCacheDataSub(*itemID, "name", *itemName)
|
|
|
|
itemNames[*itemID] = *itemName
|
|
}
|
|
}
|
|
}
|
|
|
|
return itemNames
|
|
}
|
|
|
|
type planetInformation struct {
|
|
PlanetName string
|
|
PlanetTypeID int32
|
|
}
|
|
|
|
func getPlanetInformation(swaggerclient *ESI.App, planetID int32) planetInformation {
|
|
|
|
planetName := getCachedDataSub(planetID, "name")
|
|
planetType := getCachedDataIntSub(planetID, "type")
|
|
|
|
if planetName == "" {
|
|
pcallParams := ESIUniverse.NewGetUniversePlanetsPlanetIDParams()
|
|
pcallParams.WithPlanetID(planetID)
|
|
|
|
pesiresponse, _ := swaggerclient.Universe.GetUniversePlanetsPlanetID(pcallParams)
|
|
|
|
planetESIInfo := pesiresponse.Payload
|
|
|
|
var planetInfo planetInformation
|
|
planetInfo.PlanetName = *planetESIInfo.Name
|
|
planetInfo.PlanetTypeID = *planetESIInfo.TypeID
|
|
|
|
putCacheDataSub(planetID, "name", planetInfo.PlanetName)
|
|
putCacheDataIntSub(planetID, "type", planetInfo.PlanetTypeID)
|
|
|
|
return planetInfo
|
|
}
|
|
|
|
var planetInfo planetInformation
|
|
planetInfo.PlanetName = planetName
|
|
planetInfo.PlanetTypeID = planetType
|
|
|
|
return planetInfo
|
|
}
|
|
|
|
type solarSystemInfo struct {
|
|
SolarSystemName string
|
|
ConstellationID int32
|
|
ConstellationName string
|
|
RegionID int32
|
|
RegionName string
|
|
}
|
|
|
|
func getSolarSystemInformation(swaggerclient *ESI.App, solarSystemID int32) solarSystemInfo {
|
|
|
|
systemName := getCachedDataSub(solarSystemID, "name")
|
|
systemConstellationID := getCachedDataIntSub(solarSystemID, "constellation")
|
|
constellationName := getCachedDataSub(systemConstellationID, "name")
|
|
constellationRegionID := getCachedDataIntSub(systemConstellationID, "region")
|
|
regionName := getCachedDataSub(constellationRegionID, "name")
|
|
|
|
if systemName == "" || constellationName == "" || regionName == "" {
|
|
scallParams := ESIUniverse.NewGetUniverseSystemsSystemIDParams()
|
|
scallParams.WithSystemID(solarSystemID)
|
|
|
|
sesiresponse, _ := swaggerclient.Universe.GetUniverseSystemsSystemID(scallParams)
|
|
solarSystemESIInfo := sesiresponse.Payload
|
|
|
|
var ssInfo solarSystemInfo
|
|
ssInfo.SolarSystemName = *solarSystemESIInfo.Name
|
|
ssInfo.ConstellationID = *solarSystemESIInfo.ConstellationID
|
|
|
|
ccallParams := ESIUniverse.NewGetUniverseConstellationsConstellationIDParams()
|
|
ccallParams.WithConstellationID(ssInfo.ConstellationID)
|
|
|
|
cesiresponse, _ := swaggerclient.Universe.GetUniverseConstellationsConstellationID(ccallParams)
|
|
constellationESIInfo := cesiresponse.Payload
|
|
|
|
ssInfo.ConstellationName = *constellationESIInfo.Name
|
|
ssInfo.RegionID = *constellationESIInfo.RegionID
|
|
|
|
rcallParams := ESIUniverse.NewGetUniverseRegionsRegionIDParams()
|
|
rcallParams.WithRegionID(ssInfo.RegionID)
|
|
|
|
resiresponse, _ := swaggerclient.Universe.GetUniverseRegionsRegionID(rcallParams)
|
|
regionESIInfo := resiresponse.Payload
|
|
|
|
ssInfo.RegionName = *regionESIInfo.Name
|
|
|
|
putCacheDataSub(solarSystemID, "name", ssInfo.SolarSystemName)
|
|
putCacheDataIntSub(solarSystemID, "constellation", ssInfo.ConstellationID)
|
|
|
|
putCacheDataSub(ssInfo.ConstellationID, "name", ssInfo.ConstellationName)
|
|
putCacheDataIntSub(ssInfo.ConstellationID, "region", ssInfo.RegionID)
|
|
|
|
putCacheDataSub(ssInfo.RegionID, "name", ssInfo.RegionName)
|
|
|
|
return ssInfo
|
|
}
|
|
|
|
var ssInfo solarSystemInfo
|
|
|
|
ssInfo.SolarSystemName = systemName
|
|
ssInfo.ConstellationID = systemConstellationID
|
|
ssInfo.ConstellationName = constellationName
|
|
ssInfo.RegionID = constellationRegionID
|
|
ssInfo.RegionName = regionName
|
|
return ssInfo
|
|
}
|
|
|
|
// SchematicInfo - Structure to store and cache the schematics information from ESI
|
|
type SchematicInfo struct {
|
|
CycleTime int32
|
|
SchematicName string
|
|
}
|
|
|
|
func getSchematicsInformation(swaggerclient *ESI.App, schematicID int32) (*SchematicInfo, error) {
|
|
|
|
schematicName := getCachedDataSub(schematicID, "name")
|
|
schematicCycle := getCachedDataIntSub(schematicID, "cycle")
|
|
|
|
if schematicName == "" {
|
|
scallParams := ESIPlanetaryInteraction.NewGetUniverseSchematicsSchematicIDParams()
|
|
scallParams.WithSchematicID(schematicID)
|
|
|
|
sesiresponse, sesierr := swaggerclient.PlanetaryInteraction.GetUniverseSchematicsSchematicID(scallParams)
|
|
if sesierr != nil {
|
|
return nil, sesierr
|
|
}
|
|
|
|
schematicsESIInfo := sesiresponse.Payload
|
|
|
|
var schematicInfo SchematicInfo
|
|
schematicInfo.CycleTime = *schematicsESIInfo.CycleTime
|
|
schematicInfo.SchematicName = *schematicsESIInfo.SchematicName
|
|
|
|
putCacheDataSub(schematicID, "name", schematicInfo.SchematicName)
|
|
putCacheDataIntSub(schematicID, "cycle", schematicInfo.CycleTime)
|
|
|
|
return &schematicInfo, nil
|
|
}
|
|
|
|
var schematicInfo SchematicInfo
|
|
schematicInfo.CycleTime = schematicCycle
|
|
schematicInfo.SchematicName = schematicName
|
|
|
|
return &schematicInfo, nil
|
|
|
|
}
|
|
|
|
func getNewAuthorizationToken() *oauth2.Token {
|
|
http.HandleFunc("/", handleLogin)
|
|
http.HandleFunc("/callback", handleAuthenticationCallback)
|
|
go func() {
|
|
fmt.Println("No available token. Please visit http://localhost:3000 to renew.")
|
|
http.ListenAndServe(":3000", nil)
|
|
}()
|
|
|
|
return <-messages
|
|
}
|
|
|
|
func handleLogin(w http.ResponseWriter, r *http.Request) {
|
|
url := googleOauthConfig.AuthCodeURL(oauthStateString, oauth2.AccessTypeOffline)
|
|
|
|
// https://eveonline-third-party-documentation.readthedocs.io/en/latest/sso/authentication.html
|
|
// response_type: Must be set to “code”.
|
|
url = url + "&response_type=code"
|
|
|
|
http.Redirect(w, r, url, http.StatusTemporaryRedirect)
|
|
}
|
|
|
|
func handleAuthenticationCallback(w http.ResponseWriter, r *http.Request) {
|
|
state := r.FormValue("state")
|
|
if state != oauthStateString {
|
|
log.Printf("invalid oauth state, expected '%s', got '%s'\n", oauthStateString, state)
|
|
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
|
|
// No token to pass, we will get one on the second pass
|
|
return
|
|
}
|
|
|
|
code := r.FormValue("code")
|
|
token, err := googleOauthConfig.Exchange(oauth2.NoContext, code)
|
|
if err != nil {
|
|
log.Printf("Code exchange failed with '%s'\n", err)
|
|
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
|
|
// No token to pass, we will get one on the second pass
|
|
return
|
|
}
|
|
|
|
client := googleOauthConfig.Client(oauth2.NoContext, token)
|
|
|
|
req, _ := http.NewRequest("GET", "https://login.eveonline.com/oauth/verify", nil)
|
|
response, _ := client.Do(req)
|
|
|
|
defer response.Body.Close()
|
|
contents, _ := ioutil.ReadAll(response.Body)
|
|
|
|
var m Character
|
|
errJSON := json.Unmarshal(contents, &m)
|
|
if errJSON != nil {
|
|
fmt.Printf("JSON read error with '%s'\n", errJSON)
|
|
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
|
|
return
|
|
}
|
|
|
|
fmt.Fprintf(w, "Got token for character %s.\n", m.CharacterName)
|
|
fmt.Fprintf(w, "You can now close this navigator tab.\n")
|
|
|
|
log.Printf("Refresh token is %s\n", token.RefreshToken)
|
|
|
|
putCacheData("accessToken", token.AccessToken)
|
|
putCacheData("refreshToken", token.RefreshToken)
|
|
|
|
messages <- token
|
|
}
|
|
|
|
func getCachedDataIntSub(key int32, subkey string) int32 {
|
|
return getCachedDataInt(fmt.Sprintf("%d.%s", key, subkey))
|
|
}
|
|
|
|
func getCachedDataSub(key int32, subkey string) string {
|
|
return getCachedData(fmt.Sprintf("%d.%s", key, subkey))
|
|
}
|
|
|
|
func getCachedDataIntSub64(key int64, subkey string) int32 {
|
|
return getCachedDataInt(fmt.Sprintf("%d.%s", key, subkey))
|
|
}
|
|
|
|
func getCachedDataSub64(key int64, subkey string) string {
|
|
return getCachedData(fmt.Sprintf("%d.%s", key, subkey))
|
|
}
|
|
|
|
func getCachedDataInt(key string) int32 {
|
|
strVal := getCachedData(key)
|
|
|
|
intVal, convErr := strconv.ParseInt(strVal, 10, 32)
|
|
if convErr != nil {
|
|
return -1
|
|
}
|
|
|
|
return int32(intVal)
|
|
}
|
|
|
|
func getCachedData(key string) string {
|
|
db, err := sql.Open("sqlite3", *cacheDBPath)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
defer db.Close()
|
|
|
|
_, err = db.Exec("CREATE TABLE IF NOT EXISTS properties (id text NOT NULL PRIMARY KEY, value TEXT);")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
stmt, err := db.Prepare("SELECT value FROM properties WHERE id = ?")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
defer stmt.Close()
|
|
var response string
|
|
|
|
err = stmt.QueryRow(key).Scan(&response)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
|
|
return response
|
|
}
|
|
|
|
func putCacheDataIntSub(key int32, subkey string, value int32) {
|
|
putCacheDataSub(key, subkey, fmt.Sprintf("%d", value))
|
|
}
|
|
|
|
func putCacheDataSub(key int32, subkey string, value string) {
|
|
putCacheData(fmt.Sprintf("%d.%s", key, subkey), value)
|
|
}
|
|
|
|
func putCacheDataIntSub64(key int64, subkey string, value int32) {
|
|
putCacheDataSub64(key, subkey, fmt.Sprintf("%d", value))
|
|
}
|
|
|
|
func putCacheDataSub64(key int64, subkey string, value string) {
|
|
putCacheData(fmt.Sprintf("%d.%s", key, subkey), value)
|
|
}
|
|
|
|
func putCacheData(key string, value string) {
|
|
db, err := sql.Open("sqlite3", *cacheDBPath)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
defer db.Close()
|
|
|
|
_, err = db.Exec("CREATE TABLE IF NOT EXISTS properties (id text NOT NULL PRIMARY KEY, value TEXT);")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
tx, err := db.Begin()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
stmt, err := tx.Prepare("INSERT OR REPLACE INTO properties(id, value) values(?, ?)")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
defer stmt.Close()
|
|
|
|
_, err = stmt.Exec(key, value)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
tx.Commit()
|
|
}
|
|
|
|
func getDatabaseToken() *oauth2.Token {
|
|
refreshToken := getCachedData("refreshToken")
|
|
//accessToken := getCachedData("accessToken")
|
|
|
|
token := new(oauth2.Token)
|
|
token.RefreshToken = refreshToken
|
|
//token.AccessToken = accessToken
|
|
token.TokenType = "Bearer"
|
|
|
|
return token
|
|
}
|