forked from EVEIPH/EVE-IPH
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ESI.vb
2486 lines (2036 loc) · 114 KB
/
ESI.vb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
Imports System.Net
Imports System.IO
Imports System.Net.Sockets
Imports System.Text
Imports System.Data.SQLite
Imports System.Collections.Specialized
Imports System.Threading
Imports Newtonsoft.Json
Public Module ESIGlobals
Public ESICharacterSkillsScope As String = "esi-skills.read_skills" ' only required scope to use IPH
End Module
Public Class ESI
Private Const ESIAuthorizeURL As String = "https://login.eveonline.com/oauth/authorize"
Private Const ESITokenURL As String = "https://login.eveonline.com/oauth/token"
Private Const ESIVerifyURL As String = "https://login.eveonline.com/oauth/verify"
Private Const ESIPublicURL As String = "https://esi.tech.ccp.is/latest/"
Private Const TranquilityDataSource As String = "?datasource=tranquility"
Private Const LocalHost As String = "127.0.0.1" ' All calls will redirect to local host.
Private Const LocalPort As String = "12500" ' Always use this port
Private ClientID As String
Private SecretKey As String
Public ScopesString As String
Public Const ESICharacterAssetScope As String = "esi-assets.read_assets"
Public Const ESICharacterResearchAgentsScope As String = "esi-characters.read_agents_research"
Public Const ESICharacterBlueprintsScope As String = "esi-characters.read_blueprints"
Public Const ESICharacterStandingsScope As String = "esi-characters.read_standings"
Public Const ESICharacterIndustryJobsScope As String = "esi-industry.read_character_jobs"
Public Const ESICharacterSkillsScope As String = "esi-skills.read_skill"
Public Const ESICorporationAssetScope As String = "esi-assets.read_corporation_assets"
Public Const ESICorporationBlueprintsScope As String = "esi-corporations.read_blueprints"
Public Const ESICorporationIndustryJobsScope As String = "esi-industry.read_corporation_jobs"
Public Const ESICorporationMembership As String = "esi-corporations.read_corporation_membership"
' Cache field names and times
Private Const IndustrySystemsField As String = "INDUSTRY_SYSTEMS_CACHED_UNTIL"
Private Const IndustrySystemsLength As Integer = 1
Private Const IndustryFacilitiesField As String = "INDUSTRY_FACILITIES_CACHED_UNTIL"
Private Const IndustryFacilitiesLength As Integer = 1
Private Const MarketPricesField As String = "MARKET_PRICES_CACHED_UNTIL"
Private Const MarketPricesLength As Integer = 23
' Rate limits
'For your requests, this means you can send an occasional burst of 400 requests all at once.
'If you do, you'll hit the rate limit once you try to send your 401st request unless you wait.
'Your bucket refills at a rate of 1 per 1/150th of a second. If you send 400 requests at once,
'you need to wait 2.67 seconds before you can send another 400 requests (1/150 * 400), if you
'only wait 1.33 seconds you can send another 200, and so on. Altrnatively, you can send a constant 150 requests every 1 second.
Private Const ESIRatePerSecond As Integer = 150 ' max requests per second
Private Const ESIBurstSize As Integer = 400 ' max burst of requests, which need 2.46 seconds to refill before re-bursting
Private Const ESIMaximumConnections As Integer = 20
Private IDToFind As Long ' for predicate
Private RetriedCall As Boolean
Private AuthThreadReference As Thread ' Reference in case we need to kill this
Private AuthStreamText As String ' The return data from the web call
Private myListner As TcpListener = New TcpListener(IPAddress.Parse(LocalHost), CInt(LocalPort)) ' Ref to the listener so we can stop it
' ESI implements the following scopes:
' Character
' esi-assets.read_assets.v1: Allows reading a list of assets that the character owns
' esi-characters.read_agents_research.v1: Allows reading a character's research status with agents
' esi-characters.read_blueprints.v1: Allows reading a character's blueprints
' esi-characters.read_standings.v1: Allows reading a character's standings
' esi-industry.read_character_jobs.v1: Allows reading a character's industry jobs
' esi-skills.read_skills.v1: Allows reading of a character's currently known skills.
' Corporation
' esi-assets.read_corporation_assets.v1: Allows reading of a character's corporation's assets, if the character has roles to do so.
' esi-corporations.read_blueprints.v1: Allows reading a corporation's blueprints
' esi-industry.read_corporation_jobs.v1: Allows reading of a character's corporation's industry jobs, if the character has roles to do so.
''' <summary>
''' Initialize the class and set the implemented scopes
''' </summary>
Public Sub New()
' Load all the details from the authorization information file
Dim ApplicationSettings As AppRegistrationInformationSettings = AllSettings.LoadAppRegistrationInformationSettings
With ApplicationSettings
ClientID = .ClientID
SecretKey = .SecretKey
ScopesString = String.Join(" ", .Scopes.Split(New String() {" ", ",", vbCr, vbLf}, StringSplitOptions.RemoveEmptyEntries))
End With
AuthStreamText = ""
RetriedCall = False
End Sub
Public Function GetClientID() As String
Return ClientID
End Function
''' <summary>
''' Opens a connection to EVE SSO server and gets an authorization token to get an access token
''' </summary>
''' <returns>Authorization Token</returns>
Private Function GetAuthorizationToken(ByRef ErrorCode As Integer) As String
Dim ErrorResponse As String = ""
Try
If ClientID <> "" And ClientID <> DummyClient Then
Dim StartTime As Date = Now
AuthThreadReference = New Thread(AddressOf GetAuthorizationfromWeb)
AuthThreadReference.Start()
' Now loop until thread is done, 60 seconds goes by, or cancel clicked
Do
If DateDiff(DateInterval.Second, StartTime, Now) > 60 Then
Call MsgBox("Request timed out. You must complete your login within 60 seconds.", vbInformation, Application.ProductName)
myListner.Stop()
AuthThreadReference.Abort()
Return Nothing
ElseIf CancelESISSOLogin Then
Call MsgBox("Request Canceled", vbInformation, Application.ProductName)
myListner.Stop()
AuthThreadReference.Abort()
Return Nothing
ElseIf Not AuthThreadReference.IsAlive Then
Exit Do
End If
Application.DoEvents()
Loop
' Process the auth stream now
Dim AuthTokenString As String() = AuthStreamText.Split(New Char() {" "c})
Dim AuthToken As String = ""
For i = 0 To AuthTokenString.Count - 1
If AuthTokenString(i).Contains("/?code=") Then
' Strip the header and save the string
Dim Start As Integer = InStr(AuthTokenString(i), "=")
AuthToken = AuthTokenString(i).Substring(Start)
End If
Next
Return AuthToken
End If
Catch ex As WebException
ErrorCode = CType(ex.Response, HttpWebResponse).StatusCode
ErrorResponse = GetErrorResponseBody(ex)
If ErrorCode >= 500 And Not RetriedCall Then
' Try this call again after waiting a few
Threading.Thread.Sleep(2000)
RetriedCall = True
Call GetAuthorizationToken(0)
End If
MsgBox("Web Request failed to get Authorization Token. Code: " & ErrorCode & ", " & ex.Message & " - " & ErrorResponse)
Catch ex As Exception
MsgBox("The request failed to get Authorization Token. " & ex.Message, vbInformation, Application.ProductName)
ErrorCode = -1
End Try
Return ""
RetriedCall = False
End Function
''' <summary>
''' Opens the login for EVE SSO and returns the data stream from a successful log in
''' </summary>
Private Sub GetAuthorizationfromWeb()
Try
' Build the authorization call
Dim URL As String = ESIAuthorizeURL & "?response_type=code" & "&redirect_uri=http://"
URL &= LocalHost & ":" & LocalPort & "&client_id=" & ClientID & "&scope=" & ScopesString
Process.Start(URL)
Dim mySocket As Socket = Nothing
Dim myStream As NetworkStream = Nothing
Dim myReader As StreamReader = Nothing
Dim myWriter As StreamWriter = Nothing
myListner.Start()
mySocket = myListner.AcceptSocket() ' Wait for response
Debug.Print("After socket listen")
myStream = New NetworkStream(mySocket)
myReader = New StreamReader(myStream)
myWriter = New StreamWriter(myStream)
myWriter.AutoFlush = True
Do
AuthStreamText &= myReader.ReadLine & "|"
If AuthStreamText.Contains("code") Then
Exit Do
End If
Loop Until myReader.EndOfStream
myWriter.Write("Login Successful!" & vbCrLf & vbCrLf & "You can close this window.")
myWriter.Close()
myReader.Close()
myStream.Close()
mySocket.Close()
myListner.Stop()
Application.DoEvents()
Catch ex As Exception
Application.DoEvents()
End Try
End Sub
''' <summary>
''' Gets the Access Token data from the EVE server for authorization or refresh tokens.
''' </summary>
''' <param name="Token">An Authorization or Refresh Token</param>
''' <param name="Refresh">If the token is Authorization or Refresh</param>
''' <returns>Access Token Data object</returns>
Private Function GetAccessToken(Token As String, Refresh As Boolean, ByRef ErrorCode As Integer) As ESITokenData
If Token = "No Token" Or Token = "" Then
Return Nothing
End If
Dim AccessTokenOutput As New ESITokenData
Dim Success As Boolean = False
Dim WC As New WebClient
Dim Response As Byte()
Dim Data As String = ""
Dim ErrorResponse As String = ""
Dim AuthHeader As String = $"Basic {Convert.ToBase64String(Encoding.UTF8.GetBytes($"{ClientID}:{SecretKey}"), Base64FormattingOptions.None)}"
WC.Headers(HttpRequestHeader.Authorization) = AuthHeader
WC.Proxy = Nothing
Dim PostParameters As New NameValueCollection
If Not Refresh Then
PostParameters.Add("grant_type", "authorization_code")
PostParameters.Add("code", Token)
Else
PostParameters.Add("grant_type", "refresh_token")
PostParameters.Add("refresh_token", Token)
End If
Try
Response = WC.UploadValues(ESITokenURL, "POST", PostParameters)
' Convert byte data to string
Data = Encoding.UTF8.GetString(Response)
' Parse the data to the class
AccessTokenOutput = JsonConvert.DeserializeObject(Of ESITokenData)(Data)
Success = True
Catch ex As WebException
ErrorCode = CType(ex.Response, HttpWebResponse).StatusCode
ErrorResponse = GetErrorResponseBody(ex)
If ErrorCode >= 500 And Not RetriedCall Then
' Try this call again after waiting a few
RetriedCall = True
Threading.Thread.Sleep(2000)
Call GetAccessToken(Token, Refresh, 0)
End If
MsgBox("Web Request failed to get Access Token. Code: " & ErrorCode & ", " & ex.Message & " - " & errorresponse)
Catch ex As Exception
MsgBox("The request failed to get Access Token. " & ex.Message, vbInformation, Application.ProductName)
ErrorCode = -1
End Try
RetriedCall = False
If Success Then
Return AccessTokenOutput
Else
Return Nothing
End If
End Function
''' <summary>
''' Queries the server for public data for the URL sent. If not found, returns nothing
''' </summary>
''' <param name="URL">Full public data URL as a string</param>
''' <returns>Byte Array of response or nothing if call fails</returns>
Private Function GetPublicData(ByVal URL As String, ByRef CacheDate As Date, Optional BodyData As String = "") As String
Dim Response As String = ""
Dim WC As New WebClient
Dim ErrorCode As Integer = 0
Dim ErrorResponse As String = ""
WC.Proxy = Nothing
Try
If BodyData <> "" Then
Response = Encoding.UTF8.GetString(WC.UploadData(URL, Encoding.UTF8.GetBytes(BodyData)))
Else
Response = WC.DownloadString(URL)
End If
' Get the expiration date for the cache date
Dim myWebHeaderCollection As WebHeaderCollection = WC.ResponseHeaders
Dim Expires As String = myWebHeaderCollection.Item("Expires")
Dim Pages As Integer = CInt(myWebHeaderCollection.Item("X-Pages"))
If Not IsNothing(Expires) Then
CacheDate = CDate(Expires.Replace("GMT", "").Substring(InStr(Expires, ",") + 1)) ' Expiration date is in GMT
Else
CacheDate = NoExpiry
End If
Return Response
Catch ex As WebException
ErrorCode = CType(ex.Response, HttpWebResponse).StatusCode
ErrorResponse = GetErrorResponseBody(ex)
If ErrorCode >= 500 And Not RetriedCall Then
RetriedCall = True
' Try this call again after waiting a few
Threading.Thread.Sleep(2000)
Return GetPublicData(URL, CacheDate, BodyData)
End If
MsgBox("Web Request failed to get Public data. Code: " & ErrorCode & ", " & ex.Message & " - " & errorresponse)
Catch ex As Exception
MsgBox("The request failed to get Public data. " & ex.Message, vbInformation, Application.ProductName)
End Try
RetriedCall = False
If Response <> "" Then
Return Response
Else
Return Nothing
End If
End Function
''' <summary>
''' Queries the server for private, authorized data for data sent. Function will check the
''' authorization token and update the sent variable and DB data if expired.
''' </summary>
''' <returns>Returns data response as a string</returns>
Private Function GetPrivateAuthorizedData(ByVal URL As String, ByRef TokenData As ESITokenData,
ByVal TokenExpiration As Date, ByRef CacheDate As Date,
ByVal CharacterID As Long) As String
Dim WC As New WebClient
Dim ErrorCode As Integer = 0
Dim ErrorResponse As String = ""
Dim Response As String = ""
WC.Proxy = Nothing
' See if we update the token data first
If TokenExpiration <= DateTime.UtcNow Then
' Update the token
TokenData = GetAccessToken(TokenData.refresh_token, True, ErrorCode)
' Update the token data in the DB for this character
Dim SQL As String = ""
' Update data - only stuff that could (reasonably) change
SQL = "UPDATE ESI_CHARACTER_DATA SET ACCESS_TOKEN = '{0}', ACCESS_TOKEN_EXPIRE_DATE_TIME = '{1}', "
SQL &= "TOKEN_TYPE = '{2}', REFRESH_TOKEN = '{3}' WHERE CHARACTER_ID = {4}"
With TokenData
SQL = String.Format(SQL, FormatDBString(.access_token),
Format(DateAdd(DateInterval.Second, TokenData.expires_in, DateTime.UtcNow), SQLiteDateFormat),
FormatDBString(.token_type), FormatDBString(.refresh_token), CharacterID)
End With
End If
If ErrorCode = 0 Then
Try
Dim Auth_header As String = $"Bearer {TokenData.access_token}"
WC.Headers(HttpRequestHeader.Authorization) = Auth_header
Response = WC.DownloadString(URL)
' Get the expiration date for the cache date
Dim myWebHeaderCollection As WebHeaderCollection = WC.ResponseHeaders
Dim Expires As String = myWebHeaderCollection.Item("Expires")
Dim Pages As Integer = CInt(myWebHeaderCollection.Item("X-Pages"))
If Not IsNothing(Expires) Then
CacheDate = CDate(Expires.Replace("GMT", "").Substring(InStr(Expires, ",") + 1)) ' Expiration date is in GMT
Else
CacheDate = NoExpiry
End If
If Not IsNothing(Pages) Then
If Pages > 1 Then
Dim TempResponse As String = ""
For i = 2 To Pages
TempResponse = WC.DownloadString(URL & "&page=" & CStr(i))
' Combine with the original response - strip the end and leading brackets
Response = Response.Substring(0, Len(Response) - 1) & "," & TempResponse.Substring(1)
Next
End If
End If
Return Response
Catch ex As WebException
ErrorCode = CType(ex.Response, HttpWebResponse).StatusCode
ErrorResponse = GetErrorResponseBody(ex)
If ErrorResponse = "Character not in corporation" Then
' Assume this error came from checking on NPC corp roles and just exit with nothing
Exit Try
End If
If ErrorCode >= 500 And Not RetriedCall Then
RetriedCall = True
' Try this call again after waiting a few
Threading.Thread.Sleep(2000)
Return GetPrivateAuthorizedData(URL, TokenData, TokenExpiration, CacheDate, CharacterID)
End If
MsgBox("Web Request failed to get Authorized data. Code: " & ErrorCode & ", " & ex.Message & " - " & ErrorResponse)
Catch ex As Exception
MsgBox("The request failed to get Authorized data. " & ex.Message, vbInformation, Application.ProductName)
End Try
End If
RetriedCall = False
If Response <> "" Then
Return Response
Else
Return Nothing
End If
End Function
''' <summary>
''' Formats a SavedTokenData object to ESITokenData
''' </summary>
''' <param name="TokenData">SavedTokenData object</param>
''' <returns>the ESITokenData object</returns>
Private Function FormatTokenData(ByVal TokenData As SavedTokenData) As ESITokenData
Dim TempTokenData As New ESITokenData
TempTokenData.access_token = TokenData.AccessToken
TempTokenData.refresh_token = TokenData.RefreshToken
TempTokenData.token_type = TokenData.TokenType
Return TempTokenData
End Function
''' <summary>
''' Formats string dates returned from the ESI server into a date object
''' </summary>
''' <param name="OriginalDate">ESI date value as a string</param>
''' <returns>Date object of the sent date as a string</returns>
Public Function FormatESIDate(ByVal OriginalDate As String) As Date
If Not IsNothing(OriginalDate) Then
Return CDate(OriginalDate.Replace("T", " ").Replace("Z", ""))
Else
Return NoDate
End If
End Function
#Region "Load Character Data"
''' <summary>
''' Gets verification and public data about the character returned from logging into the EVE SSO and authorizing
''' access for a new character first logging in. If the character has already been loaded, then update the data.
''' </summary>
''' <returns>Returns boolean if the function was successful in setting character data.</returns>
Public Function SetCharacterData(Optional ByRef CharacterTokenData As SavedTokenData = Nothing, Optional IgnoreCacheDate As Boolean = False) As Boolean
Dim TokenData As ESITokenData
Dim CharacterData As New ESICharacterData
Dim CharacterID As Long
Dim ErrorCode As Integer = 0
If Not IsNothing(CharacterTokenData) Then
CharacterID = CharacterTokenData.CharacterID
Else
CharacterID = 0
End If
Try
If CharacterID = 0 Then
' We need to get the token data from the authorization
TokenData = GetAccessToken(GetAuthorizationToken(ErrorCode), False, ErrorCode)
CharacterTokenData = New SavedTokenData
Else
' We need to refresh the token data
TokenData = GetAccessToken(CharacterTokenData.RefreshToken, True, ErrorCode)
' Update the local copy with the new information
CharacterTokenData.AccessToken = TokenData.access_token
CharacterTokenData.RefreshToken = TokenData.refresh_token
CharacterTokenData.TokenType = TokenData.token_type
End If
If ErrorCode = 0 And Not IsNothing(TokenData) Then
Dim CB As New CacheBox
Dim CacheDate As Date
' Set the expiration date to pass
CharacterTokenData.TokenExpiration = DateAdd(DateInterval.Second, TokenData.expires_in, DateTime.UtcNow)
If CB.DataUpdateable(CacheDateType.PublicCharacterData, CharacterID) Or IgnoreCacheDate Then
' Now with the token data, look up the character data
CharacterData.VerificationData = GetCharacterVerificationData(TokenData, CharacterTokenData.TokenExpiration)
CharacterData.PublicData = GetCharacterPublicData(CharacterData.VerificationData.CharacterID, CacheDate)
' Save it in the table if not there, or update it if they selected the character again
Dim rsCheck As SQLiteDataReader
Dim SQL As String
SQL = "SELECT * FROM ESI_CHARACTER_DATA WHERE CHARACTER_ID = " & CStr(CharacterData.VerificationData.CharacterID)
DBCommand = New SQLiteCommand(SQL, EVEDB.DBREf)
rsCheck = DBCommand.ExecuteReader
If rsCheck.HasRows Then
' Update data - only stuff that could (reasonably) change
SQL = "UPDATE ESI_CHARACTER_DATA SET CORPORATION_ID = {0}, DESCRIPTION = '{1}', SCOPES = '{2}', ACCESS_TOKEN = '{3}',"
SQL &= "ACCESS_TOKEN_EXPIRE_DATE_TIME = '{4}', TOKEN_TYPE = '{5}', REFRESH_TOKEN = '{6}' "
SQL &= "WHERE CHARACTER_ID = {7}"
With CharacterData
SQL = String.Format(SQL, .PublicData.corporation_id,
FormatDBString(FormatNullString(.PublicData.description)),
FormatDBString(.VerificationData.Scopes),
FormatDBString(TokenData.access_token),
Format(CharacterTokenData.TokenExpiration, SQLiteDateFormat),
FormatDBString(TokenData.token_type),
FormatDBString(TokenData.refresh_token),
.VerificationData.CharacterID)
End With
Else
' Insert new data
SQL = "INSERT INTO ESI_CHARACTER_DATA (CHARACTER_ID, CHARACTER_NAME, CORPORATION_ID, BIRTHDAY, GENDER, RACE_ID, "
SQL &= "BLOODLINE_ID, ANCESTRY_ID, DESCRIPTION, ACCESS_TOKEN, ACCESS_TOKEN_EXPIRE_DATE_TIME, TOKEN_TYPE, REFRESH_TOKEN, "
SQL &= "SCOPES, OVERRIDE_SKILLS, IS_DEFAULT)"
SQL &= "VALUES ({0},'{1}',{2},'{3}','{4}',{5},{6},{7},'{8}','{9}','{10}','{11}','{12}','{13}',{14},{15})"
With CharacterData
SQL = String.Format(SQL, .VerificationData.CharacterID,
FormatDBString(.VerificationData.CharacterName),
.PublicData.corporation_id,
Format(CDate(.PublicData.birthday.Replace("T", " ")), SQLiteDateFormat),
FormatDBString(.PublicData.gender),
.PublicData.race_id,
.PublicData.bloodline_id,
FormatNullInteger(.PublicData.ancestry_id),
FormatDBString(FormatNullString(.PublicData.description)),
FormatDBString(TokenData.access_token),
Format(CharacterTokenData.TokenExpiration, SQLiteDateFormat),
FormatDBString(TokenData.token_type),
FormatDBString(TokenData.refresh_token),
FormatDBString(.VerificationData.Scopes),
0, 0) ' Don't set default yet or override skills
End With
End If
EVEDB.ExecuteNonQuerySQL(SQL)
' Update public cachedate for character now that we have a record
Call CB.UpdateCacheDate(CacheDateType.PublicCharacterData, CacheDate, CLng(CharacterData.VerificationData.CharacterID))
' While we are here, load the public information of the corporation too
Call SetCorporationData(CharacterData.PublicData.corporation_id, CacheDate)
' Update after we update/insert the record
Call CB.UpdateCacheDate(CacheDateType.PublicCorporationData, CacheDate, CharacterData.PublicData.corporation_id)
If CharacterID = 0 Then
MsgBox("Character successfully added to IPH", vbInformation, Application.ProductName)
End If
Return True
Else
If ErrorCode = 0 Then
' Just didn't need to update yet
Return True
End If
If ErrorCode <> 400 Then
MsgBox("Unable to load the selected character to IPH", vbExclamation, Application.ProductName)
End If
End If
End If
Catch ex As Exception
MsgBox("Unable to get authorization and verification data through ESI: " & ex.Message, vbInformation, Application.ProductName)
End Try
Return False
End Function
''' <summary>
''' Retrieves the public data about the character ID sent
''' </summary>
''' <param name="CharacterID">CharacterID you want public data for</param>
''' <returns>Returns data in the ESICharacterPublicData JSON property class</returns>
Public Function GetCharacterPublicData(ByVal CharacterID As String, ByRef DataCacheDate As Date) As ESICharacterPublicData
Dim CharacterData As ESICharacterPublicData
Dim PublicData As String
PublicData = GetPublicData(ESIPublicURL & "characters/" & CStr(CharacterID) & "/" & TranquilityDataSource, DataCacheDate)
CharacterData = JsonConvert.DeserializeObject(Of ESICharacterPublicData)(PublicData)
Return CharacterData
End Function
''' <summary>
''' Gets the character verification data when sent the refresh token
''' </summary>
''' <param name="TokenData"></param>
''' <param name="ExpirationDate"></param>
''' <returns>Character Verification Data object</returns>
Public Function GetCharacterVerificationData(ByVal TokenData As ESITokenData, ByVal ExpirationDate As Date) As ESICharacterVerificationData
Dim CacheDate As Date
Dim WC As New WebClient
Dim ErrorCode As Integer = 0
Dim ErrorResponse As String = ""
Dim Response As String = ""
WC.Proxy = Nothing
' See if we update the token data first
If ExpirationDate <= DateTime.UtcNow Then
' Update the token
TokenData = GetAccessToken(TokenData.refresh_token, True, ErrorCode)
End If
If ErrorCode = 0 Then
Try
Dim Auth_header As String = $"Bearer {TokenData.access_token}"
WC.Headers(HttpRequestHeader.Authorization) = Auth_header
Response = WC.DownloadString(ESIVerifyURL)
' Get the expiration date for the cache date
Dim myWebHeaderCollection As WebHeaderCollection = WC.ResponseHeaders
Dim Expires As String = myWebHeaderCollection.Item("Expires")
Dim Pages As Integer = CInt(myWebHeaderCollection.Item("X-Pages"))
If Not IsNothing(Expires) Then
CacheDate = CDate(Expires.Replace("GMT", "").Substring(InStr(Expires, ",") + 1)) ' Expiration date is in GMT
Else
CacheDate = NoExpiry
End If
If Not IsNothing(Pages) Then
If Pages > 1 Then
Dim TempResponse As String = ""
For i = 2 To Pages
TempResponse = WC.DownloadString(ESIVerifyURL & "&page=" & CStr(i))
' Combine with the original response - strip the end and leading brackets
Response = Response.Substring(0, Len(Response) - 1) & "," & TempResponse.Substring(1)
Next
End If
End If
Return JsonConvert.DeserializeObject(Of ESICharacterVerificationData)(Response)
Catch ex As WebException
ErrorCode = CType(ex.Response, HttpWebResponse).StatusCode
ErrorResponse = GetErrorResponseBody(ex)
If ErrorCode >= 500 And Not RetriedCall Then
RetriedCall = True
' Try this call again after waiting a few
Thread.Sleep(2000)
Return GetCharacterVerificationData(TokenData, ExpirationDate)
End If
MsgBox("Web Request failed to get Authorized data. Code: " & ErrorCode & ", " & ex.Message & " - " & errorresponse)
Catch ex As Exception
MsgBox("The request failed to get Authorized data. " & ex.Message, vbInformation, Application.ProductName)
End Try
End If
RetriedCall = False
Return Nothing
End Function
#End Region
#Region "Scopes Processing"
Public Function GetCharacterSkills(ByVal CharacterID As Long, ByVal TokenData As SavedTokenData, ByRef SkillsCacheDate As Date) As EVESkillList
Dim SkillData As New ESICharacterSkillsBase
Dim ReturnData As String
Dim ReturnSkills As New EVESkillList
Dim TempSkill As New EVESkill
Dim TempTokenData As New ESITokenData
TempTokenData = FormatTokenData(TokenData)
ReturnData = GetPrivateAuthorizedData(ESIPublicURL & "characters/" & CStr(CharacterID) & "/skills/" & TranquilityDataSource, TempTokenData, TokenData.TokenExpiration, SkillsCacheDate, CharacterID)
If Not IsNothing(ReturnData) Then
SkillData = JsonConvert.DeserializeObject(Of ESICharacterSkillsBase)(ReturnData)
For Each entry In SkillData.skills
TempSkill = New EVESkill
TempSkill.TypeID = entry.skill_id
TempSkill.Level = entry.trained_skill_level
TempSkill.SkillPoints = entry.skillpoints_in_skill
Call ReturnSkills.InsertSkill(TempSkill, True)
Next
Return ReturnSkills
Else
Return Nothing
End If
End Function
Public Function GetCharacterStandings(ByVal CharacterID As Long, ByVal TokenData As SavedTokenData, ByRef StandingsCacheDate As Date) As EVENPCStandings
Dim TempStandingsList As New EVENPCStandings
Dim StandingsData As List(Of ESICharacterStandingsData)
Dim ReturnData As String = ""
Dim StandingType As String = ""
Dim TempTokenData As New ESITokenData
TempTokenData = FormatTokenData(TokenData)
ReturnData = GetPrivateAuthorizedData(ESIPublicURL & "characters/" & CStr(CharacterID) & "/standings/" & TranquilityDataSource, TempTokenData, TokenData.TokenExpiration, StandingsCacheDate, CharacterID)
If Not IsNothing(ReturnData) Then
StandingsData = JsonConvert.DeserializeObject(Of List(Of ESICharacterStandingsData))(ReturnData)
For Each entry In StandingsData
Select Case entry.from_type
Case "agents"
StandingType = "Agent"
Case "faction"
StandingType = "Faction"
Case "npc_corp"
StandingType = "Corporation"
End Select
TempStandingsList.InsertStanding(entry.from_id, StandingType, "", entry.standing)
Next
Return TempStandingsList
Else
Return Nothing
End If
End Function
Public Function GetCurrentResearchAgents(ByVal CharacterID As Long, ByVal TokenData As SavedTokenData, ByRef AgentsCacheDate As Date) As List(Of ESIResearchAgent)
Dim ReturnData As String
Dim TempTokenData As New ESITokenData
TempTokenData = FormatTokenData(TokenData)
ReturnData = GetPrivateAuthorizedData(ESIPublicURL & "characters/" & CStr(CharacterID) & "/agents_research/" & TranquilityDataSource, TempTokenData, TokenData.TokenExpiration, AgentsCacheDate, CharacterID)
If Not IsNothing(ReturnData) Then
Return JsonConvert.DeserializeObject(Of List(Of ESIResearchAgent))(ReturnData)
Else
Return Nothing
End If
End Function
Public Function GetBlueprints(ByVal ID As Long, ByVal TokenData As SavedTokenData, ByVal ScanType As ScanType, ByRef BPCacheDate As Date) As List(Of EVEBlueprint)
Dim ReturnedBPs As New List(Of EVEBlueprint)
Dim TempBlueprint As EVEBlueprint
Dim RawBPData As New List(Of ESIBlueprint)
Dim ReturnData As String = ""
Dim TempTokenData As New ESITokenData
TempTokenData = FormatTokenData(TokenData)
Dim rsLookup As SQLiteDataReader
' Set up query string
If ScanType = ScanType.Personal Then
ReturnData = GetPrivateAuthorizedData(ESIPublicURL & "characters/" & CStr(ID) & "/blueprints/" & TranquilityDataSource,
TempTokenData, TokenData.TokenExpiration, BPCacheDate, ID)
Else ' Corp
ReturnData = GetPrivateAuthorizedData(ESIPublicURL & "corporations/" & CStr(ID) & "/blueprints/" & TranquilityDataSource,
TempTokenData, TokenData.TokenExpiration, BPCacheDate, ID)
End If
If Not IsNothing(ReturnData) Then
RawBPData = JsonConvert.DeserializeObject(Of List(Of ESIBlueprint))(ReturnData)
' Process the return data
For Each BP In RawBPData
TempBlueprint.ItemID = BP.item_id
TempBlueprint.TypeID = BP.type_id
' Get the typeName for this bp
DBCommand = New SQLiteCommand("SELECT typeName FROM INVENTORY_TYPES WHERE typeID = " & CStr(BP.type_id), EVEDB.DBREf)
rsLookup = DBCommand.ExecuteReader
If rsLookup.Read Then
TempBlueprint.TypeName = rsLookup.GetString(0)
Else
TempBlueprint.TypeName = Unknown
End If
rsLookup.Close()
TempBlueprint.LocationID = BP.location_id
' Get the flag id for this location
DBCommand = New SQLiteCommand("SELECT flagID FROM INVENTORY_FLAGS WHERE flagText = '" & BP.location_flag & "'", EVEDB.DBREf)
rsLookup = DBCommand.ExecuteReader
If rsLookup.Read Then
TempBlueprint.FlagID = rsLookup.GetInt32(0)
Else
TempBlueprint.FlagID = 0
End If
rsLookup.Close()
TempBlueprint.Quantity = BP.quantity
TempBlueprint.MaterialEfficiency = BP.material_efficiency
TempBlueprint.TimeEfficiency = BP.time_efficiency
TempBlueprint.Runs = BP.runs
' We determine the type of bp from quantity
If TempBlueprint.Quantity = BPType.Original Or TempBlueprint.Quantity > 0 Then
' BPO or stack of BPOs
TempBlueprint.BPType = BPType.Original
ElseIf TempBlueprint.Quantity = BPType.Copy Then
' BPC
TempBlueprint.BPType = BPType.Copy
Else
' Not sure what this is
TempBlueprint.BPType = 0
End If
TempBlueprint.Owned = False
TempBlueprint.Scanned = True ' We just scanned it
TempBlueprint.Favorite = False
TempBlueprint.AdditionalCosts = 0
ReturnedBPs.Add(TempBlueprint)
Next
Return ReturnedBPs
Else
Return Nothing
End If
End Function
Public Function GetIndustryJobs(ByVal ID As Long, ByVal TokenData As SavedTokenData, ByVal JobType As ScanType, ByRef JobsCacheDate As Date) As List(Of ESIIndustryJob)
Dim ReturnData As String = ""
Dim TempTokenData As New ESITokenData
TempTokenData = FormatTokenData(TokenData)
' Set up query string
If JobType = ScanType.Personal Then
ReturnData = GetPrivateAuthorizedData(ESIPublicURL & "characters/" & CStr(ID) & "/industry/jobs/" & TranquilityDataSource,' & "&include_completed=true",
TempTokenData, TokenData.TokenExpiration, JobsCacheDate, ID)
Else ' Corp
ReturnData = GetPrivateAuthorizedData(ESIPublicURL & "corporations/" & CStr(ID) & "/industry/jobs/" & TranquilityDataSource,
TempTokenData, TokenData.TokenExpiration, JobsCacheDate, ID)
End If
If Not IsNothing(ReturnData) Then
Return JsonConvert.DeserializeObject(Of List(Of ESIIndustryJob))(ReturnData)
Else
Return Nothing
End If
End Function
Public Function GetAssets(ByVal ID As Long, ByVal TokenData As SavedTokenData, ByVal JobType As ScanType, ByRef AssetsCacheDate As Date) As List(Of ESIAsset)
Dim AssetList As New List(Of EVEAsset)
Dim TempAsset As New EVEAsset
Dim ReturnData As String = ""
Dim TempTokenData As New ESITokenData
TempTokenData = FormatTokenData(TokenData)
' Set up query string
If JobType = ScanType.Personal Then
ReturnData = GetPrivateAuthorizedData(ESIPublicURL & "characters/" & CStr(ID) & "/assets/" & TranquilityDataSource,
TempTokenData, TokenData.TokenExpiration, AssetsCacheDate, ID)
Else ' Corp
ReturnData = GetPrivateAuthorizedData(ESIPublicURL & "corporations/" & CStr(ID) & "/assets/" & TranquilityDataSource,
TempTokenData, TokenData.TokenExpiration, AssetsCacheDate, ID)
End If
If Not IsNothing(ReturnData) Then
Return JsonConvert.DeserializeObject(Of List(Of ESIAsset))(ReturnData)
Else
Return Nothing
End If
End Function
Public Function GetCorpRoles(ByVal CharacterID As Long, ByVal CorporationID As Long, ByVal TokenData As SavedTokenData, ByRef RolesCacheDate As Date) As List(Of ESICorporationRoles)
Dim ReturnData As String
Dim TempTokenData As New ESITokenData
TempTokenData = FormatTokenData(TokenData)
ReturnData = GetPrivateAuthorizedData(ESIPublicURL & "corporations/" & CStr(CorporationID) & "/roles/" & TranquilityDataSource, TempTokenData, TokenData.TokenExpiration, RolesCacheDate, CharacterID)
If Not IsNothing(ReturnData) Then
Return JsonConvert.DeserializeObject(Of List(Of ESICorporationRoles))(ReturnData)
Else
Return Nothing
End If
End Function
Public Sub SetCorporationData(ByVal ID As Long, ByRef DataCacheDate As Date)
Dim ReturnData As String = ""
Dim SQL As String = ""
Dim CorpData As ESICorporation = Nothing
' Set up query string
ReturnData = GetPublicData(ESIPublicURL & "corporations/" & CStr(ID) & TranquilityDataSource, DataCacheDate)
If Not IsNothing(ReturnData) Then
CorpData = JsonConvert.DeserializeObject(Of ESICorporation)(ReturnData)
If Not IsNothing(CorpData) Then
Call EVEDB.BeginSQLiteTransaction()
' See if we insert or update
Dim rsCheck As SQLiteDataReader
' Load up all the data for the corporation
SQL = "SELECT * FROM ESI_CORPORATION_DATA WHERE CORPORATION_ID = " & ID
DBCommand = New SQLiteCommand(SQL, EVEDB.DBREf)
rsCheck = DBCommand.ExecuteReader
If rsCheck.Read Then
' Found a record, so update the data
With CorpData
SQL = "UPDATE ESI_CORPORATION_DATA SET "
SQL &= "CORPORATION_NAME = " & BuildInsertFieldString(.name) & ","
SQL &= "TICKER = " & BuildInsertFieldString(.ticker) & ","
SQL &= "MEMBER_COUNT = " & BuildInsertFieldString(.member_count) & ","
SQL &= "FACTION_ID = " & BuildInsertFieldString(.faction_id) & ","
SQL &= "ALLIANCE_ID = " & BuildInsertFieldString(.alliance_id) & ","
SQL &= "CEO_ID = " & BuildInsertFieldString(.ceo_id) & ","
SQL &= "CREATOR_ID = " & BuildInsertFieldString(.creator_id) & ","
SQL &= "HOME_STATION_ID = " & BuildInsertFieldString(.home_station_id) & ","
SQL &= "SHARES = " & BuildInsertFieldString(.shares) & ","
SQL &= "TAX_RATE = " & BuildInsertFieldString(.tax_rate) & ","
SQL &= "DESCRIPTION = " & BuildInsertFieldString(.description) & ","
SQL &= "DATE_FOUNDED = " & BuildInsertFieldString(.date_founded) & ","
SQL &= "URL = " & BuildInsertFieldString(.date_founded) & " "
SQL &= "WHERE CORPORATION_ID = " & CStr(ID)
End With
Else
' New record
With CorpData
SQL = "INSERT INTO ESI_CORPORATION_DATA VALUES ("
SQL &= BuildInsertFieldString(ID) & ","
SQL &= BuildInsertFieldString(.name) & ","
SQL &= BuildInsertFieldString(.ticker) & ","
SQL &= BuildInsertFieldString(.member_count) & ","
SQL &= BuildInsertFieldString(.faction_id) & ","
SQL &= BuildInsertFieldString(.alliance_id) & ","
SQL &= BuildInsertFieldString(.ceo_id) & ","
SQL &= BuildInsertFieldString(.creator_id) & ","
SQL &= BuildInsertFieldString(.home_station_id) & ","
SQL &= BuildInsertFieldString(.shares) & ","
SQL &= BuildInsertFieldString(.tax_rate) & ","
SQL &= BuildInsertFieldString(.description) & ","
SQL &= BuildInsertFieldString(.date_founded) & ","
SQL &= BuildInsertFieldString(.url) & ","
SQL &= "NULL,NULL,NULL,NULL,NULL)"
End With
End If
Call EVEDB.ExecuteNonQuerySQL(SQL)
Call EVEDB.CommitSQLiteTransaction()
DBCommand = Nothing
End If
End If
End Sub
#End Region
#Region "Public Data"
' Gets the ESI file from CCP for the current Market orders (buy and sell) for the region_id and type_id sent
' Open transaction will open an SQL transaction here instead of the calling function
' Returns boolean if the history was updated or not
Public Function UpdateMarketOrders(ByRef MHDB As DBConnection, ByVal TypeID As Long, ByVal RegionID As Long,
Optional OpenTransaction As Boolean = True,
Optional IgnoreCacheLookup As Boolean = False) As Boolean
Dim MarketOrdersOutput As List(Of ESIMarketOrder)
Dim SQL As String
Dim rsCache As SQLiteDataReader