mirror of
https://github.com/JustArchiNET/ArchiSteamFarm.git
synced 2025-12-16 14:30:31 +00:00
Compare commits
518 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3cdc93d373 | ||
|
|
36e99d9139 | ||
|
|
7bee2d468b | ||
|
|
8630cc40c8 | ||
|
|
3683195a0e | ||
|
|
7f5b946645 | ||
|
|
fdb194fe67 | ||
|
|
9063b9206b | ||
|
|
8d300894e5 | ||
|
|
3a0d3c444e | ||
|
|
8118fe0690 | ||
|
|
344c2ad23d | ||
|
|
d1a6613541 | ||
|
|
3dc88c65aa | ||
|
|
46384829c9 | ||
|
|
415ee8cc57 | ||
|
|
351d45e049 | ||
|
|
f81bbc60c5 | ||
|
|
eb6e93a172 | ||
|
|
e17c3ecf2a | ||
|
|
048b0fb538 | ||
|
|
ac7ecb6bb4 | ||
|
|
fcaf038dac | ||
|
|
f3da5d6afc | ||
|
|
84f33fcef4 | ||
|
|
22f0d423a3 | ||
|
|
f1d7609796 | ||
|
|
77386ecae5 | ||
|
|
4e86d21ef8 | ||
|
|
044fc87691 | ||
|
|
79fad62a4d | ||
|
|
6622d3b147 | ||
|
|
6b6d5429ad | ||
|
|
d59d0a8415 | ||
|
|
e3100d3938 | ||
|
|
42c020e552 | ||
|
|
554273833b | ||
|
|
093a29df62 | ||
|
|
31aa6b2e4a | ||
|
|
9f7ecdf054 | ||
|
|
7c4c74bf84 | ||
|
|
b8f03abd8b | ||
|
|
47f846540b | ||
|
|
bc14713079 | ||
|
|
770a8fee66 | ||
|
|
1df9af08e6 | ||
|
|
27464f6120 | ||
|
|
dfd45c6e25 | ||
|
|
adc1759cee | ||
|
|
88369ec71a | ||
|
|
a5d8ae53dd | ||
|
|
cd7b65868a | ||
|
|
7575704a01 | ||
|
|
b6ce8f435c | ||
|
|
565acca9fb | ||
|
|
610954ba73 | ||
|
|
74a748b03f | ||
|
|
585a075ec9 | ||
|
|
891d40afe1 | ||
|
|
387f0dd1c7 | ||
|
|
8b4d3c219c | ||
|
|
365877ec89 | ||
|
|
f03a43d573 | ||
|
|
d15a9cbfca | ||
|
|
acfad624fb | ||
|
|
03a7d5f4ac | ||
|
|
0f96d84d36 | ||
|
|
0edc9f4ff6 | ||
|
|
d11e141ece | ||
|
|
9ff3834ed7 | ||
|
|
9806703cfa | ||
|
|
ca0857abd1 | ||
|
|
d32d3bfd36 | ||
|
|
6efb07eada | ||
|
|
2b28f01b1c | ||
|
|
fb350fe792 | ||
|
|
5e73d18cd2 | ||
|
|
3ff95995b9 | ||
|
|
17b2bc2259 | ||
|
|
4a36345635 | ||
|
|
e9d8f271a2 | ||
|
|
6c84f8eb4f | ||
|
|
54ad58a22d | ||
|
|
790e6baf46 | ||
|
|
c8fb715558 | ||
|
|
2ab5e6013d | ||
|
|
3e0c34e62c | ||
|
|
65b31e5537 | ||
|
|
62a6e38e47 | ||
|
|
0b50a45336 | ||
|
|
8d1d162b02 | ||
|
|
dbe13a1965 | ||
|
|
fc13633f5e | ||
|
|
d0cc10f3c6 | ||
|
|
567931a4cc | ||
|
|
396dc17ab2 | ||
|
|
288cc29338 | ||
|
|
844ca7da94 | ||
|
|
b14b9f87c7 | ||
|
|
8aa086cc27 | ||
|
|
cf00989d84 | ||
|
|
8f2f85282c | ||
|
|
fa12ffd9d0 | ||
|
|
ecb27adedd | ||
|
|
6e9be09944 | ||
|
|
6a79a89a10 | ||
|
|
d67be4f092 | ||
|
|
a8e1039e32 | ||
|
|
2ad9d9e197 | ||
|
|
fd6e2c72d7 | ||
|
|
1eed0f7647 | ||
|
|
c018c08260 | ||
|
|
72fa98cb89 | ||
|
|
71215d695e | ||
|
|
75b785d4b6 | ||
|
|
12e32692cb | ||
|
|
6158a9268d | ||
|
|
a48e53585a | ||
|
|
33383633ea | ||
|
|
3f7359c608 | ||
|
|
d8b59c6889 | ||
|
|
0f71b788cb | ||
|
|
d012255a21 | ||
|
|
8fc39a44cd | ||
|
|
c6fe424fcc | ||
|
|
12488dafd3 | ||
|
|
3b53491567 | ||
|
|
e337bd3856 | ||
|
|
cc47c0a764 | ||
|
|
22e4c0cd40 | ||
|
|
20b47e1787 | ||
|
|
941da0658d | ||
|
|
8048d4a7aa | ||
|
|
2554794daa | ||
|
|
4fcee90b99 | ||
|
|
761d73eb90 | ||
|
|
c163e5e2f3 | ||
|
|
8fd41cc587 | ||
|
|
f195563ba5 | ||
|
|
33df54365a | ||
|
|
4a6ae3064a | ||
|
|
84857e060b | ||
|
|
3311f2a703 | ||
|
|
5666ebf891 | ||
|
|
0ded4f3b80 | ||
|
|
4c9354308c | ||
|
|
0bcf2f35b7 | ||
|
|
b93d5187d6 | ||
|
|
1426922854 | ||
|
|
d8925f9409 | ||
|
|
4302029bee | ||
|
|
01a7800a44 | ||
|
|
ab3cf5b8d0 | ||
|
|
2ab9741fa8 | ||
|
|
f1c3339764 | ||
|
|
da01d4eab3 | ||
|
|
cd9bd5f0dd | ||
|
|
2602ac3623 | ||
|
|
2fe8db712e | ||
|
|
687b20bcb5 | ||
|
|
23d9dcffe7 | ||
|
|
42623ffb5a | ||
|
|
214003edbd | ||
|
|
c21333b6e9 | ||
|
|
630f1c008c | ||
|
|
68977b7907 | ||
|
|
6dade5440e | ||
|
|
10fcfae171 | ||
|
|
320055680b | ||
|
|
84a99c3696 | ||
|
|
df415c8db5 | ||
|
|
9bc04640a1 | ||
|
|
d6c1bdb9b5 | ||
|
|
7276350f74 | ||
|
|
7d3c05088a | ||
|
|
41c2bd9131 | ||
|
|
514a9f38c8 | ||
|
|
5cc884639f | ||
|
|
f77cb6d33b | ||
|
|
b642f4b240 | ||
|
|
6de721089c | ||
|
|
4bd48c399a | ||
|
|
2b3a5ff337 | ||
|
|
0387eb3746 | ||
|
|
3ebc6d618a | ||
|
|
92d9e10cb8 | ||
|
|
d0d670f1a5 | ||
|
|
3597c8f138 | ||
|
|
8a8ec29b41 | ||
|
|
a3352d032d | ||
|
|
d5f8647fcc | ||
|
|
79a700d786 | ||
|
|
bc223f0644 | ||
|
|
2b0d82453b | ||
|
|
fa0150e745 | ||
|
|
c95d11ef66 | ||
|
|
63b268f9c4 | ||
|
|
03d38f8a2a | ||
|
|
c88d9bf123 | ||
|
|
053ebe15bb | ||
|
|
b03bfb6bbc | ||
|
|
e48867000e | ||
|
|
e08cdbd74d | ||
|
|
0b4c585a58 | ||
|
|
6213e77fab | ||
|
|
fccca6a4fc | ||
|
|
d4f4ec3401 | ||
|
|
b23dd3612d | ||
|
|
ca4fa4ac27 | ||
|
|
5e53a208d5 | ||
|
|
895c69642d | ||
|
|
765f9d29bb | ||
|
|
99af5c86a5 | ||
|
|
0bb2b3ed5f | ||
|
|
4181f3ba21 | ||
|
|
c189b398c0 | ||
|
|
de4fbf8b04 | ||
|
|
f91a558314 | ||
|
|
7587ff024c | ||
|
|
06778c9bb0 | ||
|
|
7a97045412 | ||
|
|
5d9bedfd28 | ||
|
|
575992c25d | ||
|
|
72db5bc9f3 | ||
|
|
b015720a3e | ||
|
|
84a6d4501b | ||
|
|
07049e71c0 | ||
|
|
4710d9c1eb | ||
|
|
d08462745a | ||
|
|
eb4e9ee077 | ||
|
|
601a486b13 | ||
|
|
14867f470d | ||
|
|
187f0800b2 | ||
|
|
3afa202d0b | ||
|
|
d33e76c8b0 | ||
|
|
3061c55eaf | ||
|
|
621a1dc2cb | ||
|
|
1a832780a2 | ||
|
|
ab531c80df | ||
|
|
f20ea0a87f | ||
|
|
3e7f726afb | ||
|
|
d247515a03 | ||
|
|
f084a3f219 | ||
|
|
4d3673c305 | ||
|
|
0253c3bf7b | ||
|
|
0eae895676 | ||
|
|
045acb362d | ||
|
|
ebd65da3ff | ||
|
|
1de3c5bec0 | ||
|
|
b334c939df | ||
|
|
8e6100e236 | ||
|
|
8cb512b6e5 | ||
|
|
6a28946205 | ||
|
|
c632c025cb | ||
|
|
1403ffe190 | ||
|
|
71ae9a84da | ||
|
|
a823471771 | ||
|
|
dac057d242 | ||
|
|
3d9fe36245 | ||
|
|
5c1da24def | ||
|
|
1e961a5945 | ||
|
|
84898146d2 | ||
|
|
552613e977 | ||
|
|
07687df91f | ||
|
|
1a4d941a2c | ||
|
|
cfbd880995 | ||
|
|
09abe77495 | ||
|
|
ac9943ff94 | ||
|
|
741dd2adb7 | ||
|
|
1ad5d3676f | ||
|
|
27254aa31e | ||
|
|
bb90dc1c01 | ||
|
|
292ec97b1c | ||
|
|
238cc2ad46 | ||
|
|
b9064bbfda | ||
|
|
eddcc2816a | ||
|
|
52360a682a | ||
|
|
709ce6489b | ||
|
|
220a9baa7d | ||
|
|
a7f2dd99c3 | ||
|
|
3481adb3c5 | ||
|
|
9f5197a426 | ||
|
|
f6a631a33a | ||
|
|
9dc04ff5a0 | ||
|
|
248d200764 | ||
|
|
e3d08eca3f | ||
|
|
dc5d2483c1 | ||
|
|
93aacad350 | ||
|
|
1b822bee91 | ||
|
|
c65d6fee5a | ||
|
|
6bc715575f | ||
|
|
335044e38a | ||
|
|
6a6232b655 | ||
|
|
75ff6b4f11 | ||
|
|
9cea6e35e4 | ||
|
|
52d8dc88d2 | ||
|
|
1d5a50bcca | ||
|
|
44a595c1df | ||
|
|
a528dee670 | ||
|
|
b6e96d8711 | ||
|
|
c6cc4e2c9d | ||
|
|
6974e51148 | ||
|
|
25da5fb650 | ||
|
|
a1e8af0660 | ||
|
|
af61ad66c6 | ||
|
|
a06af3402a | ||
|
|
08b7f18796 | ||
|
|
6feb1a46cc | ||
|
|
3f8e6125d1 | ||
|
|
ff887196c3 | ||
|
|
3509b1be8c | ||
|
|
b27aa50083 | ||
|
|
45230a57ee | ||
|
|
b3e420d423 | ||
|
|
65d9f4fbee | ||
|
|
935e11ab2a | ||
|
|
c2cd7ca52d | ||
|
|
120691c6bf | ||
|
|
334d2df4ef | ||
|
|
1a2616c648 | ||
|
|
9858c0e0c1 | ||
|
|
7f19d04e93 | ||
|
|
8ba497855b | ||
|
|
7ef8b224eb | ||
|
|
a841b9494a | ||
|
|
856351aea0 | ||
|
|
6a172ce269 | ||
|
|
f86bc32f0d | ||
|
|
18800ff8d5 | ||
|
|
3b610432ec | ||
|
|
b20423e415 | ||
|
|
e4189b2bc4 | ||
|
|
c5ac0196de | ||
|
|
8934e004fb | ||
|
|
ed0fd3df78 | ||
|
|
bd19cc794a | ||
|
|
c52a4be7e7 | ||
|
|
7ae29aa122 | ||
|
|
49d213290f | ||
|
|
fc618cb24f | ||
|
|
2826fdb475 | ||
|
|
b0e990f9f3 | ||
|
|
6a7a568e59 | ||
|
|
547be25b57 | ||
|
|
9559bcb3b8 | ||
|
|
049b78eb02 | ||
|
|
d55c734718 | ||
|
|
79b6fc8b17 | ||
|
|
8603e7a579 | ||
|
|
ea1b401228 | ||
|
|
ee73a12440 | ||
|
|
cad47368ac | ||
|
|
50c7d32ed7 | ||
|
|
eab136f635 | ||
|
|
ae829d7942 | ||
|
|
a3ab01bf0e | ||
|
|
4913d6c0a0 | ||
|
|
8c1ffda944 | ||
|
|
8d5d407c90 | ||
|
|
af4117690f | ||
|
|
f02be5753c | ||
|
|
691949eb08 | ||
|
|
42758eadc3 | ||
|
|
319822d1a1 | ||
|
|
948787f8ba | ||
|
|
73b5246a88 | ||
|
|
670091c293 | ||
|
|
1332d12087 | ||
|
|
55633c8d14 | ||
|
|
42f47740db | ||
|
|
aaf10cf8f8 | ||
|
|
3d54772da5 | ||
|
|
1b552b305b | ||
|
|
af93dc5b0e | ||
|
|
63e1598ebf | ||
|
|
aa2cf6dbe4 | ||
|
|
d507b40c97 | ||
|
|
3df34ece61 | ||
|
|
047af8912f | ||
|
|
fdef39887a | ||
|
|
e0d944efc5 | ||
|
|
74e416394a | ||
|
|
35fb7c4e7f | ||
|
|
8d06dd90d8 | ||
|
|
254b0a7773 | ||
|
|
3fc51af261 | ||
|
|
20ea58981d | ||
|
|
f8c1582aeb | ||
|
|
13722ca8f8 | ||
|
|
da3537eba2 | ||
|
|
67f0b0f8e9 | ||
|
|
08a962e688 | ||
|
|
5b711f158d | ||
|
|
0ddcf004c1 | ||
|
|
4997cc48ca | ||
|
|
51bcf73e15 | ||
|
|
d7b21ded96 | ||
|
|
daf4efa054 | ||
|
|
6a12a26612 | ||
|
|
533058aa3f | ||
|
|
804d1260f2 | ||
|
|
78fcf53606 | ||
|
|
ceb641d1fa | ||
|
|
47fdff2993 | ||
|
|
1a96a975f9 | ||
|
|
cd460c5ec5 | ||
|
|
70b7f9d5dd | ||
|
|
f236d6c33f | ||
|
|
356df9098e | ||
|
|
9a8aa1e10d | ||
|
|
7aa985154f | ||
|
|
d287674d69 | ||
|
|
40b1e8f4c2 | ||
|
|
d38fde0359 | ||
|
|
e6015ca999 | ||
|
|
3bcecc6256 | ||
|
|
6cf264dcf7 | ||
|
|
b12db11956 | ||
|
|
591d665d56 | ||
|
|
327cae955a | ||
|
|
9fa23ecddf | ||
|
|
ab3bfe2460 | ||
|
|
465ca2d4f9 | ||
|
|
21e892c042 | ||
|
|
98be9e1efd | ||
|
|
3081bfe814 | ||
|
|
61e4461bc4 | ||
|
|
5c2bcd551c | ||
|
|
15f9eb4e80 | ||
|
|
5038527c90 | ||
|
|
e8370987ea | ||
|
|
3f7660526d | ||
|
|
f484b50ab3 | ||
|
|
55000c7653 | ||
|
|
4cd2444a1a | ||
|
|
4f23d2e874 | ||
|
|
76bdd898e1 | ||
|
|
a45cf02d52 | ||
|
|
828c76e201 | ||
|
|
32e68f2be3 | ||
|
|
de3f3cd93b | ||
|
|
31fb6fec08 | ||
|
|
cf2473a3d5 | ||
|
|
0d44379948 | ||
|
|
43264e77b2 | ||
|
|
c09af9b937 | ||
|
|
c84565f091 | ||
|
|
d07f1c6852 | ||
|
|
754b52647b | ||
|
|
9f4ed24704 | ||
|
|
015c6b7bdf | ||
|
|
a17c1fc35a | ||
|
|
19e46ce78d | ||
|
|
cf6ee3b60d | ||
|
|
488003993f | ||
|
|
c64a6fabbc | ||
|
|
eb2751861d | ||
|
|
48a1cf1189 | ||
|
|
98e1d51a48 | ||
|
|
5e22c832a2 | ||
|
|
6523b30f10 | ||
|
|
8fefa7c5af | ||
|
|
b6a5b8942c | ||
|
|
cd622989d6 | ||
|
|
8251274bf8 | ||
|
|
dd63c97ced | ||
|
|
b17960096e | ||
|
|
83934af041 | ||
|
|
0421da30cd | ||
|
|
bf084a1ffb | ||
|
|
f3868b9acc | ||
|
|
7c3d195fd4 | ||
|
|
4d7294feac | ||
|
|
6877abed6e | ||
|
|
5353d755a0 | ||
|
|
d2dff449d1 | ||
|
|
d526c6c1ce | ||
|
|
49fc908f0f | ||
|
|
b3162ce390 | ||
|
|
9a2a37f2fc | ||
|
|
a90b3afcdd | ||
|
|
6c903b2433 | ||
|
|
00558441fe | ||
|
|
7e10e6ba81 | ||
|
|
3995b3a083 | ||
|
|
e6c9c3f0a9 | ||
|
|
c8fc25111e | ||
|
|
6b3cf5e788 | ||
|
|
afbd8d12b4 | ||
|
|
e8e393b320 | ||
|
|
84db370516 | ||
|
|
173148ce25 | ||
|
|
0fd0528fd0 | ||
|
|
664a0e0758 | ||
|
|
6454e3ee12 | ||
|
|
33f4a82dd5 | ||
|
|
9bd6532411 | ||
|
|
b89d2977f8 | ||
|
|
25b557d5d1 | ||
|
|
3e34fabfaf | ||
|
|
fc49e8ffa7 | ||
|
|
48a5eeec29 | ||
|
|
7fa176ef7b | ||
|
|
bfaa99a8a9 | ||
|
|
1ff4ed026e | ||
|
|
1703bbfb68 | ||
|
|
a6a2e1605d | ||
|
|
c0c61913c2 | ||
|
|
fb2437d75d | ||
|
|
499e55c473 | ||
|
|
dfd4513b16 | ||
|
|
f873da7236 | ||
|
|
59360c5b60 | ||
|
|
750c34189a | ||
|
|
7c3f5beb3a | ||
|
|
8dfe095dae | ||
|
|
53022632e0 | ||
|
|
29a916e5ad |
13
.gitignore
vendored
13
.gitignore
vendored
@@ -2,9 +2,18 @@
|
||||
## ArchiSteamFarm
|
||||
#################
|
||||
|
||||
# Ignore all config files, apart from example.xml
|
||||
# Ignore all config files, apart from ones we want to include
|
||||
ArchiSteamFarm/config/*
|
||||
!ArchiSteamFarm/config/example.xml
|
||||
!ArchiSteamFarm/config/ASF.json
|
||||
!ArchiSteamFarm/config/example.json
|
||||
!ArchiSteamFarm/config/minimal.json
|
||||
|
||||
# Ignore local debugging
|
||||
ArchiSteamFarm/log.txt
|
||||
ArchiSteamFarm/debug/*
|
||||
|
||||
# Ignore out
|
||||
out/
|
||||
|
||||
#################
|
||||
## Eclipse
|
||||
|
||||
12
.travis.yml
Normal file
12
.travis.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
language: csharp
|
||||
solution: ArchiSteamFarm.sln
|
||||
|
||||
git:
|
||||
depth: 10
|
||||
|
||||
mono:
|
||||
- weekly
|
||||
- latest
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
@@ -1,10 +1,22 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.23107.0
|
||||
VisualStudioVersion = 14.0.24720.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ArchiSteamFarm", "ArchiSteamFarm\ArchiSteamFarm.csproj", "{35AF7887-08B9-40E8-A5EA-797D8B60B30C}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SteamAuth", "SteamAuth\SteamAuth.csproj", "{5AD0934E-F6C4-4AE5-83AF-C788313B2A87}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConfigGenerator", "ConfigGenerator\ConfigGenerator.csproj", "{C3F6FE68-5E75-415E-BEA1-1E7C16D6A433}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{35AF7887-08B9-40E8-A5EA-797D8B60B30C} = {35AF7887-08B9-40E8-A5EA-797D8B60B30C}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GUI", "GUI\GUI.csproj", "{599121A9-5887-4522-A3D6-61470B90BAD4}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{35AF7887-08B9-40E8-A5EA-797D8B60B30C} = {35AF7887-08B9-40E8-A5EA-797D8B60B30C}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -15,6 +27,18 @@ Global
|
||||
{35AF7887-08B9-40E8-A5EA-797D8B60B30C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{35AF7887-08B9-40E8-A5EA-797D8B60B30C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{35AF7887-08B9-40E8-A5EA-797D8B60B30C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{5AD0934E-F6C4-4AE5-83AF-C788313B2A87}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5AD0934E-F6C4-4AE5-83AF-C788313B2A87}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5AD0934E-F6C4-4AE5-83AF-C788313B2A87}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5AD0934E-F6C4-4AE5-83AF-C788313B2A87}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C3F6FE68-5E75-415E-BEA1-1E7C16D6A433}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C3F6FE68-5E75-415E-BEA1-1E7C16D6A433}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C3F6FE68-5E75-415E-BEA1-1E7C16D6A433}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C3F6FE68-5E75-415E-BEA1-1E7C16D6A433}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{599121A9-5887-4522-A3D6-61470B90BAD4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{599121A9-5887-4522-A3D6-61470B90BAD4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{599121A9-5887-4522-A3D6-61470B90BAD4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{599121A9-5887-4522-A3D6-61470B90BAD4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.1"/>
|
||||
</startup>
|
||||
</configuration>
|
||||
</configuration>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
|
||||
Copyright 2015 Łukasz "JustArchi" Domeradzki
|
||||
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
|
||||
Contact: JustArchi@JustArchi.net
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -24,112 +24,303 @@
|
||||
|
||||
using SteamKit2;
|
||||
using SteamKit2.Internal;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal sealed class ArchiHandler : ClientMsgHandler {
|
||||
private readonly Bot Bot;
|
||||
|
||||
internal ArchiHandler(Bot bot) {
|
||||
if (bot == null) {
|
||||
throw new ArgumentNullException("bot");
|
||||
}
|
||||
|
||||
Bot = bot;
|
||||
}
|
||||
|
||||
/*
|
||||
____ _ _ _ _
|
||||
/ ___| __ _ | || || |__ __ _ ___ | | __ ___
|
||||
| | / _` || || || '_ \ / _` | / __|| |/ // __|
|
||||
| |___| (_| || || || |_) || (_| || (__ | < \__ \
|
||||
\____|\__,_||_||_||_.__/ \__,_| \___||_|\_\|___/
|
||||
|
||||
*/
|
||||
|
||||
internal sealed class NotificationsCallback : CallbackMsg {
|
||||
internal enum ENotification : byte {
|
||||
Unknown = 0,
|
||||
Trading = 1,
|
||||
// Only custom below, different than ones available as user_notification_type
|
||||
Items = 255
|
||||
}
|
||||
|
||||
internal readonly HashSet<ENotification> Notifications;
|
||||
|
||||
internal NotificationsCallback(JobID jobID, CMsgClientUserNotifications msg) {
|
||||
if (jobID == null || msg == null) {
|
||||
throw new ArgumentNullException("jobID || msg");
|
||||
}
|
||||
|
||||
JobID = jobID;
|
||||
|
||||
Notifications = new HashSet<ENotification>();
|
||||
foreach (var notification in msg.notifications) {
|
||||
Notifications.Add((ENotification) notification.user_notification_type);
|
||||
}
|
||||
}
|
||||
|
||||
internal NotificationsCallback(JobID jobID, CMsgClientItemAnnouncements msg) {
|
||||
if (jobID == null || msg == null) {
|
||||
throw new ArgumentNullException("jobID || msg");
|
||||
}
|
||||
|
||||
JobID = jobID;
|
||||
|
||||
if (msg.count_new_items > 0) {
|
||||
Notifications = new HashSet<ENotification> {
|
||||
ENotification.Items
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class OfflineMessageCallback : CallbackMsg {
|
||||
internal readonly uint OfflineMessagesCount;
|
||||
|
||||
internal OfflineMessageCallback(JobID jobID, CMsgClientOfflineMessageNotification msg) {
|
||||
if (jobID == null || msg == null) {
|
||||
throw new ArgumentNullException("jobID || msg");
|
||||
}
|
||||
|
||||
JobID = jobID;
|
||||
OfflineMessagesCount = msg.offline_messages;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class PurchaseResponseCallback : CallbackMsg {
|
||||
internal enum EPurchaseResult {
|
||||
internal enum EPurchaseResult : sbyte {
|
||||
Unknown = -1,
|
||||
OK = 0,
|
||||
AlreadyOwned = 9,
|
||||
RegionLockedKey = 13,
|
||||
RegionLocked = 13,
|
||||
InvalidKey = 14,
|
||||
DuplicatedKey = 15,
|
||||
BaseGameRequired = 24,
|
||||
OnCooldown = 53
|
||||
}
|
||||
|
||||
internal EResult Result { get; private set; }
|
||||
internal EPurchaseResult PurchaseResult { get; private set; }
|
||||
internal KeyValue ReceiptInfo { get; private set; } = new KeyValue();
|
||||
internal Dictionary<uint, string> Items { get; private set; } = new Dictionary<uint, string>();
|
||||
internal readonly EResult Result;
|
||||
internal readonly EPurchaseResult PurchaseResult;
|
||||
internal readonly KeyValue ReceiptInfo;
|
||||
internal readonly Dictionary<uint, string> Items;
|
||||
|
||||
internal PurchaseResponseCallback(CMsgClientPurchaseResponse body) {
|
||||
Result = (EResult) body.eresult;
|
||||
PurchaseResult = (EPurchaseResult) body.purchase_result_details;
|
||||
internal PurchaseResponseCallback(JobID jobID, CMsgClientPurchaseResponse msg) {
|
||||
if (jobID == null || msg == null) {
|
||||
throw new ArgumentNullException("jobID || msg");
|
||||
}
|
||||
|
||||
using (MemoryStream ms = new MemoryStream(body.purchase_receipt_info)) {
|
||||
if (ReceiptInfo.TryReadAsBinary(ms)) {
|
||||
foreach (KeyValue lineItem in ReceiptInfo["lineitems"].Children) {
|
||||
Items.Add((uint) lineItem["PackageID"].AsUnsignedLong(), lineItem["ItemDescription"].AsString());
|
||||
}
|
||||
JobID = jobID;
|
||||
Result = (EResult) msg.eresult;
|
||||
PurchaseResult = (EPurchaseResult) msg.purchase_result_details;
|
||||
|
||||
if (msg.purchase_receipt_info == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ReceiptInfo = new KeyValue();
|
||||
using (MemoryStream ms = new MemoryStream(msg.purchase_receipt_info)) {
|
||||
if (!ReceiptInfo.TryReadAsBinary(ms)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var lineItems = ReceiptInfo["lineitems"].Children;
|
||||
Items = new Dictionary<uint, string>(lineItems.Count);
|
||||
|
||||
foreach (KeyValue lineItem in lineItems) {
|
||||
uint appID = (uint) lineItem["PackageID"].AsUnsignedLong();
|
||||
string gameName = lineItem["ItemDescription"].Value;
|
||||
gameName = WebUtility.HtmlDecode(gameName); // Apparently steam expects client to decode sent HTML
|
||||
Items[appID] = gameName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class NotificationCallback : CallbackMsg {
|
||||
internal enum ENotificationType {
|
||||
Unknown = 0,
|
||||
Trading = 1,
|
||||
/*
|
||||
__ __ _ _ _
|
||||
| \/ | ___ | |_ | |__ ___ __| | ___
|
||||
| |\/| | / _ \| __|| '_ \ / _ \ / _` |/ __|
|
||||
| | | || __/| |_ | | | || (_) || (_| |\__ \
|
||||
|_| |_| \___| \__||_| |_| \___/ \__,_||___/
|
||||
|
||||
*/
|
||||
|
||||
internal void PlayGame(string gameName) {
|
||||
if (!Client.IsConnected) {
|
||||
return;
|
||||
}
|
||||
|
||||
internal ENotificationType NotificationType { get; private set; }
|
||||
|
||||
internal NotificationCallback(CMsgClientUserNotifications.Notification body) {
|
||||
NotificationType = (ENotificationType) body.user_notification_type;
|
||||
}
|
||||
}
|
||||
|
||||
internal void AcceptClanInvite(ulong clanID) {
|
||||
var request = new ClientMsg<CMsgClientClanInviteAction>((int) EMsg.ClientAcknowledgeClanInvite);
|
||||
request.Body.GroupID = clanID;
|
||||
request.Body.AcceptInvite = true;
|
||||
Client.Send(request);
|
||||
}
|
||||
|
||||
internal void DeclineClanInvite(ulong clanID) {
|
||||
var request = new ClientMsg<CMsgClientClanInviteAction>((int) EMsg.ClientAcknowledgeClanInvite);
|
||||
request.Body.GroupID = clanID;
|
||||
request.Body.AcceptInvite = false;
|
||||
Client.Send(request);
|
||||
}
|
||||
|
||||
internal void PlayGames(params ulong[] gameIDs) {
|
||||
var request = new ClientMsgProtobuf<CMsgClientGamesPlayed>(EMsg.ClientGamesPlayed);
|
||||
foreach (ulong gameID in gameIDs) {
|
||||
if (gameID != 0) {
|
||||
request.Body.games_played.Add(new CMsgClientGamesPlayed.GamePlayed {
|
||||
game_id = new GameID(gameID),
|
||||
});
|
||||
}
|
||||
if (!string.IsNullOrEmpty(gameName)) {
|
||||
request.Body.games_played.Add(new CMsgClientGamesPlayed.GamePlayed {
|
||||
game_extra_info = gameName,
|
||||
game_id = new GameID() {
|
||||
AppType = GameID.GameType.Shortcut,
|
||||
ModID = uint.MaxValue
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Client.Send(request);
|
||||
}
|
||||
|
||||
// Will provide result in ClientPurchaseResponse, regardless if success or not
|
||||
internal void RedeemKey(string key) {
|
||||
var request = new ClientMsgProtobuf<CMsgClientRegisterKey>(EMsg.ClientRegisterKey);
|
||||
internal void PlayGames(uint gameID) {
|
||||
if (!Client.IsConnected) {
|
||||
return;
|
||||
}
|
||||
|
||||
PlayGames(new HashSet<uint> { gameID });
|
||||
}
|
||||
|
||||
internal void PlayGames(HashSet<uint> gameIDs) {
|
||||
if (gameIDs == null || !Client.IsConnected) {
|
||||
return;
|
||||
}
|
||||
|
||||
var request = new ClientMsgProtobuf<CMsgClientGamesPlayed>(EMsg.ClientGamesPlayed);
|
||||
foreach (uint gameID in gameIDs) {
|
||||
if (gameID == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
request.Body.games_played.Add(new CMsgClientGamesPlayed.GamePlayed {
|
||||
game_id = new GameID(gameID),
|
||||
});
|
||||
}
|
||||
|
||||
Client.Send(request);
|
||||
}
|
||||
|
||||
internal async Task<PurchaseResponseCallback> RedeemKey(string key) {
|
||||
if (string.IsNullOrEmpty(key) || !Client.IsConnected) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var request = new ClientMsgProtobuf<CMsgClientRegisterKey>(EMsg.ClientRegisterKey) {
|
||||
SourceJobID = Client.GetNextJobID()
|
||||
};
|
||||
|
||||
request.Body.key = key;
|
||||
|
||||
Client.Send(request);
|
||||
|
||||
try {
|
||||
return await new AsyncJob<PurchaseResponseCallback>(Client, request.SourceJobID);
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException(e, Bot.BotName);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed override void HandleMsg(IPacketMsg packetMsg) {
|
||||
if (packetMsg != null) {
|
||||
switch (packetMsg.MsgType) {
|
||||
case EMsg.ClientPurchaseResponse:
|
||||
HandlePurchaseResponse(packetMsg);
|
||||
break;
|
||||
case EMsg.ClientUserNotifications:
|
||||
HandleUserNotifications(packetMsg);
|
||||
break;
|
||||
}
|
||||
// TODO: Please remove me immediately after https://github.com/SteamRE/SteamKit/issues/254 gets fixed
|
||||
internal void HackedLogOn(SteamUser.LogOnDetails details) {
|
||||
if (!Client.IsConnected) {
|
||||
return;
|
||||
}
|
||||
|
||||
SteamID steamID = new SteamID(details.AccountID, details.AccountInstance, Client.ConnectedUniverse, EAccountType.Individual);
|
||||
|
||||
var logon = new ClientMsgProtobuf<CMsgClientLogon>(EMsg.ClientLogon);
|
||||
logon.Body.obfustucated_private_ip = details.LoginID.Value;
|
||||
logon.ProtoHeader.client_sessionid = 0;
|
||||
logon.ProtoHeader.steamid = steamID.ConvertToUInt64();
|
||||
logon.Body.account_name = details.Username;
|
||||
logon.Body.password = details.Password;
|
||||
logon.Body.should_remember_password = details.ShouldRememberPassword;
|
||||
logon.Body.protocol_version = MsgClientLogon.CurrentProtocol;
|
||||
logon.Body.client_os_type = (uint) details.ClientOSType;
|
||||
logon.Body.client_language = details.ClientLanguage;
|
||||
logon.Body.cell_id = details.CellID;
|
||||
logon.Body.steam2_ticket_request = details.RequestSteam2Ticket;
|
||||
logon.Body.client_package_version = 1771;
|
||||
logon.Body.auth_code = details.AuthCode;
|
||||
logon.Body.two_factor_code = details.TwoFactorCode;
|
||||
logon.Body.login_key = details.LoginKey;
|
||||
logon.Body.sha_sentryfile = details.SentryFileHash;
|
||||
logon.Body.eresult_sentryfile = (int) (details.SentryFileHash != null ? EResult.OK : EResult.FileNotFound);
|
||||
|
||||
Client.Send(logon);
|
||||
}
|
||||
|
||||
/*
|
||||
_ _ _ _
|
||||
| | | | __ _ _ __ __| || | ___ _ __ ___
|
||||
| |_| | / _` || '_ \ / _` || | / _ \| '__|/ __|
|
||||
| _ || (_| || | | || (_| || || __/| | \__ \
|
||||
|_| |_| \__,_||_| |_| \__,_||_| \___||_| |___/
|
||||
|
||||
*/
|
||||
|
||||
public override void HandleMsg(IPacketMsg packetMsg) {
|
||||
if (packetMsg == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (packetMsg.MsgType) {
|
||||
case EMsg.ClientFSOfflineMessageNotification:
|
||||
HandleFSOfflineMessageNotification(packetMsg);
|
||||
break;
|
||||
case EMsg.ClientItemAnnouncements:
|
||||
HandleItemAnnouncements(packetMsg);
|
||||
break;
|
||||
case EMsg.ClientPurchaseResponse:
|
||||
HandlePurchaseResponse(packetMsg);
|
||||
break;
|
||||
case EMsg.ClientUserNotifications:
|
||||
HandleUserNotifications(packetMsg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleFSOfflineMessageNotification(IPacketMsg packetMsg) {
|
||||
if (packetMsg == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var response = new ClientMsgProtobuf<CMsgClientOfflineMessageNotification>(packetMsg);
|
||||
Client.PostCallback(new OfflineMessageCallback(packetMsg.TargetJobID, response.Body));
|
||||
}
|
||||
|
||||
private void HandleItemAnnouncements(IPacketMsg packetMsg) {
|
||||
if (packetMsg == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var response = new ClientMsgProtobuf<CMsgClientItemAnnouncements>(packetMsg);
|
||||
Client.PostCallback(new NotificationsCallback(packetMsg.TargetJobID, response.Body));
|
||||
}
|
||||
|
||||
private void HandlePurchaseResponse(IPacketMsg packetMsg) {
|
||||
if (packetMsg == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var response = new ClientMsgProtobuf<CMsgClientPurchaseResponse>(packetMsg);
|
||||
Client.PostCallback(new PurchaseResponseCallback(response.Body));
|
||||
Client.PostCallback(new PurchaseResponseCallback(packetMsg.TargetJobID, response.Body));
|
||||
}
|
||||
|
||||
private void HandleUserNotifications(IPacketMsg packetMsg) {
|
||||
var response = new ClientMsgProtobuf<CMsgClientUserNotifications>(packetMsg);
|
||||
foreach (var notification in response.Body.notifications) {
|
||||
Client.PostCallback(new NotificationCallback(notification));
|
||||
if (packetMsg == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var response = new ClientMsgProtobuf<CMsgClientUserNotifications>(packetMsg);
|
||||
Client.PostCallback(new NotificationsCallback(packetMsg.TargetJobID, response.Body));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,9 +9,10 @@
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>ArchiSteamFarm</RootNamespace>
|
||||
<AssemblyName>ArchiSteamFarm</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<IsWebBootstrapper>false</IsWebBootstrapper>
|
||||
<TargetFrameworkProfile />
|
||||
<PublishUrl>publish\</PublishUrl>
|
||||
<Install>true</Install>
|
||||
<InstallFrom>Disk</InstallFrom>
|
||||
@@ -35,7 +36,7 @@
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<WarningLevel>3</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
@@ -46,10 +47,12 @@
|
||||
</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
<DocumentationFile>
|
||||
</DocumentationFile>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
<GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ApplicationIcon>cirno.ico</ApplicationIcon>
|
||||
@@ -57,29 +60,37 @@
|
||||
<PropertyGroup>
|
||||
<RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<SignAssembly>false</SignAssembly>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<AssemblyOriginatorKeyFile>
|
||||
</AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<DelaySign>false</DelaySign>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="HtmlAgilityPack, Version=1.4.9.0, Culture=neutral, PublicKeyToken=bd319b19eaf3b43a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\HtmlAgilityPack.1.4.9\lib\Net45\HtmlAgilityPack.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.8.0.1-beta1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<HintPath>..\packages\Newtonsoft.Json.8.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="protobuf-net, Version=2.0.0.668, Culture=neutral, PublicKeyToken=257b51d87d2e4d67, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\protobuf-net.2.0.0.668\lib\net40\protobuf-net.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="SteamKit2, Version=1.6.5.29095, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\SteamKit2.1.6.5\lib\net40\SteamKit2.dll</HintPath>
|
||||
<Reference Include="SteamKit2, Version=1.7.0.33680, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\SteamKit2.1.7.0\lib\net45\SteamKit2.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="System.ServiceModel" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
@@ -87,19 +98,33 @@
|
||||
<Compile Include="ArchiHandler.cs" />
|
||||
<Compile Include="ArchiWebHandler.cs" />
|
||||
<Compile Include="Bot.cs" />
|
||||
<Compile Include="BotConfig.cs" />
|
||||
<Compile Include="GlobalDatabase.cs" />
|
||||
<Compile Include="BotDatabase.cs" />
|
||||
<Compile Include="CardsFarmer.cs" />
|
||||
<Compile Include="CMsgClientClanInviteAction.cs" />
|
||||
<Compile Include="Debugging.cs" />
|
||||
<Compile Include="GlobalConfig.cs" />
|
||||
<Compile Include="JSON\GitHub.cs" />
|
||||
<Compile Include="JSON\Steam.cs" />
|
||||
<Compile Include="Logging.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="SteamItem.cs" />
|
||||
<Compile Include="SteamTradeOffer.cs" />
|
||||
<Compile Include="Trading.cs" />
|
||||
<Compile Include="Utilities.cs" />
|
||||
<Compile Include="WCF.cs" />
|
||||
<Compile Include="WebBrowser.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
<None Include="config\ASF.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="config\example.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="config\minimal.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@@ -116,22 +141,36 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="cirno.ico" />
|
||||
<Content Include="config\example.xml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\SteamAuth\SteamAuth.csproj">
|
||||
<Project>{5ad0934e-f6c4-4ae5-83af-c788313b2a87}</Project>
|
||||
<Name>SteamAuth</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PreBuildEvent>
|
||||
</PreBuildEvent>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent Condition=" '$(OS)' != 'Unix' ">if $(ConfigurationName) == Release (
|
||||
mkdir "$(TargetDir)out" "$(TargetDir)out\config"
|
||||
copy "$(TargetDir)config\example.xml" "$(TargetDir)out\config"
|
||||
"$(SolutionDir)tools\ILMerge.exe" /out:"$(TargetDir)out\ASF.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll" /target:exe /targetplatform:v4,C:\Windows\Microsoft.NET\Framework64\v4.0.30319 /wildcards
|
||||
del "$(TargetDir)out\ASF.pdb"
|
||||
)</PostBuildEvent>
|
||||
<PostBuildEvent Condition=" '$(OS)' != 'Unix' AND '$(ConfigurationName)' == 'Release' ">
|
||||
mkdir "$(SolutionDir)out\config"
|
||||
copy "$(TargetDir)config\ASF.json" "$(SolutionDir)out\config"
|
||||
copy "$(TargetDir)config\example.json" "$(SolutionDir)out\config"
|
||||
copy "$(TargetDir)config\minimal.json" "$(SolutionDir)out\config"
|
||||
"$(SolutionDir)tools\ILRepack\ILRepack.exe" /ndebug /internalize /parallel /targetplatform:v4 /wildcards /out:"$(SolutionDir)out\ASF.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll"
|
||||
del "$(SolutionDir)out\ASF.exe.config"
|
||||
</PostBuildEvent>
|
||||
<PostBuildEvent Condition=" '$(OS)' == 'Unix' AND '$(ConfigurationName)' == 'Release' ">
|
||||
mkdir -p "$(SolutionDir)out/config"
|
||||
cp "$(TargetDir)config/ASF.json" "$(SolutionDir)out/config"
|
||||
cp "$(TargetDir)config/example.json" "$(SolutionDir)out/config"
|
||||
cp "$(TargetDir)config/minimal.json" "$(SolutionDir)out/config"
|
||||
mono -O=all "$(SolutionDir)tools/ILRepack/ILRepack.exe" /ndebug /internalize /parallel /targetplatform:v4 /wildcards /out:"$(SolutionDir)out/ASF.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll"
|
||||
rm "$(SolutionDir)out/ASF.exe.config"
|
||||
</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
|
||||
Copyright 2015 Łukasz "JustArchi" Domeradzki
|
||||
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
|
||||
Contact: JustArchi@JustArchi.net
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -21,245 +21,427 @@
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using HtmlAgilityPack;
|
||||
using SteamKit2;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
using System.Threading;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal class ArchiWebHandler {
|
||||
private const int Timeout = 1000 * 15; // In miliseconds
|
||||
internal sealed class ArchiWebHandler {
|
||||
private const string SteamCommunityHost = "steamcommunity.com";
|
||||
private const byte MinSessionTTL = 15; // Assume session is valid for at least that amount of seconds
|
||||
|
||||
private static string SteamCommunityURL = "https://" + SteamCommunityHost;
|
||||
private static int Timeout = GlobalConfig.DefaultHttpTimeout * 1000;
|
||||
|
||||
private readonly Bot Bot;
|
||||
private readonly string ApiKey;
|
||||
private readonly SemaphoreSlim SessionSemaphore = new SemaphoreSlim(1);
|
||||
private readonly WebBrowser WebBrowser;
|
||||
|
||||
private ulong SteamID;
|
||||
private string VanityURL;
|
||||
private readonly Dictionary<string, string> SteamCookieDictionary = new Dictionary<string, string>();
|
||||
private DateTime LastSessionRefreshCheck = DateTime.MinValue;
|
||||
|
||||
// This is required because home_process request must be done on final URL
|
||||
private string GetHomeProcess() {
|
||||
if (!string.IsNullOrEmpty(VanityURL)) {
|
||||
return "http://steamcommunity.com/id/" + VanityURL + "/home_process";
|
||||
} else if (SteamID != 0) {
|
||||
return "http://steamcommunity.com/profiles/" + SteamID + "/home_process";
|
||||
} else {
|
||||
return null;
|
||||
internal static void Init() {
|
||||
Timeout = Program.GlobalConfig.HttpTimeout * 1000;
|
||||
SteamCommunityURL = (Program.GlobalConfig.ForceHttp ? "http://" : "https://") + SteamCommunityHost;
|
||||
}
|
||||
|
||||
private static uint GetAppIDFromMarketHashName(string hashName) {
|
||||
if (string.IsNullOrEmpty(hashName)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int index = hashName.IndexOf('-');
|
||||
if (index < 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint appID;
|
||||
if (!uint.TryParse(hashName.Substring(0, index), out appID)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return appID;
|
||||
}
|
||||
|
||||
private static Steam.Item.EType GetItemType(string name) {
|
||||
if (string.IsNullOrEmpty(name)) {
|
||||
return Steam.Item.EType.Unknown;
|
||||
}
|
||||
|
||||
switch (name) {
|
||||
case "Booster Pack":
|
||||
return Steam.Item.EType.BoosterPack;
|
||||
case "Coupon":
|
||||
return Steam.Item.EType.Coupon;
|
||||
case "Gift":
|
||||
return Steam.Item.EType.Gift;
|
||||
case "Steam Gems":
|
||||
return Steam.Item.EType.SteamGems;
|
||||
default:
|
||||
if (name.EndsWith("Emoticon", StringComparison.Ordinal)) {
|
||||
return Steam.Item.EType.Emoticon;
|
||||
} else if (name.EndsWith("Foil Trading Card", StringComparison.Ordinal)) {
|
||||
return Steam.Item.EType.FoilTradingCard;
|
||||
} else if (name.EndsWith("Profile Background", StringComparison.Ordinal)) {
|
||||
return Steam.Item.EType.ProfileBackground;
|
||||
} else if (name.EndsWith("Trading Card", StringComparison.Ordinal)) {
|
||||
return Steam.Item.EType.TradingCard;
|
||||
} else {
|
||||
return Steam.Item.EType.Unknown;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal ArchiWebHandler(Bot bot, string apiKey) {
|
||||
internal ArchiWebHandler(Bot bot) {
|
||||
if (bot == null) {
|
||||
throw new ArgumentNullException("bot");
|
||||
}
|
||||
|
||||
Bot = bot;
|
||||
|
||||
if (!string.IsNullOrEmpty(apiKey) && !apiKey.Equals("null")) {
|
||||
ApiKey = apiKey;
|
||||
}
|
||||
WebBrowser = new WebBrowser(bot.BotName);
|
||||
}
|
||||
|
||||
internal async Task Init(SteamClient steamClient, string webAPIUserNonce, string vanityURL, string parentalPin) {
|
||||
internal bool Init(SteamClient steamClient, string webAPIUserNonce, string parentalPin) {
|
||||
if (steamClient == null || steamClient.SteamID == null || string.IsNullOrEmpty(webAPIUserNonce)) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
SteamID = steamClient.SteamID;
|
||||
VanityURL = vanityURL;
|
||||
ulong steamID = steamClient.SteamID;
|
||||
|
||||
string sessionID = Convert.ToBase64String(Encoding.UTF8.GetBytes(SteamID.ToString(CultureInfo.InvariantCulture)));
|
||||
string sessionID = Convert.ToBase64String(Encoding.UTF8.GetBytes(steamID.ToString()));
|
||||
|
||||
// Generate an AES session key
|
||||
byte[] sessionKey = CryptoHelper.GenerateRandomBlock(32);
|
||||
|
||||
// RSA encrypt it with the public key for the universe we're on
|
||||
byte[] cryptedSessionKey = null;
|
||||
byte[] cryptedSessionKey;
|
||||
using (RSACrypto rsa = new RSACrypto(KeyDictionary.GetPublicKey(steamClient.ConnectedUniverse))) {
|
||||
cryptedSessionKey = rsa.Encrypt(sessionKey);
|
||||
}
|
||||
|
||||
// Copy our login key
|
||||
byte[] loginKey = new byte[20];
|
||||
byte[] loginKey = new byte[webAPIUserNonce.Length];
|
||||
Array.Copy(Encoding.ASCII.GetBytes(webAPIUserNonce), loginKey, webAPIUserNonce.Length);
|
||||
|
||||
// AES encrypt the loginkey with our session key
|
||||
byte[] cryptedLoginKey = CryptoHelper.SymmetricEncrypt(loginKey, sessionKey);
|
||||
|
||||
// Send the magic
|
||||
// Do the magic
|
||||
Logging.LogGenericInfo("Logging in to ISteamUserAuth...", Bot.BotName);
|
||||
|
||||
KeyValue authResult;
|
||||
Logging.LogGenericInfo(Bot.BotName, "Logging in to ISteamUserAuth...");
|
||||
using (dynamic iSteamUserAuth = WebAPI.GetInterface("ISteamUserAuth")) {
|
||||
iSteamUserAuth.Timeout = Timeout;
|
||||
|
||||
try {
|
||||
authResult = iSteamUserAuth.AuthenticateUser(
|
||||
steamid: SteamID,
|
||||
steamid: steamID,
|
||||
sessionkey: Encoding.ASCII.GetString(WebUtility.UrlEncodeToBytes(cryptedSessionKey, 0, cryptedSessionKey.Length)),
|
||||
encrypted_loginkey: Encoding.ASCII.GetString(WebUtility.UrlEncodeToBytes(cryptedLoginKey, 0, cryptedLoginKey.Length)),
|
||||
method: WebRequestMethods.Http.Post,
|
||||
secure: true
|
||||
secure: !Program.GlobalConfig.ForceHttp
|
||||
);
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException(Bot.BotName, e);
|
||||
steamClient.Disconnect(); // We may get 403 if we use the same webAPIUserNonce twice
|
||||
return;
|
||||
Logging.LogGenericException(e, Bot.BotName);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (authResult == null) {
|
||||
steamClient.Disconnect(); // Try again
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
Logging.LogGenericInfo(Bot.BotName, "Success!");
|
||||
Logging.LogGenericInfo("Success!", Bot.BotName);
|
||||
|
||||
string steamLogin = authResult["token"].AsString();
|
||||
string steamLoginSecure = authResult["tokensecure"].AsString();
|
||||
WebBrowser.CookieContainer.Add(new Cookie("sessionid", sessionID, "/", "." + SteamCommunityHost));
|
||||
|
||||
SteamCookieDictionary.Clear();
|
||||
SteamCookieDictionary.Add("sessionid", sessionID);
|
||||
SteamCookieDictionary.Add("steamLogin", steamLogin);
|
||||
SteamCookieDictionary.Add("steamLoginSecure", steamLoginSecure);
|
||||
SteamCookieDictionary.Add("birthtime", "-473356799"); // ( ͡° ͜ʖ ͡°)
|
||||
string steamLogin = authResult["token"].Value;
|
||||
WebBrowser.CookieContainer.Add(new Cookie("steamLogin", steamLogin, "/", "." + SteamCommunityHost));
|
||||
|
||||
if (!string.IsNullOrEmpty(parentalPin) && !parentalPin.Equals("0")) {
|
||||
Logging.LogGenericInfo(Bot.BotName, "Unlocking parental account...");
|
||||
Dictionary<string, string> postData = new Dictionary<string, string>() {
|
||||
{"pin", parentalPin}
|
||||
};
|
||||
string steamLoginSecure = authResult["tokensecure"].Value;
|
||||
WebBrowser.CookieContainer.Add(new Cookie("steamLoginSecure", steamLoginSecure, "/", "." + SteamCommunityHost));
|
||||
|
||||
HttpResponseMessage response = await Utilities.UrlPostRequestWithResponse("https://steamcommunity.com/parental/ajaxunlock", postData, SteamCookieDictionary, "https://steamcommunity.com/").ConfigureAwait(false);
|
||||
if (response != null && response.IsSuccessStatusCode) {
|
||||
Logging.LogGenericInfo(Bot.BotName, "Success!");
|
||||
|
||||
var setCookieValues = response.Headers.GetValues("Set-Cookie");
|
||||
foreach (string setCookieValue in setCookieValues) {
|
||||
if (setCookieValue.Contains("steamparental=")) {
|
||||
string setCookie = setCookieValue.Substring(setCookieValue.IndexOf("steamparental=") + 14);
|
||||
setCookie = setCookie.Substring(0, setCookie.IndexOf(';'));
|
||||
SteamCookieDictionary.Add("steamparental", setCookie);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Logging.LogGenericInfo(Bot.BotName, "Failed!");
|
||||
}
|
||||
if (!UnlockParentalAccount(parentalPin).Result) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Bot.Trading.CheckTrades();
|
||||
LastSessionRefreshCheck = DateTime.Now;
|
||||
return true;
|
||||
}
|
||||
|
||||
internal List<SteamTradeOffer> GetTradeOffers() {
|
||||
if (ApiKey == null) {
|
||||
internal async Task<bool> AcceptGift(ulong gid) {
|
||||
if (gid == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string sessionID = WebBrowser.CookieContainer.GetCookieValue(SteamCommunityURL, "sessionid");
|
||||
if (string.IsNullOrEmpty(sessionID)) {
|
||||
Logging.LogNullError("sessionID");
|
||||
return false;
|
||||
}
|
||||
|
||||
string request = SteamCommunityURL + "/gifts/" + gid + "/acceptunpack";
|
||||
Dictionary<string, string> data = new Dictionary<string, string>(1) {
|
||||
{ "sessionid", sessionID }
|
||||
};
|
||||
|
||||
bool result = false;
|
||||
for (byte i = 0; i < WebBrowser.MaxRetries && !result; i++) {
|
||||
result = await WebBrowser.UrlPost(request, data).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal async Task<bool> JoinGroup(ulong groupID) {
|
||||
if (groupID == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string sessionID = WebBrowser.CookieContainer.GetCookieValue(SteamCommunityURL, "sessionid");
|
||||
if (string.IsNullOrEmpty(sessionID)) {
|
||||
Logging.LogNullError("sessionID");
|
||||
return false;
|
||||
}
|
||||
|
||||
string request = SteamCommunityURL + "/gid/" + groupID;
|
||||
Dictionary<string, string> data = new Dictionary<string, string>(2) {
|
||||
{ "sessionID", sessionID },
|
||||
{ "action", "join" }
|
||||
};
|
||||
|
||||
bool result = false;
|
||||
for (byte i = 0; i < WebBrowser.MaxRetries && !result; i++) {
|
||||
result = await WebBrowser.UrlPost(request, data).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal async Task<Dictionary<uint, string>> GetOwnedGames() {
|
||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
KeyValue response;
|
||||
using (dynamic iEconService = WebAPI.GetInterface("IEconService")) {
|
||||
// Timeout
|
||||
iEconService.Timeout = Timeout;
|
||||
string request = SteamCommunityURL + "/my/games/?xml=1";
|
||||
|
||||
try {
|
||||
response = iEconService.GetTradeOffers(
|
||||
key: ApiKey,
|
||||
get_received_offers: 1,
|
||||
active_only: 1,
|
||||
secure: true
|
||||
);
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException(Bot.BotName, e);
|
||||
return null;
|
||||
}
|
||||
XmlDocument response = null;
|
||||
for (byte i = 0; i < WebBrowser.MaxRetries && response == null; i++) {
|
||||
response = await WebBrowser.UrlGetToXML(request).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (response == null) {
|
||||
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
||||
return null;
|
||||
}
|
||||
|
||||
List<SteamTradeOffer> result = new List<SteamTradeOffer>();
|
||||
foreach (KeyValue trade in response["trade_offers_received"].Children) {
|
||||
SteamTradeOffer tradeOffer = new SteamTradeOffer {
|
||||
tradeofferid = trade["tradeofferid"].AsString(),
|
||||
accountid_other = trade["accountid_other"].AsInteger(),
|
||||
message = trade["message"].AsString(),
|
||||
expiration_time = trade["expiration_time"].AsInteger(),
|
||||
trade_offer_state = (SteamTradeOffer.ETradeOfferState) trade["trade_offer_state"].AsInteger(),
|
||||
items_to_give = new List<SteamItem>(),
|
||||
items_to_receive = new List<SteamItem>(),
|
||||
is_our_offer = trade["is_our_offer"].AsBoolean(),
|
||||
time_created = trade["time_created"].AsInteger(),
|
||||
time_updated = trade["time_updated"].AsInteger(),
|
||||
from_real_time_trade = trade["from_real_time_trade"].AsBoolean()
|
||||
};
|
||||
foreach (KeyValue item in trade["items_to_give"].Children) {
|
||||
tradeOffer.items_to_give.Add(new SteamItem {
|
||||
appid = item["appid"].AsString(),
|
||||
contextid = item["contextid"].AsString(),
|
||||
assetid = item["assetid"].AsString(),
|
||||
currencyid = item["currencyid"].AsString(),
|
||||
classid = item["classid"].AsString(),
|
||||
instanceid = item["instanceid"].AsString(),
|
||||
amount = item["amount"].AsString(),
|
||||
missing = item["missing"].AsBoolean()
|
||||
});
|
||||
XmlNodeList xmlNodeList = response.SelectNodes("gamesList/games/game");
|
||||
if (xmlNodeList == null || xmlNodeList.Count == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Dictionary<uint, string> result = new Dictionary<uint, string>(xmlNodeList.Count);
|
||||
foreach (XmlNode xmlNode in xmlNodeList) {
|
||||
XmlNode appNode = xmlNode.SelectSingleNode("appID");
|
||||
if (appNode == null) {
|
||||
continue;
|
||||
}
|
||||
foreach (KeyValue item in trade["items_to_receive"].Children) {
|
||||
tradeOffer.items_to_receive.Add(new SteamItem {
|
||||
appid = item["appid"].AsString(),
|
||||
contextid = item["contextid"].AsString(),
|
||||
assetid = item["assetid"].AsString(),
|
||||
currencyid = item["currencyid"].AsString(),
|
||||
classid = item["classid"].AsString(),
|
||||
instanceid = item["instanceid"].AsString(),
|
||||
amount = item["amount"].AsString(),
|
||||
missing = item["missing"].AsBoolean()
|
||||
});
|
||||
|
||||
uint appID;
|
||||
if (!uint.TryParse(appNode.InnerText, out appID)) {
|
||||
continue;
|
||||
}
|
||||
result.Add(tradeOffer);
|
||||
|
||||
XmlNode nameNode = xmlNode.SelectSingleNode("name");
|
||||
if (nameNode == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
result[appID] = nameNode.InnerText;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
internal async Task JoinClan(ulong clanID) {
|
||||
if (clanID == 0) {
|
||||
return;
|
||||
internal Dictionary<uint, string> GetOwnedGames(ulong steamID) {
|
||||
if (steamID == 0 || string.IsNullOrEmpty(Bot.BotConfig.SteamApiKey)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
string sessionID;
|
||||
if (!SteamCookieDictionary.TryGetValue("sessionid", out sessionID)) {
|
||||
return;
|
||||
KeyValue response = null;
|
||||
using (dynamic iPlayerService = WebAPI.GetInterface("IPlayerService", Bot.BotConfig.SteamApiKey)) {
|
||||
iPlayerService.Timeout = Timeout;
|
||||
|
||||
for (byte i = 0; i < WebBrowser.MaxRetries && response == null; i++) {
|
||||
try {
|
||||
response = iPlayerService.GetOwnedGames(
|
||||
steamid: steamID,
|
||||
include_appinfo: 1,
|
||||
secure: !Program.GlobalConfig.ForceHttp
|
||||
);
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string request = "http://steamcommunity.com/gid/" + clanID;
|
||||
if (response == null) {
|
||||
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries");
|
||||
return null;
|
||||
}
|
||||
|
||||
Dictionary<string, string> postData = new Dictionary<string, string>() {
|
||||
{"sessionID", sessionID},
|
||||
{"action", "join"}
|
||||
};
|
||||
Dictionary<uint, string> result = new Dictionary<uint, string>(response["games"].Children.Count);
|
||||
foreach (KeyValue game in response["games"].Children) {
|
||||
uint appID = (uint) game["appid"].AsUnsignedLong();
|
||||
if (appID == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
await Utilities.UrlPostRequest(request, postData, SteamCookieDictionary).ConfigureAwait(false);
|
||||
result[appID] = game["name"].Value;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
internal async Task LeaveClan(ulong clanID) {
|
||||
if (clanID == 0) {
|
||||
return;
|
||||
internal HashSet<Steam.TradeOffer> GetTradeOffers() {
|
||||
if (string.IsNullOrEmpty(Bot.BotConfig.SteamApiKey)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
string sessionID;
|
||||
if (!SteamCookieDictionary.TryGetValue("sessionid", out sessionID)) {
|
||||
return;
|
||||
KeyValue response = null;
|
||||
using (dynamic iEconService = WebAPI.GetInterface("IEconService", Bot.BotConfig.SteamApiKey)) {
|
||||
iEconService.Timeout = Timeout;
|
||||
|
||||
for (byte i = 0; i < WebBrowser.MaxRetries && response == null; i++) {
|
||||
try {
|
||||
response = iEconService.GetTradeOffers(
|
||||
get_received_offers: 1,
|
||||
active_only: 1,
|
||||
get_descriptions: 1,
|
||||
secure: !Program.GlobalConfig.ForceHttp
|
||||
);
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException(e, Bot.BotName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string request = GetHomeProcess();
|
||||
Dictionary<string, string> postData = new Dictionary<string, string>() {
|
||||
{"sessionID", sessionID},
|
||||
{"action", "leaveGroup"},
|
||||
{"groupId", clanID.ToString()}
|
||||
};
|
||||
await Utilities.UrlPostRequest(request, postData, SteamCookieDictionary).ConfigureAwait(false);
|
||||
if (response == null) {
|
||||
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
||||
return null;
|
||||
}
|
||||
|
||||
Dictionary<Tuple<ulong, ulong>, Tuple<uint, Steam.Item.EType>> descriptions = new Dictionary<Tuple<ulong, ulong>, Tuple<uint, Steam.Item.EType>>();
|
||||
foreach (KeyValue description in response["descriptions"].Children) {
|
||||
ulong classID = description["classid"].AsUnsignedLong();
|
||||
if (classID == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ulong instanceID = description["instanceid"].AsUnsignedLong();
|
||||
|
||||
Tuple<ulong, ulong> key = new Tuple<ulong, ulong>(classID, instanceID);
|
||||
if (descriptions.ContainsKey(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint appID = 0;
|
||||
Steam.Item.EType type = Steam.Item.EType.Unknown;
|
||||
|
||||
string hashName = description["market_hash_name"].Value;
|
||||
if (!string.IsNullOrEmpty(hashName)) {
|
||||
appID = GetAppIDFromMarketHashName(hashName);
|
||||
}
|
||||
|
||||
string descriptionType = description["type"].Value;
|
||||
if (!string.IsNullOrEmpty(descriptionType)) {
|
||||
type = GetItemType(descriptionType);
|
||||
}
|
||||
|
||||
descriptions[key] = new Tuple<uint, Steam.Item.EType>(appID, type);
|
||||
}
|
||||
|
||||
HashSet<Steam.TradeOffer> result = new HashSet<Steam.TradeOffer>();
|
||||
foreach (KeyValue trade in response["trade_offers_received"].Children) {
|
||||
// TODO: Correct some of these when SK2 with https://github.com/SteamRE/SteamKit/pull/255 gets released
|
||||
Steam.TradeOffer tradeOffer = new Steam.TradeOffer {
|
||||
TradeOfferID = trade["tradeofferid"].AsUnsignedLong(),
|
||||
OtherSteamID3 = (uint) trade["accountid_other"].AsUnsignedLong(),
|
||||
State = trade["trade_offer_state"].AsEnum<Steam.TradeOffer.ETradeOfferState>()
|
||||
};
|
||||
|
||||
foreach (KeyValue item in trade["items_to_give"].Children) {
|
||||
Steam.Item steamItem = new Steam.Item {
|
||||
AppID = (uint) item["appid"].AsUnsignedLong(),
|
||||
ContextID = item["contextid"].AsUnsignedLong(),
|
||||
AssetID = item["assetid"].AsUnsignedLong(),
|
||||
ClassID = item["classid"].AsUnsignedLong(),
|
||||
InstanceID = item["instanceid"].AsUnsignedLong(),
|
||||
Amount = (uint) item["amount"].AsUnsignedLong()
|
||||
};
|
||||
|
||||
Tuple<ulong, ulong> key = new Tuple<ulong, ulong>(steamItem.ClassID, steamItem.InstanceID);
|
||||
|
||||
Tuple<uint, Steam.Item.EType> description;
|
||||
if (descriptions.TryGetValue(key, out description)) {
|
||||
steamItem.RealAppID = description.Item1;
|
||||
steamItem.Type = description.Item2;
|
||||
}
|
||||
|
||||
tradeOffer.ItemsToGive.Add(steamItem);
|
||||
}
|
||||
|
||||
foreach (KeyValue item in trade["items_to_receive"].Children) {
|
||||
Steam.Item steamItem = new Steam.Item {
|
||||
AppID = (uint) item["appid"].AsUnsignedLong(),
|
||||
ContextID = item["contextid"].AsUnsignedLong(),
|
||||
AssetID = item["assetid"].AsUnsignedLong(),
|
||||
ClassID = item["classid"].AsUnsignedLong(),
|
||||
InstanceID = item["instanceid"].AsUnsignedLong(),
|
||||
Amount = (uint) item["amount"].AsUnsignedLong()
|
||||
};
|
||||
|
||||
Tuple<ulong, ulong> key = new Tuple<ulong, ulong>(steamItem.ClassID, steamItem.InstanceID);
|
||||
|
||||
Tuple<uint, Steam.Item.EType> description;
|
||||
if (descriptions.TryGetValue(key, out description)) {
|
||||
steamItem.RealAppID = description.Item1;
|
||||
steamItem.Type = description.Item2;
|
||||
}
|
||||
|
||||
tradeOffer.ItemsToReceive.Add(steamItem);
|
||||
}
|
||||
|
||||
result.Add(tradeOffer);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
internal async Task<bool> AcceptTradeOffer(ulong tradeID) {
|
||||
@@ -267,81 +449,352 @@ namespace ArchiSteamFarm {
|
||||
return false;
|
||||
}
|
||||
|
||||
string sessionID;
|
||||
if (!SteamCookieDictionary.TryGetValue("sessionid", out sessionID)) {
|
||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string referer = "https://steamcommunity.com/tradeoffer/" + tradeID + "/";
|
||||
string request = referer + "accept";
|
||||
string sessionID = WebBrowser.CookieContainer.GetCookieValue(SteamCommunityURL, "sessionid");
|
||||
if (string.IsNullOrEmpty(sessionID)) {
|
||||
Logging.LogNullError("sessionID");
|
||||
return false;
|
||||
}
|
||||
|
||||
Dictionary<string, string> postData = new Dictionary<string, string>() {
|
||||
{"sessionid", sessionID},
|
||||
{"serverid", "1"},
|
||||
{"tradeofferid", tradeID.ToString()}
|
||||
string referer = SteamCommunityURL + "/tradeoffer/" + tradeID;
|
||||
string request = referer + "/accept";
|
||||
Dictionary<string, string> data = new Dictionary<string, string>(3) {
|
||||
{ "sessionid", sessionID },
|
||||
{ "serverid", "1" },
|
||||
{ "tradeofferid", tradeID.ToString() }
|
||||
};
|
||||
|
||||
HttpResponseMessage result = await Utilities.UrlPostRequestWithResponse(request, postData, SteamCookieDictionary, referer).ConfigureAwait(false);
|
||||
bool success = result.IsSuccessStatusCode;
|
||||
bool result = false;
|
||||
for (byte i = 0; i < WebBrowser.MaxRetries && !result; i++) {
|
||||
result = await WebBrowser.UrlPost(request, data, referer).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
Logging.LogGenericWarning(Bot.BotName, "Request failed, reason: " + result.ReasonPhrase);
|
||||
switch (result.StatusCode) {
|
||||
case HttpStatusCode.InternalServerError:
|
||||
Logging.LogGenericWarning(Bot.BotName, "That might be caused by 7-days trade lock from new device");
|
||||
Logging.LogGenericWarning(Bot.BotName, "Try again in 7 days, declining that offer for now");
|
||||
DeclineTradeOffer(tradeID);
|
||||
break;
|
||||
if (!result) {
|
||||
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal async Task<HashSet<Steam.Item>> GetMyTradableInventory() {
|
||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
HashSet<Steam.Item> result = new HashSet<Steam.Item>();
|
||||
|
||||
ushort nextPage = 0;
|
||||
while (true) {
|
||||
string request = SteamCommunityURL + "/my/inventory/json/" + Steam.Item.SteamAppID + "/" + Steam.Item.SteamContextID + "?trading=1&start=" + nextPage;
|
||||
|
||||
JObject jObject = null;
|
||||
for (byte i = 0; i < WebBrowser.MaxRetries && jObject == null; i++) {
|
||||
jObject = await WebBrowser.UrlGetToJObject(request).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (jObject == null) {
|
||||
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
||||
return null;
|
||||
}
|
||||
|
||||
IEnumerable<JToken> descriptions = jObject.SelectTokens("$.rgDescriptions.*");
|
||||
if (descriptions == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Dictionary<Tuple<ulong, ulong>, Tuple<uint, Steam.Item.EType>> descriptionMap = new Dictionary<Tuple<ulong, ulong>, Tuple<uint, Steam.Item.EType>>();
|
||||
foreach (JToken description in descriptions) {
|
||||
string classIDString = description["classid"].ToString();
|
||||
if (string.IsNullOrEmpty(classIDString)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ulong classID;
|
||||
if (!ulong.TryParse(classIDString, out classID) || classID == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
string instanceIDString = description["instanceid"].ToString();
|
||||
if (string.IsNullOrEmpty(instanceIDString)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ulong instanceID;
|
||||
if (!ulong.TryParse(instanceIDString, out instanceID)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Tuple<ulong, ulong> key = new Tuple<ulong, ulong>(classID, instanceID);
|
||||
if (descriptionMap.ContainsKey(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint appID = 0;
|
||||
Steam.Item.EType type = Steam.Item.EType.Unknown;
|
||||
|
||||
string hashName = description["market_hash_name"].ToString();
|
||||
if (!string.IsNullOrEmpty(hashName)) {
|
||||
appID = GetAppIDFromMarketHashName(hashName);
|
||||
}
|
||||
|
||||
string descriptionType = description["type"].ToString();
|
||||
if (!string.IsNullOrEmpty(descriptionType)) {
|
||||
type = GetItemType(descriptionType);
|
||||
}
|
||||
|
||||
descriptionMap[key] = new Tuple<uint, Steam.Item.EType>(appID, type);
|
||||
}
|
||||
|
||||
IEnumerable<JToken> items = jObject.SelectTokens("$.rgInventory.*");
|
||||
if (descriptions == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach (JToken item in items) {
|
||||
|
||||
Steam.Item steamItem;
|
||||
|
||||
try {
|
||||
steamItem = JsonConvert.DeserializeObject<Steam.Item>(item.ToString());
|
||||
} catch (JsonException e) {
|
||||
Logging.LogGenericException(e, Bot.BotName);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (steamItem == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Tuple<ulong, ulong> key = new Tuple<ulong, ulong>(steamItem.ClassID, steamItem.InstanceID);
|
||||
|
||||
Tuple<uint, Steam.Item.EType> description;
|
||||
if (descriptionMap.TryGetValue(key, out description)) {
|
||||
steamItem.RealAppID = description.Item1;
|
||||
steamItem.Type = description.Item2;
|
||||
}
|
||||
|
||||
result.Add(steamItem);
|
||||
}
|
||||
|
||||
bool more;
|
||||
if (!bool.TryParse(jObject["more"].ToString(), out more) || !more) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ushort.TryParse(jObject["more_start"].ToString(), out nextPage)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
return result;
|
||||
}
|
||||
|
||||
internal bool DeclineTradeOffer(ulong tradeID) {
|
||||
if (ApiKey == null) {
|
||||
internal async Task<bool> SendTradeOffer(HashSet<Steam.Item> inventory, ulong partnerID, string token = null) {
|
||||
if (inventory == null || inventory.Count == 0 || partnerID == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tradeID == 0) {
|
||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
KeyValue response;
|
||||
using (dynamic iEconService = WebAPI.GetInterface("IEconService")) {
|
||||
// Timeout
|
||||
iEconService.Timeout = Timeout;
|
||||
string sessionID = WebBrowser.CookieContainer.GetCookieValue(SteamCommunityURL, "sessionid");
|
||||
if (string.IsNullOrEmpty(sessionID)) {
|
||||
Logging.LogNullError("sessionID");
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
response = iEconService.DeclineTradeOffer(
|
||||
key: ApiKey,
|
||||
tradeofferid: tradeID.ToString(),
|
||||
method: WebRequestMethods.Http.Post,
|
||||
secure: true
|
||||
);
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException(Bot.BotName, e);
|
||||
HashSet<Steam.TradeOfferRequest> trades = new HashSet<Steam.TradeOfferRequest>();
|
||||
|
||||
Steam.TradeOfferRequest singleTrade = null;
|
||||
|
||||
byte itemID = 0;
|
||||
foreach (Steam.Item item in inventory) {
|
||||
if (itemID % Trading.MaxItemsPerTrade == 0) {
|
||||
if (trades.Count >= Trading.MaxTradesPerAccount) {
|
||||
break;
|
||||
}
|
||||
|
||||
singleTrade = new Steam.TradeOfferRequest();
|
||||
trades.Add(singleTrade);
|
||||
itemID = 0;
|
||||
}
|
||||
|
||||
singleTrade.ItemsToGive.Assets.Add(new Steam.Item() {
|
||||
AppID = Steam.Item.SteamAppID,
|
||||
ContextID = Steam.Item.SteamContextID,
|
||||
Amount = item.Amount,
|
||||
AssetID = item.AssetID
|
||||
});
|
||||
|
||||
itemID++;
|
||||
}
|
||||
|
||||
string referer = SteamCommunityURL + "/tradeoffer/new";
|
||||
string request = referer + "/send";
|
||||
foreach (Steam.TradeOfferRequest trade in trades) {
|
||||
Dictionary<string, string> data = new Dictionary<string, string>(6) {
|
||||
{ "sessionid", sessionID },
|
||||
{ "serverid", "1" },
|
||||
{ "partner", partnerID.ToString() },
|
||||
{ "tradeoffermessage", "Sent by ASF" },
|
||||
{ "json_tradeoffer", JsonConvert.SerializeObject(trade) },
|
||||
{ "trade_offer_create_params", string.IsNullOrEmpty(token) ? "" : $"{{\"trade_offer_access_token\":\"{token}\"}}" }
|
||||
};
|
||||
|
||||
bool result = false;
|
||||
for (byte i = 0; i < WebBrowser.MaxRetries && !result; i++) {
|
||||
result = await WebBrowser.UrlPost(request, data, referer).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return response != null; // Steam API doesn't respond with any error code, assume any response is a success
|
||||
return true;
|
||||
}
|
||||
|
||||
internal async Task<HtmlDocument> GetBadgePage(int page) {
|
||||
if (SteamID == 0 || page == 0) {
|
||||
internal async Task<HtmlDocument> GetBadgePage(byte page) {
|
||||
if (page == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return await Utilities.UrlToHtmlDocument("http://steamcommunity.com/profiles/" + SteamID + "/badges?p=" + page, SteamCookieDictionary).ConfigureAwait(false);
|
||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
string request = SteamCommunityURL + "/my/badges?p=" + page;
|
||||
|
||||
HtmlDocument htmlDocument = null;
|
||||
for (byte i = 0; i < WebBrowser.MaxRetries && htmlDocument == null; i++) {
|
||||
htmlDocument = await WebBrowser.UrlGetToHtmlDocument(request).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (htmlDocument == null) {
|
||||
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
||||
return null;
|
||||
}
|
||||
|
||||
return htmlDocument;
|
||||
}
|
||||
|
||||
internal async Task<HtmlDocument> GetGameCardsPage(ulong appID) {
|
||||
if (SteamID == 0 || appID == 0) {
|
||||
if (appID == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return await Utilities.UrlToHtmlDocument("http://steamcommunity.com/profiles/" + SteamID + "/gamecards/" + appID, SteamCookieDictionary).ConfigureAwait(false);
|
||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
string request = SteamCommunityURL + "/my/gamecards/" + appID;
|
||||
|
||||
HtmlDocument htmlDocument = null;
|
||||
for (byte i = 0; i < WebBrowser.MaxRetries && htmlDocument == null; i++) {
|
||||
htmlDocument = await WebBrowser.UrlGetToHtmlDocument(request).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (htmlDocument == null) {
|
||||
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
||||
return null;
|
||||
}
|
||||
|
||||
return htmlDocument;
|
||||
}
|
||||
|
||||
internal async Task<bool> MarkInventory() {
|
||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string request = SteamCommunityURL + "/my/inventory";
|
||||
|
||||
bool result = false;
|
||||
for (byte i = 0; i < WebBrowser.MaxRetries && !result; i++) {
|
||||
result = await WebBrowser.UrlHead(request).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task<bool?> IsLoggedIn() {
|
||||
string request = SteamCommunityURL + "/my/profile";
|
||||
|
||||
Uri uri = null;
|
||||
for (byte i = 0; i < WebBrowser.MaxRetries && uri == null; i++) {
|
||||
uri = await WebBrowser.UrlHeadToUri(request).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (uri == null) {
|
||||
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
||||
return null;
|
||||
}
|
||||
|
||||
return !uri.AbsolutePath.StartsWith("/login", StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
private async Task<bool> RefreshSessionIfNeeded() {
|
||||
if (DateTime.Now.Subtract(LastSessionRefreshCheck).TotalSeconds < MinSessionTTL) {
|
||||
return true;
|
||||
}
|
||||
|
||||
await SessionSemaphore.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
if (DateTime.Now.Subtract(LastSessionRefreshCheck).TotalSeconds < MinSessionTTL) {
|
||||
SessionSemaphore.Release();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool result;
|
||||
|
||||
bool? isLoggedIn = await IsLoggedIn().ConfigureAwait(false);
|
||||
if (isLoggedIn.GetValueOrDefault(true)) {
|
||||
result = true;
|
||||
LastSessionRefreshCheck = DateTime.Now;
|
||||
} else {
|
||||
Logging.LogGenericInfo("Refreshing our session!", Bot.BotName);
|
||||
result = await Bot.RefreshSession().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
SessionSemaphore.Release();
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task<bool> UnlockParentalAccount(string parentalPin) {
|
||||
if (string.IsNullOrEmpty(parentalPin) || parentalPin.Equals("0")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Logging.LogGenericInfo("Unlocking parental account...", Bot.BotName);
|
||||
|
||||
string request = SteamCommunityURL + "/parental/ajaxunlock";
|
||||
Dictionary<string, string> data = new Dictionary<string, string>(1) {
|
||||
{ "pin", parentalPin }
|
||||
};
|
||||
|
||||
bool result = false;
|
||||
for (byte i = 0; i < WebBrowser.MaxRetries && !result; i++) {
|
||||
result = await WebBrowser.UrlPost(request, data, SteamCommunityURL).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
||||
return false;
|
||||
}
|
||||
|
||||
Logging.LogGenericInfo("Success!", Bot.BotName);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1829
ArchiSteamFarm/Bot.cs
Normal file → Executable file
1829
ArchiSteamFarm/Bot.cs
Normal file → Executable file
File diff suppressed because it is too large
Load Diff
124
ArchiSteamFarm/BotConfig.cs
Normal file
124
ArchiSteamFarm/BotConfig.cs
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
_ _ _ ____ _ _____
|
||||
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
|
||||
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
|
||||
Contact: JustArchi@JustArchi.net
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal sealed class BotConfig {
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal bool Enabled { get; private set; } = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal bool StartOnLaunch { get; private set; } = true;
|
||||
|
||||
[JsonProperty]
|
||||
internal string SteamLogin { get; set; } = null;
|
||||
|
||||
[JsonProperty]
|
||||
internal string SteamPassword { get; set; } = null;
|
||||
|
||||
[JsonProperty]
|
||||
internal string SteamParentalPIN { get; set; } = "0";
|
||||
|
||||
[JsonProperty]
|
||||
internal string SteamApiKey { get; private set; } = null;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal ulong SteamMasterID { get; private set; } = 0;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal ulong SteamMasterClanID { get; private set; } = 0;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal bool CardDropsRestricted { get; private set; } = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal bool DismissInventoryNotifications { get; private set; } = true;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal bool FarmOffline { get; private set; } = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal bool HandleOfflineMessages { get; private set; } = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal bool AcceptGifts { get; private set; } = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal bool SteamTradeMatcher { get; private set; } = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal bool ForwardKeysToOtherBots { get; private set; } = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal bool DistributeKeys { get; private set; } = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal bool UseAsfAsMobileAuthenticator { get; private set; } = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal bool ShutdownOnFarmingFinished { get; private set; } = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal bool SendOnFarmingFinished { get; private set; } = false;
|
||||
|
||||
[JsonProperty]
|
||||
internal string SteamTradeToken { get; private set; } = null;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal byte SendTradePeriod { get; private set; } = 0;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal byte AcceptConfirmationsPeriod { get; private set; } = 0;
|
||||
|
||||
[JsonProperty]
|
||||
internal string CustomGamePlayedWhileIdle { get; private set; } = null;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal HashSet<uint> GamesPlayedWhileIdle { get; private set; } = new HashSet<uint>() { 0 };
|
||||
|
||||
|
||||
internal static BotConfig Load(string filePath) {
|
||||
if (string.IsNullOrEmpty(filePath) || !File.Exists(filePath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
BotConfig botConfig;
|
||||
try {
|
||||
botConfig = JsonConvert.DeserializeObject<BotConfig>(File.ReadAllText(filePath));
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException(e);
|
||||
return null;
|
||||
}
|
||||
|
||||
return botConfig;
|
||||
}
|
||||
|
||||
// This constructor is used only by deserializer
|
||||
private BotConfig() { }
|
||||
}
|
||||
}
|
||||
116
ArchiSteamFarm/BotDatabase.cs
Normal file
116
ArchiSteamFarm/BotDatabase.cs
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
_ _ _ ____ _ _____
|
||||
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
|
||||
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
|
||||
Contact: JustArchi@JustArchi.net
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using SteamAuth;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal sealed class BotDatabase {
|
||||
internal string LoginKey {
|
||||
get {
|
||||
return _LoginKey;
|
||||
}
|
||||
set {
|
||||
if (_LoginKey == value) {
|
||||
return;
|
||||
}
|
||||
|
||||
_LoginKey = value;
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
||||
internal SteamGuardAccount SteamGuardAccount {
|
||||
get {
|
||||
return _SteamGuardAccount;
|
||||
}
|
||||
set {
|
||||
if (_SteamGuardAccount == value) {
|
||||
return;
|
||||
}
|
||||
|
||||
_SteamGuardAccount = value;
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
||||
[JsonProperty]
|
||||
private string _LoginKey;
|
||||
|
||||
[JsonProperty]
|
||||
private SteamGuardAccount _SteamGuardAccount;
|
||||
|
||||
private string FilePath;
|
||||
|
||||
internal static BotDatabase Load(string filePath) {
|
||||
if (string.IsNullOrEmpty(filePath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!File.Exists(filePath)) {
|
||||
return new BotDatabase(filePath);
|
||||
}
|
||||
|
||||
BotDatabase botDatabase;
|
||||
try {
|
||||
botDatabase = JsonConvert.DeserializeObject<BotDatabase>(File.ReadAllText(filePath));
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException(e);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (botDatabase == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
botDatabase.FilePath = filePath;
|
||||
return botDatabase;
|
||||
}
|
||||
|
||||
// This constructor is used when creating new database
|
||||
private BotDatabase(string filePath) {
|
||||
if (string.IsNullOrEmpty(filePath)) {
|
||||
throw new ArgumentNullException("filePath");
|
||||
}
|
||||
|
||||
FilePath = filePath;
|
||||
Save();
|
||||
}
|
||||
|
||||
// This constructor is used only by deserializer
|
||||
private BotDatabase() { }
|
||||
|
||||
internal void Save() {
|
||||
lock (FilePath) {
|
||||
try {
|
||||
File.WriteAllText(FilePath, JsonConvert.SerializeObject(this));
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
/*
|
||||
_ _ _ ____ _ _____
|
||||
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
|
||||
Copyright 2015 Łukasz "JustArchi" Domeradzki
|
||||
Contact: JustArchi@JustArchi.net
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
using SteamKit2;
|
||||
using SteamKit2.Internal;
|
||||
using System.IO;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
/// <summary>
|
||||
/// Message used to Accept or Decline a group(clan) invite.
|
||||
/// </summary>
|
||||
internal sealed class CMsgClientClanInviteAction : ISteamSerializableMessage, ISteamSerializable {
|
||||
EMsg ISteamSerializableMessage.GetEMsg() {
|
||||
return EMsg.ClientAcknowledgeClanInvite;
|
||||
}
|
||||
|
||||
public CMsgClientClanInviteAction() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Group invited to.
|
||||
/// </summary>
|
||||
internal ulong GroupID = 0;
|
||||
|
||||
/// <summary>
|
||||
/// To accept or decline the invite.
|
||||
/// </summary>
|
||||
internal bool AcceptInvite = true;
|
||||
|
||||
void ISteamSerializable.Serialize(Stream stream) {
|
||||
try {
|
||||
BinaryWriter binaryWriter = new BinaryWriter(stream);
|
||||
binaryWriter.Write(GroupID);
|
||||
binaryWriter.Write(AcceptInvite);
|
||||
} catch {
|
||||
throw new IOException();
|
||||
}
|
||||
}
|
||||
|
||||
void ISteamSerializable.Deserialize(Stream stream) {
|
||||
try {
|
||||
BinaryReader binaryReader = new BinaryReader(stream);
|
||||
GroupID = binaryReader.ReadUInt64();
|
||||
AcceptInvite = binaryReader.ReadBoolean();
|
||||
} catch {
|
||||
throw new IOException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
487
ArchiSteamFarm/CardsFarmer.cs
Normal file → Executable file
487
ArchiSteamFarm/CardsFarmer.cs
Normal file → Executable file
@@ -5,7 +5,7 @@
|
||||
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
|
||||
Copyright 2015 Łukasz "JustArchi" Domeradzki
|
||||
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
|
||||
Contact: JustArchi@JustArchi.net
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -23,156 +23,447 @@
|
||||
*/
|
||||
|
||||
using HtmlAgilityPack;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal class CardsFarmer {
|
||||
private const byte StatusCheckSleep = 5; // In minutes, how long to wait before checking the appID again
|
||||
internal sealed class CardsFarmer {
|
||||
internal readonly ConcurrentDictionary<uint, float> GamesToFarm = new ConcurrentDictionary<uint, float>();
|
||||
internal readonly HashSet<uint> CurrentGamesFarming = new HashSet<uint>();
|
||||
|
||||
private readonly ManualResetEvent FarmResetEvent = new ManualResetEvent(false);
|
||||
private readonly SemaphoreSlim Semaphore = new SemaphoreSlim(1);
|
||||
private readonly ManualResetEventSlim FarmResetEvent = new ManualResetEventSlim(false);
|
||||
private readonly SemaphoreSlim FarmingSemaphore = new SemaphoreSlim(1);
|
||||
private readonly Bot Bot;
|
||||
private readonly Timer Timer;
|
||||
|
||||
private volatile bool NowFarming = false;
|
||||
internal bool ManualMode { get; private set; }
|
||||
|
||||
private bool NowFarming;
|
||||
|
||||
internal CardsFarmer(Bot bot) {
|
||||
if (bot == null) {
|
||||
throw new ArgumentNullException("bot");
|
||||
}
|
||||
|
||||
Bot = bot;
|
||||
|
||||
if (Timer == null && Program.GlobalConfig.IdleFarmingPeriod > 0) {
|
||||
Timer = new Timer(
|
||||
async e => await CheckGamesForFarming().ConfigureAwait(false),
|
||||
null,
|
||||
TimeSpan.FromHours(Program.GlobalConfig.IdleFarmingPeriod), // Delay
|
||||
TimeSpan.FromHours(Program.GlobalConfig.IdleFarmingPeriod) // Period
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task SwitchToManualMode(bool manualMode) {
|
||||
if (ManualMode == manualMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
ManualMode = manualMode;
|
||||
|
||||
if (ManualMode) {
|
||||
Logging.LogGenericInfo("Now running in Manual Farming mode", Bot.BotName);
|
||||
await StopFarming().ConfigureAwait(false);
|
||||
} else {
|
||||
Logging.LogGenericInfo("Now running in Automatic Farming mode", Bot.BotName);
|
||||
StartFarming().Forget();
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task StartFarming() {
|
||||
await StopFarming().ConfigureAwait(false);
|
||||
|
||||
await Semaphore.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
if (NowFarming) {
|
||||
Semaphore.Release();
|
||||
if (NowFarming || ManualMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
Logging.LogGenericInfo(Bot.BotName, "Checking badges...");
|
||||
await FarmingSemaphore.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
// Find the number of badge pages
|
||||
HtmlDocument badgesDocument = await Bot.ArchiWebHandler.GetBadgePage(1).ConfigureAwait(false);
|
||||
if (badgesDocument == null) {
|
||||
Logging.LogGenericWarning(Bot.BotName, "Could not get badges information, farming is stopped!");
|
||||
Semaphore.Release();
|
||||
if (NowFarming || ManualMode) {
|
||||
FarmingSemaphore.Release(); // We have nothing to do, don't forget to release semaphore
|
||||
return;
|
||||
}
|
||||
|
||||
var maxPages = 1;
|
||||
HtmlNodeCollection badgesPagesNodeCollection = badgesDocument.DocumentNode.SelectNodes("//a[@class='pagelink']");
|
||||
if (badgesPagesNodeCollection != null) {
|
||||
maxPages = (badgesPagesNodeCollection.Count / 2) + 1; // Don't do this at home
|
||||
if (!await IsAnythingToFarm().ConfigureAwait(false)) {
|
||||
FarmingSemaphore.Release(); // We have nothing to do, don't forget to release semaphore
|
||||
Logging.LogGenericInfo("We don't have anything to farm on this account!", Bot.BotName);
|
||||
await Bot.OnFarmingFinished(false).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Find APPIDs we need to farm
|
||||
List<uint> appIDs = new List<uint>();
|
||||
for (var page = 1; page <= maxPages; page++) {
|
||||
Logging.LogGenericInfo(Bot.BotName, "Checking page: " + page + "/" + maxPages);
|
||||
Logging.LogGenericInfo("We have a total of " + GamesToFarm.Count + " games to farm on this account...", Bot.BotName);
|
||||
NowFarming = true;
|
||||
FarmingSemaphore.Release(); // From this point we allow other calls to shut us down
|
||||
|
||||
if (page > 1) { // Because we fetched page number 1 already
|
||||
badgesDocument = await Bot.ArchiWebHandler.GetBadgePage(page).ConfigureAwait(false);
|
||||
if (badgesDocument == null) {
|
||||
break;
|
||||
do {
|
||||
// Now the algorithm used for farming depends on whether account is restricted or not
|
||||
if (Bot.BotConfig.CardDropsRestricted) { // If we have restricted card drops, we use complex algorithm
|
||||
Logging.LogGenericInfo("Chosen farming algorithm: Complex", Bot.BotName);
|
||||
while (GamesToFarm.Count > 0) {
|
||||
HashSet<uint> gamesToFarmSolo = GetGamesToFarmSolo(GamesToFarm);
|
||||
if (gamesToFarmSolo.Count > 0) {
|
||||
while (gamesToFarmSolo.Count > 0) {
|
||||
uint appID = gamesToFarmSolo.First();
|
||||
if (await FarmSolo(appID).ConfigureAwait(false)) {
|
||||
gamesToFarmSolo.Remove(appID);
|
||||
gamesToFarmSolo.TrimExcess();
|
||||
} else {
|
||||
NowFarming = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (FarmMultiple()) {
|
||||
Logging.LogGenericInfo("Done farming: " + string.Join(", ", GamesToFarm.Keys), Bot.BotName);
|
||||
} else {
|
||||
NowFarming = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else { // If we have unrestricted card drops, we use simple algorithm
|
||||
Logging.LogGenericInfo("Chosen farming algorithm: Simple", Bot.BotName);
|
||||
while (GamesToFarm.Count > 0) {
|
||||
uint appID = GamesToFarm.Keys.FirstOrDefault();
|
||||
if (!await FarmSolo(appID).ConfigureAwait(false)) {
|
||||
NowFarming = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (await IsAnythingToFarm().ConfigureAwait(false));
|
||||
|
||||
HtmlNodeCollection badgesPageNodes = badgesDocument.DocumentNode.SelectNodes("//a[@class='btn_green_white_innerfade btn_small_thin']");
|
||||
if (badgesPageNodes == null) {
|
||||
continue;
|
||||
}
|
||||
CurrentGamesFarming.Clear();
|
||||
CurrentGamesFarming.TrimExcess();
|
||||
NowFarming = false;
|
||||
|
||||
foreach (HtmlNode badgesPageNode in badgesPageNodes) {
|
||||
string steamLink = badgesPageNode.GetAttributeValue("href", null);
|
||||
if (steamLink == null) {
|
||||
Logging.LogGenericError(Bot.BotName, "Couldn't get steamLink for one of the games: " + badgesPageNode.OuterHtml);
|
||||
continue;
|
||||
}
|
||||
|
||||
uint appID = (uint) Utilities.OnlyNumbers(steamLink);
|
||||
if (appID == 0) {
|
||||
Logging.LogGenericError(Bot.BotName, "Couldn't get appID for one of the games: " + badgesPageNode.OuterHtml);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Bot.Blacklist.Contains(appID)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
appIDs.Add(appID);
|
||||
}
|
||||
}
|
||||
|
||||
// Start farming
|
||||
while (appIDs.Count > 0) {
|
||||
Logging.LogGenericInfo(Bot.BotName, "Farming in progress...");
|
||||
uint appID = appIDs[0];
|
||||
if (await Farm(appID).ConfigureAwait(false)) {
|
||||
appIDs.Remove(appID);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Logging.LogGenericInfo(Bot.BotName, "Farming finished!");
|
||||
await Bot.OnFarmingFinished().ConfigureAwait(false);
|
||||
Logging.LogGenericInfo("Farming finished!", Bot.BotName);
|
||||
await Bot.OnFarmingFinished(true).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
internal async Task StopFarming() {
|
||||
await Semaphore.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
if (!NowFarming) {
|
||||
Semaphore.Release();
|
||||
return;
|
||||
}
|
||||
|
||||
Logging.LogGenericInfo(Bot.BotName, "Sending signal to stop farming");
|
||||
await FarmingSemaphore.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
if (!NowFarming) {
|
||||
FarmingSemaphore.Release();
|
||||
return;
|
||||
}
|
||||
|
||||
Logging.LogGenericInfo("Sending signal to stop farming", Bot.BotName);
|
||||
FarmResetEvent.Set();
|
||||
while (NowFarming) {
|
||||
Logging.LogGenericInfo(Bot.BotName, "Waiting for reaction...");
|
||||
|
||||
Logging.LogGenericInfo("Waiting for reaction...", Bot.BotName);
|
||||
for (byte i = 0; i < Program.GlobalConfig.HttpTimeout && NowFarming; i++) {
|
||||
await Utilities.SleepAsync(1000).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (NowFarming) {
|
||||
Logging.LogGenericWarning("Timed out!", Bot.BotName);
|
||||
}
|
||||
|
||||
FarmResetEvent.Reset();
|
||||
Logging.LogGenericInfo(Bot.BotName, "Farming stopped!");
|
||||
Semaphore.Release();
|
||||
Logging.LogGenericInfo("Farming stopped!", Bot.BotName);
|
||||
FarmingSemaphore.Release();
|
||||
}
|
||||
|
||||
private async Task<bool?> ShouldFarm(ulong appID) {
|
||||
bool? result = null;
|
||||
HtmlDocument gamePageDocument = await Bot.ArchiWebHandler.GetGameCardsPage(appID).ConfigureAwait(false);
|
||||
if (gamePageDocument != null) {
|
||||
HtmlNode gamePageNode = gamePageDocument.DocumentNode.SelectSingleNode("//span[@class='progress_info_bold']");
|
||||
if (gamePageNode != null) {
|
||||
result = !gamePageNode.InnerText.Contains("No card drops");
|
||||
internal async Task RestartFarming() {
|
||||
await StopFarming().ConfigureAwait(false);
|
||||
await StartFarming().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static HashSet<uint> GetGamesToFarmSolo(ConcurrentDictionary<uint, float> gamesToFarm) {
|
||||
if (gamesToFarm == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
HashSet<uint> result = new HashSet<uint>();
|
||||
foreach (KeyValuePair<uint, float> keyValue in gamesToFarm) {
|
||||
if (keyValue.Value >= 2) {
|
||||
result.Add(keyValue.Key);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task<bool> Farm(ulong appID) {
|
||||
bool success = true;
|
||||
bool? keepFarming = await ShouldFarm(appID).ConfigureAwait(false);
|
||||
while (keepFarming == null || keepFarming.Value) {
|
||||
if (!NowFarming) {
|
||||
NowFarming = true;
|
||||
Logging.LogGenericInfo(Bot.BotName, "Now farming: " + appID);
|
||||
Bot.PlayGame(appID);
|
||||
Semaphore.Release(); // We're farming, allow other tasks to shut us down
|
||||
} else {
|
||||
Logging.LogGenericInfo(Bot.BotName, "Still farming: " + appID);
|
||||
private async Task<bool> IsAnythingToFarm() {
|
||||
Logging.LogGenericInfo("Checking badges...", Bot.BotName);
|
||||
|
||||
// Find the number of badge pages
|
||||
Logging.LogGenericInfo("Checking first page...", Bot.BotName);
|
||||
HtmlDocument htmlDocument = await Bot.ArchiWebHandler.GetBadgePage(1).ConfigureAwait(false);
|
||||
if (htmlDocument == null) {
|
||||
Logging.LogGenericWarning("Could not get badges information, will try again later!", Bot.BotName);
|
||||
return false;
|
||||
}
|
||||
|
||||
byte maxPages = 1;
|
||||
HtmlNodeCollection htmlNodeCollection = htmlDocument.DocumentNode.SelectNodes("//a[@class='pagelink']");
|
||||
if (htmlNodeCollection != null && htmlNodeCollection.Count > 0) {
|
||||
HtmlNode htmlNode = htmlNodeCollection[htmlNodeCollection.Count - 1];
|
||||
string lastPage = htmlNode.InnerText;
|
||||
if (!string.IsNullOrEmpty(lastPage)) {
|
||||
if (!byte.TryParse(lastPage, out maxPages)) {
|
||||
maxPages = 1; // Should never happen
|
||||
}
|
||||
}
|
||||
if (FarmResetEvent.WaitOne(1000 * 60 * StatusCheckSleep)) {
|
||||
}
|
||||
|
||||
GamesToFarm.Clear();
|
||||
|
||||
CheckPage(htmlDocument);
|
||||
|
||||
if (maxPages > 1) {
|
||||
Logging.LogGenericInfo("Checking other pages...", Bot.BotName);
|
||||
List<Task> tasks = new List<Task>(maxPages - 1);
|
||||
for (byte page = 2; page <= maxPages; page++) {
|
||||
byte currentPage = page; // We need a copy of variable being passed when in for loops, as loop will proceed before task is launched
|
||||
tasks.Add(CheckPage(currentPage));
|
||||
}
|
||||
await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return GamesToFarm.Count > 0;
|
||||
}
|
||||
|
||||
private void CheckPage(HtmlDocument htmlDocument) {
|
||||
if (htmlDocument == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
HtmlNodeCollection htmlNodes = htmlDocument.DocumentNode.SelectNodes("//div[@class='badge_title_stats']");
|
||||
if (htmlNodes == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (HtmlNode htmlNode in htmlNodes) {
|
||||
HtmlNode farmingNode = htmlNode.SelectSingleNode(".//a[@class='btn_green_white_innerfade btn_small_thin']");
|
||||
if (farmingNode == null) {
|
||||
continue; // This game is not needed for farming
|
||||
}
|
||||
|
||||
string steamLink = farmingNode.GetAttributeValue("href", null);
|
||||
if (string.IsNullOrEmpty(steamLink)) {
|
||||
Logging.LogNullError("steamLink", Bot.BotName);
|
||||
continue;
|
||||
}
|
||||
|
||||
int index = steamLink.LastIndexOf('/');
|
||||
if (index < 0) {
|
||||
Logging.LogNullError("index", Bot.BotName);
|
||||
continue;
|
||||
}
|
||||
|
||||
index++;
|
||||
if (steamLink.Length <= index) {
|
||||
Logging.LogNullError("length", Bot.BotName);
|
||||
continue;
|
||||
}
|
||||
|
||||
steamLink = steamLink.Substring(index);
|
||||
|
||||
uint appID;
|
||||
if (!uint.TryParse(steamLink, out appID)) {
|
||||
Logging.LogNullError("appID", Bot.BotName);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (appID == 0) {
|
||||
Logging.LogNullError("appID", Bot.BotName);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (GlobalConfig.GlobalBlacklist.Contains(appID) || Program.GlobalConfig.Blacklist.Contains(appID)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
HtmlNode timeNode = htmlNode.SelectSingleNode(".//div[@class='badge_title_stats_playtime']");
|
||||
if (timeNode == null) {
|
||||
Logging.LogNullError("timeNode", Bot.BotName);
|
||||
continue;
|
||||
}
|
||||
|
||||
string hoursString = timeNode.InnerText;
|
||||
if (string.IsNullOrEmpty(hoursString)) {
|
||||
Logging.LogNullError("hoursString", Bot.BotName);
|
||||
continue;
|
||||
}
|
||||
|
||||
float hours = 0;
|
||||
|
||||
Match match = Regex.Match(hoursString, @"[0-9\.,]+");
|
||||
if (match.Success) {
|
||||
float.TryParse(match.Value, NumberStyles.Number, CultureInfo.InvariantCulture, out hours);
|
||||
}
|
||||
|
||||
GamesToFarm[appID] = hours;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CheckPage(byte page) {
|
||||
if (page == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
HtmlDocument htmlDocument = await Bot.ArchiWebHandler.GetBadgePage(page).ConfigureAwait(false);
|
||||
if (htmlDocument == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
CheckPage(htmlDocument);
|
||||
}
|
||||
|
||||
private async Task CheckGamesForFarming() {
|
||||
if (NowFarming || ManualMode || !Bot.SteamClient.IsConnected) {
|
||||
return;
|
||||
}
|
||||
|
||||
await StartFarming().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task<bool?> ShouldFarm(uint appID) {
|
||||
if (appID == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HtmlDocument htmlDocument = await Bot.ArchiWebHandler.GetGameCardsPage(appID).ConfigureAwait(false);
|
||||
if (htmlDocument == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
HtmlNode htmlNode = htmlDocument.DocumentNode.SelectSingleNode("//span[@class='progress_info_bold']");
|
||||
if (htmlNode == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return !htmlNode.InnerText.Contains("No card drops");
|
||||
}
|
||||
|
||||
private bool FarmMultiple() {
|
||||
if (GamesToFarm.Count == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
float maxHour = 0;
|
||||
foreach (KeyValuePair<uint, float> game in GamesToFarm) {
|
||||
CurrentGamesFarming.Add(game.Key);
|
||||
if (game.Value > maxHour) {
|
||||
maxHour = game.Value;
|
||||
}
|
||||
}
|
||||
|
||||
if (maxHour >= 2) {
|
||||
CurrentGamesFarming.Clear();
|
||||
CurrentGamesFarming.TrimExcess();
|
||||
return true;
|
||||
}
|
||||
|
||||
Logging.LogGenericInfo("Now farming: " + string.Join(", ", CurrentGamesFarming), Bot.BotName);
|
||||
if (FarmHours(maxHour, CurrentGamesFarming)) {
|
||||
CurrentGamesFarming.Clear();
|
||||
CurrentGamesFarming.TrimExcess();
|
||||
return true;
|
||||
} else {
|
||||
CurrentGamesFarming.Clear();
|
||||
CurrentGamesFarming.TrimExcess();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<bool> FarmSolo(uint appID) {
|
||||
if (appID == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
CurrentGamesFarming.Add(appID);
|
||||
|
||||
Logging.LogGenericInfo("Now farming: " + appID, Bot.BotName);
|
||||
if (await Farm(appID).ConfigureAwait(false)) {
|
||||
CurrentGamesFarming.Clear();
|
||||
CurrentGamesFarming.TrimExcess();
|
||||
float hours;
|
||||
if (GamesToFarm.TryRemove(appID, out hours)) {
|
||||
TimeSpan timeSpan = TimeSpan.FromHours(hours);
|
||||
Logging.LogGenericInfo("Done farming: " + appID + " after " + timeSpan.ToString(@"hh\:mm") + " hours of playtime!", Bot.BotName);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
CurrentGamesFarming.Clear();
|
||||
CurrentGamesFarming.TrimExcess();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<bool> Farm(uint appID) {
|
||||
if (appID == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Bot.ArchiHandler.PlayGames(appID);
|
||||
|
||||
bool success = true;
|
||||
|
||||
bool? keepFarming = await ShouldFarm(appID).ConfigureAwait(false);
|
||||
for (ushort farmingTime = 0; farmingTime <= 60 * Program.GlobalConfig.MaxFarmingTime && keepFarming.GetValueOrDefault(true); farmingTime += Program.GlobalConfig.FarmingDelay) {
|
||||
if (FarmResetEvent.Wait(60 * 1000 * Program.GlobalConfig.FarmingDelay)) {
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// Don't forget to update our GamesToFarm hours
|
||||
float timePlayed = Program.GlobalConfig.FarmingDelay / 60.0F;
|
||||
GamesToFarm[appID] += timePlayed;
|
||||
|
||||
keepFarming = await ShouldFarm(appID).ConfigureAwait(false);
|
||||
Logging.LogGenericInfo("Still farming: " + appID, Bot.BotName);
|
||||
}
|
||||
|
||||
Bot.PlayGame(0);
|
||||
NowFarming = false;
|
||||
Logging.LogGenericInfo(Bot.BotName, "Stopped farming: " + appID);
|
||||
Bot.ResetGamesPlayed();
|
||||
Logging.LogGenericInfo("Stopped farming: " + appID, Bot.BotName);
|
||||
return success;
|
||||
}
|
||||
|
||||
private bool FarmHours(float maxHour, HashSet<uint> appIDs) {
|
||||
if (maxHour < 0 || appIDs == null || appIDs.Count == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (maxHour >= 2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Bot.ArchiHandler.PlayGames(appIDs);
|
||||
|
||||
bool success = true;
|
||||
while (maxHour < 2) {
|
||||
if (FarmResetEvent.Wait(60 * 1000 * Program.GlobalConfig.FarmingDelay)) {
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// Don't forget to update our GamesToFarm hours
|
||||
float timePlayed = Program.GlobalConfig.FarmingDelay / 60.0F;
|
||||
foreach (uint appID in appIDs) {
|
||||
GamesToFarm[appID] += timePlayed;
|
||||
}
|
||||
|
||||
maxHour += timePlayed;
|
||||
Logging.LogGenericInfo("Still farming: " + string.Join(", ", appIDs), Bot.BotName);
|
||||
}
|
||||
|
||||
Bot.ResetGamesPlayed();
|
||||
Logging.LogGenericInfo("Stopped farming: " + string.Join(", ", appIDs), Bot.BotName);
|
||||
return success;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
|
||||
Copyright 2015 Łukasz "JustArchi" Domeradzki
|
||||
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
|
||||
Contact: JustArchi@JustArchi.net
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -22,20 +22,42 @@
|
||||
|
||||
*/
|
||||
|
||||
using System.Diagnostics;
|
||||
using SteamKit2;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal static class Debugging {
|
||||
internal static bool IsDebugBuild { get; private set; } = false;
|
||||
internal static bool IsReleaseBuild { get { return !IsDebugBuild; } }
|
||||
#if DEBUG
|
||||
internal static readonly bool IsDebugBuild = true;
|
||||
#else
|
||||
internal static readonly bool IsDebugBuild = false;
|
||||
#endif
|
||||
|
||||
static Debugging() {
|
||||
MarkIfDebug();
|
||||
}
|
||||
internal static bool IsReleaseBuild => !IsDebugBuild;
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
private static void MarkIfDebug() {
|
||||
IsDebugBuild = true;
|
||||
internal static bool NetHookAlreadyInitialized { get; set; } = false;
|
||||
|
||||
internal sealed class DebugListener : IDebugListener {
|
||||
private readonly string FilePath;
|
||||
|
||||
internal DebugListener(string filePath) {
|
||||
if (string.IsNullOrEmpty(filePath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
FilePath = filePath;
|
||||
}
|
||||
|
||||
public void WriteLine(string category, string msg) {
|
||||
lock (FilePath) {
|
||||
try {
|
||||
File.AppendAllText(FilePath, category + " | " + msg + Environment.NewLine);
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
174
ArchiSteamFarm/GlobalConfig.cs
Normal file
174
ArchiSteamFarm/GlobalConfig.cs
Normal file
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
_ _ _ ____ _ _____
|
||||
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
|
||||
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
|
||||
Contact: JustArchi@JustArchi.net
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal sealed class GlobalConfig {
|
||||
internal enum EUpdateChannel : byte {
|
||||
Unknown,
|
||||
Stable,
|
||||
Experimental
|
||||
}
|
||||
|
||||
internal const byte DefaultHttpTimeout = 60;
|
||||
|
||||
private const byte DefaultMaxFarmingTime = 10;
|
||||
private const byte DefaultFarmingDelay = 5;
|
||||
private const ushort DefaultWCFPort = 1242;
|
||||
private const ProtocolType DefaultSteamProtocol = ProtocolType.Tcp;
|
||||
|
||||
// This is hardcoded blacklist which should not be possible to change
|
||||
internal static readonly HashSet<uint> GlobalBlacklist = new HashSet<uint> { 267420, 303700, 335590, 368020, 425280 };
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal bool Debug { get; private set; } = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal bool Headless { get; private set; } = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal bool AutoUpdates { get; private set; } = true;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal bool AutoRestart { get; private set; } = true;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal EUpdateChannel UpdateChannel { get; private set; } = EUpdateChannel.Stable;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal ProtocolType SteamProtocol { get; private set; } = DefaultSteamProtocol;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal ulong SteamOwnerID { get; private set; } = 0;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal byte MaxFarmingTime { get; private set; } = DefaultMaxFarmingTime;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal byte IdleFarmingPeriod { get; private set; } = 3;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal byte FarmingDelay { get; private set; } = DefaultFarmingDelay;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal byte AccountPlayingDelay { get; private set; } = 5;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal byte LoginLimiterDelay { get; private set; } = 7;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal byte InventoryLimiterDelay { get; private set; } = 3;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal bool ForceHttp { get; private set; } = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal byte HttpTimeout { get; private set; } = DefaultHttpTimeout;
|
||||
|
||||
[JsonProperty]
|
||||
internal string WCFHostname { get; set; } = "localhost";
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal ushort WCFPort { get; private set; } = DefaultWCFPort;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal bool LogToFile { get; private set; } = true;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal bool Statistics { get; private set; } = true;
|
||||
|
||||
// TODO: Please remove me immediately after https://github.com/SteamRE/SteamKit/issues/254 gets fixed
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal bool HackIgnoreMachineID { get; private set; } = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal HashSet<uint> Blacklist { get; private set; } = new HashSet<uint>(GlobalBlacklist);
|
||||
|
||||
internal static GlobalConfig Load(string filePath) {
|
||||
if (string.IsNullOrEmpty(filePath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!File.Exists(filePath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
GlobalConfig globalConfig;
|
||||
try {
|
||||
globalConfig = JsonConvert.DeserializeObject<GlobalConfig>(File.ReadAllText(filePath));
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException(e);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (globalConfig == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// SK2 supports only TCP and UDP steam protocols
|
||||
// Ensure that user can't screw this up
|
||||
switch (globalConfig.SteamProtocol) {
|
||||
case ProtocolType.Tcp:
|
||||
case ProtocolType.Udp:
|
||||
break;
|
||||
default:
|
||||
Logging.LogGenericWarning("Configured SteamProtocol is invalid: " + globalConfig.SteamProtocol + ". Value of " + DefaultSteamProtocol + " will be used instead");
|
||||
globalConfig.SteamProtocol = DefaultSteamProtocol;
|
||||
break;
|
||||
}
|
||||
|
||||
// User might not know what he's doing
|
||||
// Ensure that he can't screw core ASF variables
|
||||
if (globalConfig.MaxFarmingTime == 0) {
|
||||
Logging.LogGenericWarning("Configured MaxFarmingTime is invalid: " + globalConfig.MaxFarmingTime + ". Value of " + DefaultMaxFarmingTime + " will be used instead");
|
||||
globalConfig.MaxFarmingTime = DefaultMaxFarmingTime;
|
||||
}
|
||||
|
||||
if (globalConfig.FarmingDelay == 0) {
|
||||
Logging.LogGenericWarning("Configured FarmingDelay is invalid: " + globalConfig.FarmingDelay + ". Value of " + DefaultFarmingDelay + " will be used instead");
|
||||
globalConfig.FarmingDelay = DefaultFarmingDelay;
|
||||
}
|
||||
|
||||
if (globalConfig.HttpTimeout == 0) {
|
||||
Logging.LogGenericWarning("Configured HttpTimeout is invalid: " + globalConfig.HttpTimeout + ". Value of " + DefaultHttpTimeout + " will be used instead");
|
||||
globalConfig.HttpTimeout = DefaultHttpTimeout;
|
||||
}
|
||||
|
||||
if (globalConfig.WCFPort == 0) {
|
||||
Logging.LogGenericWarning("Configured WCFPort is invalid: " + globalConfig.WCFPort + ". Value of " + DefaultWCFPort + " will be used instead");
|
||||
globalConfig.WCFPort = DefaultWCFPort;
|
||||
}
|
||||
|
||||
return globalConfig;
|
||||
}
|
||||
|
||||
// This constructor is used only by deserializer
|
||||
private GlobalConfig() { }
|
||||
}
|
||||
}
|
||||
98
ArchiSteamFarm/GlobalDatabase.cs
Normal file
98
ArchiSteamFarm/GlobalDatabase.cs
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
_ _ _ ____ _ _____
|
||||
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
|
||||
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
|
||||
Contact: JustArchi@JustArchi.net
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal sealed class GlobalDatabase {
|
||||
internal uint CellID {
|
||||
get {
|
||||
return _CellID;
|
||||
}
|
||||
set {
|
||||
if (_CellID == value) {
|
||||
return;
|
||||
}
|
||||
|
||||
_CellID = value;
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
private uint _CellID;
|
||||
|
||||
private string FilePath;
|
||||
|
||||
internal static GlobalDatabase Load(string filePath) {
|
||||
if (string.IsNullOrEmpty(filePath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!File.Exists(filePath)) {
|
||||
return new GlobalDatabase(filePath);
|
||||
}
|
||||
|
||||
GlobalDatabase globalDatabase;
|
||||
try {
|
||||
globalDatabase = JsonConvert.DeserializeObject<GlobalDatabase>(File.ReadAllText(filePath));
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException(e);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (globalDatabase == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
globalDatabase.FilePath = filePath;
|
||||
return globalDatabase;
|
||||
}
|
||||
|
||||
// This constructor is used when creating new database
|
||||
private GlobalDatabase(string filePath) {
|
||||
if (string.IsNullOrEmpty(filePath)) {
|
||||
throw new ArgumentNullException("filePath");
|
||||
}
|
||||
|
||||
FilePath = filePath;
|
||||
Save();
|
||||
}
|
||||
|
||||
// This constructor is used only by deserializer
|
||||
private GlobalDatabase() { }
|
||||
|
||||
private void Save() {
|
||||
lock (FilePath) {
|
||||
try {
|
||||
File.WriteAllText(FilePath, JsonConvert.SerializeObject(this));
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
46
ArchiSteamFarm/JSON/GitHub.cs
Normal file
46
ArchiSteamFarm/JSON/GitHub.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
_ _ _ ____ _ _____
|
||||
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
|
||||
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
|
||||
Contact: JustArchi@JustArchi.net
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal static class GitHub {
|
||||
internal sealed class ReleaseResponse {
|
||||
internal sealed class Asset {
|
||||
[JsonProperty(PropertyName = "name", Required = Required.Always)]
|
||||
internal string Name { get; private set; }
|
||||
|
||||
[JsonProperty(PropertyName = "browser_download_url", Required = Required.Always)]
|
||||
internal string DownloadURL { get; private set; }
|
||||
}
|
||||
|
||||
[JsonProperty(PropertyName = "tag_name", Required = Required.Always)]
|
||||
internal string Tag { get; private set; }
|
||||
|
||||
[JsonProperty(PropertyName = "assets", Required = Required.Always)]
|
||||
internal List<Asset> Assets { get; private set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
345
ArchiSteamFarm/JSON/Steam.cs
Normal file
345
ArchiSteamFarm/JSON/Steam.cs
Normal file
@@ -0,0 +1,345 @@
|
||||
/*
|
||||
_ _ _ ____ _ _____
|
||||
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
|
||||
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
|
||||
Contact: JustArchi@JustArchi.net
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using SteamKit2;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal static class Steam {
|
||||
internal sealed class Item { // REF: https://developer.valvesoftware.com/wiki/Steam_Web_API/IEconService#CEcon_Asset
|
||||
internal const ushort SteamAppID = 753;
|
||||
internal const byte SteamContextID = 6;
|
||||
|
||||
internal enum EType : byte {
|
||||
Unknown,
|
||||
|
||||
BoosterPack,
|
||||
Coupon,
|
||||
Gift,
|
||||
SteamGems,
|
||||
|
||||
Emoticon,
|
||||
FoilTradingCard,
|
||||
ProfileBackground,
|
||||
TradingCard
|
||||
}
|
||||
|
||||
internal uint AppID { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "appid", Required = Required.DisallowNull)]
|
||||
internal string AppIDString {
|
||||
get {
|
||||
return AppID.ToString();
|
||||
}
|
||||
set {
|
||||
if (string.IsNullOrEmpty(value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint result;
|
||||
if (!uint.TryParse(value, out result)) {
|
||||
return;
|
||||
}
|
||||
|
||||
AppID = result;
|
||||
}
|
||||
}
|
||||
|
||||
internal ulong ContextID { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "contextid", Required = Required.DisallowNull)]
|
||||
internal string ContextIDString {
|
||||
get {
|
||||
return ContextID.ToString();
|
||||
}
|
||||
set {
|
||||
if (string.IsNullOrEmpty(value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ulong result;
|
||||
if (!ulong.TryParse(value, out result)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ContextID = result;
|
||||
}
|
||||
}
|
||||
|
||||
internal ulong AssetID { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "assetid", Required = Required.DisallowNull)]
|
||||
internal string AssetIDString {
|
||||
get {
|
||||
return AssetID.ToString();
|
||||
}
|
||||
set {
|
||||
if (string.IsNullOrEmpty(value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ulong result;
|
||||
if (!ulong.TryParse(value, out result)) {
|
||||
return;
|
||||
}
|
||||
|
||||
AssetID = result;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonProperty(PropertyName = "id", Required = Required.DisallowNull)]
|
||||
internal string ID {
|
||||
get { return AssetIDString; }
|
||||
set { AssetIDString = value; }
|
||||
}
|
||||
|
||||
internal ulong ClassID { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "classid", Required = Required.DisallowNull)]
|
||||
internal string ClassIDString {
|
||||
get {
|
||||
return ClassID.ToString();
|
||||
}
|
||||
set {
|
||||
if (string.IsNullOrEmpty(value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ulong result;
|
||||
if (!ulong.TryParse(value, out result)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ClassID = result;
|
||||
}
|
||||
}
|
||||
|
||||
internal ulong InstanceID { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "instanceid", Required = Required.DisallowNull)]
|
||||
internal string InstanceIDString {
|
||||
get {
|
||||
return InstanceID.ToString();
|
||||
}
|
||||
set {
|
||||
if (string.IsNullOrEmpty(value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ulong result;
|
||||
if (!ulong.TryParse(value, out result)) {
|
||||
return;
|
||||
}
|
||||
|
||||
InstanceID = result;
|
||||
}
|
||||
}
|
||||
|
||||
internal uint Amount { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "amount", Required = Required.Always)]
|
||||
internal string AmountString {
|
||||
get {
|
||||
return Amount.ToString();
|
||||
}
|
||||
set {
|
||||
if (string.IsNullOrEmpty(value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint result;
|
||||
if (!uint.TryParse(value, out result)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Amount = result;
|
||||
}
|
||||
}
|
||||
|
||||
internal uint RealAppID { get; set; }
|
||||
internal EType Type { get; set; }
|
||||
}
|
||||
|
||||
internal sealed class TradeOffer { // REF: https://developer.valvesoftware.com/wiki/Steam_Web_API/IEconService#CEcon_TradeOffer
|
||||
internal enum ETradeOfferState : byte {
|
||||
Unknown,
|
||||
Invalid,
|
||||
Active,
|
||||
Accepted,
|
||||
Countered,
|
||||
Expired,
|
||||
Canceled,
|
||||
Declined,
|
||||
InvalidItems,
|
||||
EmailPending,
|
||||
EmailCanceled,
|
||||
OnHold
|
||||
}
|
||||
|
||||
internal ulong TradeOfferID { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "tradeofferid", Required = Required.Always)]
|
||||
internal string TradeOfferIDString {
|
||||
get {
|
||||
return TradeOfferID.ToString();
|
||||
}
|
||||
set {
|
||||
if (string.IsNullOrEmpty(value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ulong result;
|
||||
if (!ulong.TryParse(value, out result)) {
|
||||
return;
|
||||
}
|
||||
|
||||
TradeOfferID = result;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonProperty(PropertyName = "accountid_other", Required = Required.Always)]
|
||||
internal uint OtherSteamID3 { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "trade_offer_state", Required = Required.Always)]
|
||||
internal ETradeOfferState State { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "items_to_give", Required = Required.Always)]
|
||||
internal HashSet<Item> ItemsToGive { get; } = new HashSet<Item>();
|
||||
|
||||
[JsonProperty(PropertyName = "items_to_receive", Required = Required.Always)]
|
||||
internal HashSet<Item> ItemsToReceive { get; } = new HashSet<Item>();
|
||||
|
||||
// Extra
|
||||
internal ulong OtherSteamID64 {
|
||||
get {
|
||||
if (OtherSteamID3 == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return new SteamID(OtherSteamID3, EUniverse.Public, EAccountType.Individual);
|
||||
}
|
||||
set {
|
||||
if (value == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
OtherSteamID3 = new SteamID(value).AccountID;
|
||||
}
|
||||
}
|
||||
|
||||
internal bool IsSteamCardsOnlyTrade() {
|
||||
foreach (Item item in ItemsToGive) {
|
||||
if (item.AppID != Item.SteamAppID || item.ContextID != Item.SteamContextID || (item.Type != Item.EType.FoilTradingCard && item.Type != Item.EType.TradingCard)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Item item in ItemsToReceive) {
|
||||
if (item.AppID != Item.SteamAppID || item.ContextID != Item.SteamContextID || (item.Type != Item.EType.FoilTradingCard && item.Type != Item.EType.TradingCard)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool IsPotentiallyDupesTrade() {
|
||||
Dictionary<uint, Dictionary<Item.EType, uint>> ItemsToGivePerGame = new Dictionary<uint, Dictionary<Item.EType, uint>>();
|
||||
foreach (Item item in ItemsToGive) {
|
||||
Dictionary<Item.EType, uint> ItemsPerType;
|
||||
if (!ItemsToGivePerGame.TryGetValue(item.RealAppID, out ItemsPerType)) {
|
||||
ItemsPerType = new Dictionary<Item.EType, uint>();
|
||||
ItemsPerType[item.Type] = item.Amount;
|
||||
ItemsToGivePerGame[item.RealAppID] = ItemsPerType;
|
||||
} else {
|
||||
uint amount;
|
||||
if (ItemsPerType.TryGetValue(item.Type, out amount)) {
|
||||
ItemsPerType[item.Type] = amount + item.Amount;
|
||||
} else {
|
||||
ItemsPerType[item.Type] = item.Amount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Dictionary<uint, Dictionary<Item.EType, uint>> ItemsToReceivePerGame = new Dictionary<uint, Dictionary<Item.EType, uint>>();
|
||||
foreach (Item item in ItemsToReceive) {
|
||||
Dictionary<Item.EType, uint> ItemsPerType;
|
||||
if (!ItemsToReceivePerGame.TryGetValue(item.RealAppID, out ItemsPerType)) {
|
||||
ItemsPerType = new Dictionary<Item.EType, uint>();
|
||||
ItemsPerType[item.Type] = item.Amount;
|
||||
ItemsToReceivePerGame[item.RealAppID] = ItemsPerType;
|
||||
} else {
|
||||
uint amount;
|
||||
if (ItemsPerType.TryGetValue(item.Type, out amount)) {
|
||||
ItemsPerType[item.Type] = amount + item.Amount;
|
||||
} else {
|
||||
ItemsPerType[item.Type] = item.Amount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that amount per type and per game matches
|
||||
foreach (KeyValuePair<uint, Dictionary<Item.EType, uint>> ItemsPerGame in ItemsToGivePerGame) {
|
||||
Dictionary<Item.EType, uint> otherItemsPerType;
|
||||
if (!ItemsToReceivePerGame.TryGetValue(ItemsPerGame.Key, out otherItemsPerType)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<Item.EType, uint> ItemsPerType in ItemsPerGame.Value) {
|
||||
uint otherAmount;
|
||||
if (!otherItemsPerType.TryGetValue(ItemsPerType.Key, out otherAmount)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ItemsPerType.Value != otherAmount) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class TradeOfferRequest {
|
||||
internal sealed class ItemList {
|
||||
[JsonProperty(PropertyName = "assets", Required = Required.Always)]
|
||||
internal HashSet<Item> Assets { get; } = new HashSet<Item>();
|
||||
}
|
||||
|
||||
[JsonProperty(PropertyName = "newversion", Required = Required.Always)]
|
||||
internal bool NewVersion { get; } = true;
|
||||
|
||||
[JsonProperty(PropertyName = "version", Required = Required.Always)]
|
||||
internal byte Version { get; } = 2;
|
||||
|
||||
[JsonProperty(PropertyName = "me", Required = Required.Always)]
|
||||
internal ItemList ItemsToGive { get; } = new ItemList();
|
||||
|
||||
[JsonProperty(PropertyName = "them", Required = Required.Always)]
|
||||
internal ItemList ItemsToReceive { get; } = new ItemList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
|
||||
Copyright 2015 Łukasz "JustArchi" Domeradzki
|
||||
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
|
||||
Contact: JustArchi@JustArchi.net
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -24,44 +24,115 @@
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal static class Logging {
|
||||
private static void Log(string message) {
|
||||
lock (Program.ConsoleLock) {
|
||||
Console.WriteLine(DateTime.Now + " " + message);
|
||||
private static readonly object FileLock = new object();
|
||||
|
||||
private static bool LogToFile;
|
||||
|
||||
internal static void Init() {
|
||||
LogToFile = Program.GlobalConfig.LogToFile;
|
||||
|
||||
if (LogToFile) {
|
||||
lock (FileLock) {
|
||||
try {
|
||||
File.Delete(Program.LogFile);
|
||||
} catch (Exception e) {
|
||||
LogGenericException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static void LogGenericError(string botName, string message, [CallerMemberName] string previousMethodName = "") {
|
||||
internal static void LogGenericWTF(string message, string botName = "Main", [CallerMemberName] string previousMethodName = "") {
|
||||
if (string.IsNullOrEmpty(message)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Log("[!!] WTF: " + previousMethodName + "() <" + botName + "> " + message + ", WTF?");
|
||||
}
|
||||
|
||||
internal static void LogGenericError(string message, string botName = "Main", [CallerMemberName] string previousMethodName = "") {
|
||||
if (string.IsNullOrEmpty(message)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Log("[!!] ERROR: " + previousMethodName + "() <" + botName + "> " + message);
|
||||
}
|
||||
|
||||
internal static void LogGenericException(string botName, Exception exception, [CallerMemberName] string previousMethodName = "") {
|
||||
internal static void LogGenericException(Exception exception, string botName = "Main", [CallerMemberName] string previousMethodName = "") {
|
||||
if (exception == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Log("[!] EXCEPTION: " + previousMethodName + "() <" + botName + "> " + exception.Message);
|
||||
Log("[!] StackTrace:" + Environment.NewLine + exception.StackTrace);
|
||||
|
||||
if (exception.InnerException != null) {
|
||||
LogGenericException(exception.InnerException, botName, previousMethodName);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void LogGenericWarning(string botName, string message, [CallerMemberName] string previousMethodName = "") {
|
||||
internal static void LogGenericWarning(string message, string botName = "Main", [CallerMemberName] string previousMethodName = "") {
|
||||
if (string.IsNullOrEmpty(message)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Log("[!] WARNING: " + previousMethodName + "() <" + botName + "> " + message);
|
||||
}
|
||||
|
||||
internal static void LogGenericInfo(string botName, string message, [CallerMemberName] string previousMethodName = "") {
|
||||
internal static void LogGenericInfo(string message, string botName = "Main", [CallerMemberName] string previousMethodName = "") {
|
||||
if (string.IsNullOrEmpty(message)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Log("[*] INFO: " + previousMethodName + "() <" + botName + "> " + message);
|
||||
}
|
||||
|
||||
internal static void LogGenericNotice(string botName, string message, [CallerMemberName] string previousMethodName = "") {
|
||||
Log("[*] NOTICE: " + previousMethodName + "() <" + botName + "> " + message);
|
||||
internal static void LogNullError(string nullObjectName, string botName = "Main", [CallerMemberName] string previousMethodName = "") {
|
||||
if (string.IsNullOrEmpty(nullObjectName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
LogGenericError(nullObjectName + " is null!", botName, previousMethodName);
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
internal static void LogGenericDebug(string botName, string message, [CallerMemberName] string previousMethodName = "") {
|
||||
internal static void LogGenericDebug(string message, string botName = "Main", [CallerMemberName] string previousMethodName = "") {
|
||||
if (string.IsNullOrEmpty(message)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Log("[#] DEBUG: " + previousMethodName + "() <" + botName + "> " + message);
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
internal static void LogGenericDebug(string message, [CallerMemberName] string previousMethodName = "") {
|
||||
LogGenericDebug("DEBUG", message, previousMethodName);
|
||||
private static void Log(string message) {
|
||||
if (string.IsNullOrEmpty(message)) {
|
||||
return;
|
||||
}
|
||||
|
||||
string loggedMessage = DateTime.Now + " " + message + Environment.NewLine;
|
||||
|
||||
// Write on console only when not awaiting response from user
|
||||
if (!Program.ConsoleIsBusy) {
|
||||
try {
|
||||
Console.Write(loggedMessage);
|
||||
} catch { }
|
||||
}
|
||||
|
||||
if (LogToFile) {
|
||||
lock (FileLock) {
|
||||
try {
|
||||
File.AppendAllText(Program.LogFile, loggedMessage);
|
||||
} catch (Exception e) {
|
||||
LogToFile = false;
|
||||
LogGenericException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
|
||||
Copyright 2015 Łukasz "JustArchi" Domeradzki
|
||||
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
|
||||
Contact: JustArchi@JustArchi.net
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -22,148 +22,503 @@
|
||||
|
||||
*/
|
||||
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal static class Program {
|
||||
internal enum EUserInputType {
|
||||
internal enum EUserInputType : byte {
|
||||
Unknown,
|
||||
DeviceID,
|
||||
Login,
|
||||
Password,
|
||||
PhoneNumber,
|
||||
SMS,
|
||||
SteamGuard,
|
||||
SteamParentalPIN,
|
||||
RevocationCode,
|
||||
TwoFactorAuthentication,
|
||||
WCFHostname
|
||||
}
|
||||
|
||||
internal const ulong ArchiSCFarmGroup = 103582791440160998;
|
||||
internal const string ConfigDirectoryPath = "config";
|
||||
private const string LatestGithubReleaseURL = "https://api.github.com/repos/JustArchi/ArchiSteamFarm/releases/latest";
|
||||
private enum EMode : byte {
|
||||
Unknown,
|
||||
Normal, // Standard most common usage
|
||||
Client, // WCF client only
|
||||
Server // Normal + WCF server
|
||||
}
|
||||
|
||||
internal static readonly object ConsoleLock = new object();
|
||||
private static readonly SemaphoreSlim SteamSemaphore = new SemaphoreSlim(1);
|
||||
private static readonly ManualResetEvent ShutdownResetEvent = new ManualResetEvent(false);
|
||||
private static readonly Assembly Assembly = Assembly.GetExecutingAssembly();
|
||||
private static readonly string ExecutablePath = Assembly.Location;
|
||||
private static readonly AssemblyName AssemblyName = Assembly.GetName();
|
||||
private static readonly string ExeName = AssemblyName.Name + ".exe";
|
||||
private static readonly string Version = AssemblyName.Version.ToString();
|
||||
internal const string ASF = "ASF";
|
||||
internal const string ConfigDirectory = "config";
|
||||
internal const string DebugDirectory = "debug";
|
||||
internal const string LogFile = "log.txt";
|
||||
internal const string GithubRepo = "JustArchi/ArchiSteamFarm";
|
||||
internal const string GlobalConfigFile = ASF + ".json";
|
||||
internal const string GlobalDatabaseFile = ASF + ".db";
|
||||
|
||||
private static async Task CheckForUpdate() {
|
||||
JObject response = await Utilities.UrlToJObject(LatestGithubReleaseURL).ConfigureAwait(false);
|
||||
if (response == null) {
|
||||
private const string GithubReleaseURL = "https://api.github.com/repos/" + GithubRepo + "/releases"; // GitHub API is HTTPS only
|
||||
|
||||
internal static readonly Version Version = Assembly.GetEntryAssembly().GetName().Version;
|
||||
|
||||
private static readonly object ConsoleLock = new object();
|
||||
private static readonly ManualResetEventSlim ShutdownResetEvent = new ManualResetEventSlim(false);
|
||||
private static readonly string ExecutableFile = Assembly.GetEntryAssembly().Location;
|
||||
private static readonly string ExecutableName = Path.GetFileName(ExecutableFile);
|
||||
private static readonly string ExecutableDirectory = Path.GetDirectoryName(ExecutableFile);
|
||||
private static readonly WCF WCF = new WCF();
|
||||
|
||||
internal static GlobalConfig GlobalConfig { get; private set; }
|
||||
internal static GlobalDatabase GlobalDatabase { get; private set; }
|
||||
internal static bool ConsoleIsBusy { get; private set; } = false;
|
||||
|
||||
private static Timer AutoUpdatesTimer;
|
||||
private static EMode Mode = EMode.Normal;
|
||||
private static WebBrowser WebBrowser;
|
||||
|
||||
internal static async Task CheckForUpdate() {
|
||||
string oldExeFile = ExecutableFile + ".old";
|
||||
|
||||
// We booted successfully so we can now remove old exe file
|
||||
if (File.Exists(oldExeFile)) {
|
||||
// It's entirely possible that old process is still running, allow at least a second before trying to remove the file
|
||||
await Utilities.SleepAsync(1000).ConfigureAwait(false);
|
||||
|
||||
try {
|
||||
File.Delete(oldExeFile);
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException(e);
|
||||
Logging.LogGenericError("Could not remove old ASF binary, please remove " + oldExeFile + " manually in order for update function to work!");
|
||||
}
|
||||
}
|
||||
|
||||
if (GlobalConfig.UpdateChannel == GlobalConfig.EUpdateChannel.Unknown) {
|
||||
return;
|
||||
}
|
||||
|
||||
string remoteVersion = response["tag_name"].ToString();
|
||||
if (string.IsNullOrEmpty(remoteVersion)) {
|
||||
string releaseURL = GithubReleaseURL;
|
||||
if (GlobalConfig.UpdateChannel == GlobalConfig.EUpdateChannel.Stable) {
|
||||
releaseURL += "/latest";
|
||||
}
|
||||
|
||||
string response = null;
|
||||
Logging.LogGenericInfo("Checking new version...");
|
||||
for (byte i = 0; i < WebBrowser.MaxRetries && string.IsNullOrEmpty(response); i++) {
|
||||
response = await WebBrowser.UrlGetToContent(releaseURL).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(response)) {
|
||||
Logging.LogGenericWarning("Could not check latest version!");
|
||||
return;
|
||||
}
|
||||
|
||||
string localVersion = Version;
|
||||
GitHub.ReleaseResponse releaseResponse;
|
||||
if (GlobalConfig.UpdateChannel == GlobalConfig.EUpdateChannel.Stable) {
|
||||
try {
|
||||
releaseResponse = JsonConvert.DeserializeObject<GitHub.ReleaseResponse>(response);
|
||||
} catch (JsonException e) {
|
||||
Logging.LogGenericException(e);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
List<GitHub.ReleaseResponse> releases;
|
||||
try {
|
||||
releases = JsonConvert.DeserializeObject<List<GitHub.ReleaseResponse>>(response);
|
||||
} catch (JsonException e) {
|
||||
Logging.LogGenericException(e);
|
||||
return;
|
||||
}
|
||||
|
||||
Logging.LogGenericNotice("", "Local version: " + localVersion);
|
||||
Logging.LogGenericNotice("", "Remote version: " + remoteVersion);
|
||||
if (releases == null || releases.Count == 0) {
|
||||
Logging.LogGenericWarning("Could not check latest version!");
|
||||
return;
|
||||
}
|
||||
|
||||
int comparisonResult = localVersion.CompareTo(remoteVersion);
|
||||
if (localVersion.CompareTo(remoteVersion) < 0) {
|
||||
Logging.LogGenericNotice("", "New version is available!");
|
||||
Logging.LogGenericNotice("", "Consider updating yourself!");
|
||||
releaseResponse = releases[0];
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(releaseResponse.Tag)) {
|
||||
Logging.LogGenericWarning("Could not check latest version!");
|
||||
return;
|
||||
}
|
||||
|
||||
Version newVersion = new Version(releaseResponse.Tag);
|
||||
|
||||
Logging.LogGenericInfo("Local version: " + Version + " | Remote version: " + newVersion);
|
||||
|
||||
if (Version.CompareTo(newVersion) >= 0) { // If local version is the same or newer than remote version
|
||||
if (AutoUpdatesTimer == null && GlobalConfig.AutoUpdates) {
|
||||
Logging.LogGenericInfo("ASF will automatically check for new versions every 24 hours");
|
||||
AutoUpdatesTimer = new Timer(
|
||||
async e => await CheckForUpdate().ConfigureAwait(false),
|
||||
null,
|
||||
TimeSpan.FromDays(1), // Delay
|
||||
TimeSpan.FromDays(1) // Period
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!GlobalConfig.AutoUpdates) {
|
||||
Logging.LogGenericInfo("New version is available!");
|
||||
Logging.LogGenericInfo("Consider updating yourself!");
|
||||
await Utilities.SleepAsync(5000).ConfigureAwait(false);
|
||||
} else if (localVersion.CompareTo(remoteVersion) > 0) {
|
||||
Logging.LogGenericNotice("", "You're currently using pre-release version!");
|
||||
Logging.LogGenericNotice("", "Be careful!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (File.Exists(oldExeFile)) {
|
||||
Logging.LogGenericWarning("Refusing to proceed with auto update as old " + oldExeFile + " binary could not be removed, please remove it manually");
|
||||
return;
|
||||
}
|
||||
|
||||
// Auto update logic starts here
|
||||
if (releaseResponse.Assets == null) {
|
||||
Logging.LogGenericWarning("Could not proceed with update because that version doesn't include assets!");
|
||||
return;
|
||||
}
|
||||
|
||||
GitHub.ReleaseResponse.Asset binaryAsset = null;
|
||||
foreach (var asset in releaseResponse.Assets) {
|
||||
if (string.IsNullOrEmpty(asset.Name) || !asset.Name.Equals(ExecutableName, StringComparison.OrdinalIgnoreCase)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
binaryAsset = asset;
|
||||
break;
|
||||
}
|
||||
|
||||
if (binaryAsset == null) {
|
||||
Logging.LogGenericWarning("Could not proceed with update because there is no asset that relates to currently running binary!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(binaryAsset.DownloadURL)) {
|
||||
Logging.LogGenericWarning("Could not proceed with update because download URL is empty!");
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] result = null;
|
||||
for (byte i = 0; i < WebBrowser.MaxRetries && result == null; i++) {
|
||||
Logging.LogGenericInfo("Downloading new version...");
|
||||
result = await WebBrowser.UrlGetToBytes(binaryAsset.DownloadURL).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (result == null) {
|
||||
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries");
|
||||
return;
|
||||
}
|
||||
|
||||
string newExeFile = ExecutableFile + ".new";
|
||||
|
||||
// Firstly we create new exec
|
||||
try {
|
||||
File.WriteAllBytes(newExeFile, result);
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException(e);
|
||||
return;
|
||||
}
|
||||
|
||||
// Now we move current -> old
|
||||
try {
|
||||
File.Move(ExecutableFile, oldExeFile);
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException(e);
|
||||
try {
|
||||
// Cleanup
|
||||
File.Delete(newExeFile);
|
||||
} catch { }
|
||||
return;
|
||||
}
|
||||
|
||||
// Now we move new -> current
|
||||
try {
|
||||
File.Move(newExeFile, ExecutableFile);
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException(e);
|
||||
try {
|
||||
// Cleanup
|
||||
File.Move(oldExeFile, ExecutableFile);
|
||||
File.Delete(newExeFile);
|
||||
} catch { }
|
||||
return;
|
||||
}
|
||||
|
||||
Logging.LogGenericInfo("Update process finished!");
|
||||
|
||||
if (GlobalConfig.AutoRestart) {
|
||||
Logging.LogGenericInfo("Restarting...");
|
||||
await Utilities.SleepAsync(5000).ConfigureAwait(false);
|
||||
Restart();
|
||||
} else {
|
||||
Logging.LogGenericInfo("Exiting...");
|
||||
await Utilities.SleepAsync(5000).ConfigureAwait(false);
|
||||
Exit();
|
||||
}
|
||||
}
|
||||
|
||||
internal static async Task Exit(int exitCode = 0) {
|
||||
await Bot.ShutdownAllBots().ConfigureAwait(false);
|
||||
internal static void Exit(int exitCode = 0) {
|
||||
WCF.StopServer();
|
||||
Environment.Exit(exitCode);
|
||||
}
|
||||
|
||||
internal static async Task Restart() {
|
||||
await Bot.ShutdownAllBots().ConfigureAwait(false);
|
||||
System.Diagnostics.Process.Start(ExecutablePath);
|
||||
Environment.Exit(0);
|
||||
internal static void Restart() {
|
||||
try {
|
||||
Process.Start(ExecutableFile, string.Join(" ", Environment.GetCommandLineArgs().Skip(1)));
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException(e);
|
||||
}
|
||||
|
||||
Exit();
|
||||
}
|
||||
|
||||
internal static async Task LimitSteamRequestsAsync() {
|
||||
await SteamSemaphore.WaitAsync().ConfigureAwait(false);
|
||||
await Utilities.SleepAsync(5 * 1000).ConfigureAwait(false); // We must add some delay to not get caught by Steam anty-DoS
|
||||
SteamSemaphore.Release();
|
||||
}
|
||||
internal static string GetUserInput(EUserInputType userInputType, string botName = null, string extraInformation = null) {
|
||||
if (userInputType == EUserInputType.Unknown) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (GlobalConfig.Headless) {
|
||||
Logging.LogGenericWarning("Received a request for user input, but process is running in headless mode!");
|
||||
return null;
|
||||
}
|
||||
|
||||
internal static string GetUserInput(string botLogin, EUserInputType userInputType) {
|
||||
string result;
|
||||
lock (ConsoleLock) {
|
||||
ConsoleIsBusy = true;
|
||||
switch (userInputType) {
|
||||
case EUserInputType.DeviceID:
|
||||
Console.Write((string.IsNullOrEmpty(botName) ? "" : "<" + botName + "> ") + "Please enter your Device ID (including \"android:\"): ");
|
||||
break;
|
||||
case EUserInputType.Login:
|
||||
Console.Write("<" + botLogin + "> Please enter your login: ");
|
||||
Console.Write((string.IsNullOrEmpty(botName) ? "" : "<" + botName + "> ") + "Please enter your login: ");
|
||||
break;
|
||||
case EUserInputType.Password:
|
||||
Console.Write("<" + botLogin + "> Please enter your password: ");
|
||||
Console.Write((string.IsNullOrEmpty(botName) ? "" : "<" + botName + "> ") + "Please enter your password: ");
|
||||
break;
|
||||
case EUserInputType.PhoneNumber:
|
||||
Console.Write((string.IsNullOrEmpty(botName) ? "" : "<" + botName + "> ") + "Please enter your full phone number (e.g. +1234567890): ");
|
||||
break;
|
||||
case EUserInputType.SMS:
|
||||
Console.Write((string.IsNullOrEmpty(botName) ? "" : "<" + botName + "> ") + "Please enter SMS code sent on your mobile: ");
|
||||
break;
|
||||
case EUserInputType.SteamGuard:
|
||||
Console.Write("<" + botLogin + "> Please enter the auth code sent to your email: ");
|
||||
Console.Write((string.IsNullOrEmpty(botName) ? "" : "<" + botName + "> ") + "Please enter the auth code sent to your email: ");
|
||||
break;
|
||||
case EUserInputType.SteamParentalPIN:
|
||||
Console.Write("<" + botLogin + "> Please enter steam parental PIN: ");
|
||||
Console.Write((string.IsNullOrEmpty(botName) ? "" : "<" + botName + "> ") + "Please enter steam parental PIN: ");
|
||||
break;
|
||||
case EUserInputType.RevocationCode:
|
||||
Console.WriteLine((string.IsNullOrEmpty(botName) ? "" : "<" + botName + "> ") + "PLEASE WRITE DOWN YOUR REVOCATION CODE: " + extraInformation);
|
||||
Console.Write("Hit enter once ready...");
|
||||
break;
|
||||
case EUserInputType.TwoFactorAuthentication:
|
||||
Console.Write("<" + botLogin + "> Please enter your 2 factor auth code from your authenticator app: ");
|
||||
Console.Write((string.IsNullOrEmpty(botName) ? "" : "<" + botName + "> ") + "Please enter your 2 factor auth code from your authenticator app: ");
|
||||
break;
|
||||
case EUserInputType.WCFHostname:
|
||||
Console.Write((string.IsNullOrEmpty(botName) ? "" : "<" + botName + "> ") + "Please enter your WCF hostname: ");
|
||||
break;
|
||||
default:
|
||||
Console.Write((string.IsNullOrEmpty(botName) ? "" : "<" + botName + "> ") + "Please enter not documented yet value of \"" + userInputType + "\": ");
|
||||
break;
|
||||
}
|
||||
result = Console.ReadLine();
|
||||
Console.Clear(); // For security purposes
|
||||
if (!Console.IsOutputRedirected) {
|
||||
Console.Clear(); // For security purposes
|
||||
}
|
||||
ConsoleIsBusy = false;
|
||||
}
|
||||
result = result.Trim(); // Get rid of all whitespace characters
|
||||
return result;
|
||||
|
||||
return string.IsNullOrEmpty(result) ? null : result.Trim();
|
||||
}
|
||||
|
||||
internal static async void OnBotShutdown(Bot bot) {
|
||||
if (Bot.GetRunningBotsCount() == 0) {
|
||||
Logging.LogGenericInfo("Main", "No bots are running, exiting");
|
||||
await Utilities.SleepAsync(5000).ConfigureAwait(false); // This might be the only message user gets, consider giving him some time
|
||||
ShutdownResetEvent.Set();
|
||||
}
|
||||
}
|
||||
|
||||
private static void Main(string[] args) {
|
||||
Logging.LogGenericInfo("Main", "Archi's Steam Farm, version " + Version);
|
||||
|
||||
Task.Run(async () => await CheckForUpdate().ConfigureAwait(false)).Wait();
|
||||
|
||||
// Allow loading configs from source tree if it's a debug build
|
||||
if (Debugging.IsDebugBuild) {
|
||||
for (var i = 0; i < 4; i++) {
|
||||
Directory.SetCurrentDirectory("..");
|
||||
if (Directory.Exists(ConfigDirectoryPath)) {
|
||||
break;
|
||||
}
|
||||
internal static void OnBotShutdown() {
|
||||
foreach (Bot bot in Bot.Bots.Values) {
|
||||
if (bot.KeepRunning) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Directory.Exists(ConfigDirectoryPath)) {
|
||||
Logging.LogGenericError("Main", "Config directory doesn't exist!");
|
||||
Console.ReadLine();
|
||||
Task.Run(async () => await Exit(1).ConfigureAwait(false)).Wait();
|
||||
if (WCF.IsServerRunning()) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var configFile in Directory.EnumerateFiles(ConfigDirectoryPath, "*.xml")) {
|
||||
Logging.LogGenericInfo("No bots are running, exiting");
|
||||
Thread.Sleep(5000);
|
||||
ShutdownResetEvent.Set();
|
||||
}
|
||||
|
||||
private static void InitServices() {
|
||||
GlobalConfig = GlobalConfig.Load(Path.Combine(ConfigDirectory, GlobalConfigFile));
|
||||
if (GlobalConfig == null) {
|
||||
Logging.LogGenericError("Global config could not be loaded, please make sure that ASF.json exists and is valid!");
|
||||
Thread.Sleep(5000);
|
||||
Exit(1);
|
||||
}
|
||||
|
||||
GlobalDatabase = GlobalDatabase.Load(Path.Combine(ConfigDirectory, GlobalDatabaseFile));
|
||||
if (GlobalDatabase == null) {
|
||||
Logging.LogGenericError("Global database could not be loaded!");
|
||||
Thread.Sleep(5000);
|
||||
Exit(1);
|
||||
}
|
||||
|
||||
ArchiWebHandler.Init();
|
||||
WebBrowser.Init();
|
||||
WCF.Init();
|
||||
|
||||
WebBrowser = new WebBrowser("Main");
|
||||
}
|
||||
|
||||
private static void ParseArgs(string[] args) {
|
||||
foreach (string arg in args) {
|
||||
switch (arg) {
|
||||
case "--client":
|
||||
Mode = EMode.Client;
|
||||
break;
|
||||
case "--server":
|
||||
Mode = EMode.Server;
|
||||
WCF.StartServer();
|
||||
break;
|
||||
default:
|
||||
if (arg.StartsWith("--")) {
|
||||
Logging.LogGenericWarning("Unrecognized parameter: " + arg);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Mode != EMode.Client) {
|
||||
Logging.LogGenericWarning("Ignoring command because --client wasn't specified: " + arg);
|
||||
continue;
|
||||
}
|
||||
|
||||
Logging.LogGenericInfo("Command sent: " + arg);
|
||||
|
||||
// We intentionally execute this async block synchronously
|
||||
Logging.LogGenericInfo("Response received: " + WCF.SendCommand(arg));
|
||||
/*
|
||||
Task.Run(async () => {
|
||||
Logging.LogGenericNotice("WCF", "Response received: " + await WCF.SendCommand(arg).ConfigureAwait(false));
|
||||
}).Wait();
|
||||
*/
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs args) {
|
||||
if (sender == null || args == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Logging.LogGenericException((Exception) args.ExceptionObject);
|
||||
}
|
||||
|
||||
private static void UnobservedTaskExceptionHandler(object sender, UnobservedTaskExceptionEventArgs args) {
|
||||
if (sender == null || args == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Logging.LogGenericException(args.Exception);
|
||||
}
|
||||
|
||||
private static void Init(string[] args) {
|
||||
AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionHandler;
|
||||
TaskScheduler.UnobservedTaskException += UnobservedTaskExceptionHandler;
|
||||
|
||||
Logging.LogGenericInfo("Archi's Steam Farm, version " + Version);
|
||||
Directory.SetCurrentDirectory(ExecutableDirectory);
|
||||
InitServices();
|
||||
|
||||
// Allow loading configs from source tree if it's a debug build
|
||||
if (Debugging.IsDebugBuild) {
|
||||
|
||||
// Common structure is bin/(x64/)Debug/ArchiSteamFarm.exe, so we allow up to 4 directories up
|
||||
for (var i = 0; i < 4; i++) {
|
||||
Directory.SetCurrentDirectory("..");
|
||||
if (Directory.Exists(ConfigDirectory)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If config directory doesn't exist after our adjustment, abort all of that
|
||||
if (!Directory.Exists(ConfigDirectory)) {
|
||||
Directory.SetCurrentDirectory(ExecutableDirectory);
|
||||
}
|
||||
}
|
||||
|
||||
// If debugging is on, we prepare debug directory prior to running
|
||||
if (GlobalConfig.Debug) {
|
||||
if (Directory.Exists(DebugDirectory)) {
|
||||
Directory.Delete(DebugDirectory, true);
|
||||
Thread.Sleep(1000); // Dirty workaround giving Windows some time to sync
|
||||
}
|
||||
Directory.CreateDirectory(DebugDirectory);
|
||||
|
||||
SteamKit2.DebugLog.AddListener(new Debugging.DebugListener(Path.Combine(Program.DebugDirectory, "debug.txt")));
|
||||
SteamKit2.DebugLog.Enabled = true;
|
||||
}
|
||||
|
||||
// Parse args
|
||||
ParseArgs(args);
|
||||
|
||||
// If we ran ASF as a client, we're done by now
|
||||
if (Mode == EMode.Client) {
|
||||
Exit();
|
||||
}
|
||||
|
||||
// From now on it's server mode
|
||||
Logging.Init();
|
||||
|
||||
if (!Directory.Exists(ConfigDirectory)) {
|
||||
Logging.LogGenericError("Config directory doesn't exist!");
|
||||
Thread.Sleep(5000);
|
||||
Exit(1);
|
||||
}
|
||||
|
||||
CheckForUpdate().Wait();
|
||||
|
||||
// Before attempting to connect, initialize our list of CMs
|
||||
Bot.RefreshCMs(GlobalDatabase.CellID).Wait();
|
||||
|
||||
bool isRunning = false;
|
||||
|
||||
foreach (var configFile in Directory.EnumerateFiles(ConfigDirectory, "*.json")) {
|
||||
string botName = Path.GetFileNameWithoutExtension(configFile);
|
||||
switch (botName) {
|
||||
case ASF:
|
||||
case "example":
|
||||
case "minimal":
|
||||
continue;
|
||||
}
|
||||
|
||||
Bot bot = new Bot(botName);
|
||||
if (!bot.Enabled) {
|
||||
Logging.LogGenericInfo(botName, "Not starting this instance because it's disabled in config file");
|
||||
if (bot.BotConfig != null && bot.BotConfig.Enabled) {
|
||||
if (bot.BotConfig.StartOnLaunch) {
|
||||
isRunning = true;
|
||||
}
|
||||
} else {
|
||||
Logging.LogGenericInfo("Not starting this instance because it's disabled in config file", botName);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we got any bots running
|
||||
OnBotShutdown(null);
|
||||
if (!isRunning) {
|
||||
OnBotShutdown();
|
||||
}
|
||||
}
|
||||
|
||||
ShutdownResetEvent.WaitOne();
|
||||
private static void Main(string[] args) {
|
||||
Init(args);
|
||||
|
||||
// Wait for signal to shutdown
|
||||
ShutdownResetEvent.Wait();
|
||||
|
||||
// We got a signal to shutdown
|
||||
Exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ using System.Runtime.InteropServices;
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("ArchiSteamFarm")]
|
||||
[assembly: AssemblyCopyright("Copyright © Łukasz Domeradzki 2015")]
|
||||
[assembly: AssemblyCopyright("Copyright © ArchiSteamFarm 2015-2016")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("0.6.0.0")]
|
||||
[assembly: AssemblyFileVersion("0.6.0.0")]
|
||||
[assembly: AssemblyVersion("2.0.4.2")]
|
||||
[assembly: AssemblyFileVersion("2.0.4.2")]
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
_ _ _ ____ _ _____
|
||||
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
|
||||
Copyright 2015 Łukasz "JustArchi" Domeradzki
|
||||
Contact: JustArchi@JustArchi.net
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
using SteamKit2;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal sealed class SteamTradeOffer {
|
||||
// REF: https://developer.valvesoftware.com/wiki/Steam_Web_API/IEconService
|
||||
internal enum ETradeOfferState {
|
||||
Unknown,
|
||||
Invalid,
|
||||
Active,
|
||||
Accepted,
|
||||
Countered,
|
||||
Expired,
|
||||
Canceled,
|
||||
Declined,
|
||||
InvalidItems,
|
||||
EmailPending,
|
||||
EmailCanceled
|
||||
}
|
||||
|
||||
internal string tradeofferid { get; set; }
|
||||
internal int accountid_other { get; set; }
|
||||
internal string message { get; set; }
|
||||
internal int expiration_time { get; set; }
|
||||
internal ETradeOfferState trade_offer_state { get; set; }
|
||||
internal List<SteamItem> items_to_give { get; set; }
|
||||
internal List<SteamItem> items_to_receive { get; set; }
|
||||
internal bool is_our_offer { get; set; }
|
||||
internal int time_created { get; set; }
|
||||
internal int time_updated { get; set; }
|
||||
internal bool from_real_time_trade { get; set; }
|
||||
|
||||
// Extra
|
||||
private ulong _OtherSteamID64 = 0;
|
||||
internal ulong OtherSteamID64 {
|
||||
get {
|
||||
if (_OtherSteamID64 == 0 && accountid_other != 0) {
|
||||
_OtherSteamID64 = new SteamID((uint) accountid_other, EUniverse.Public, EAccountType.Individual).ConvertToUInt64();
|
||||
}
|
||||
|
||||
return _OtherSteamID64;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
|
||||
Copyright 2015 Łukasz "JustArchi" Domeradzki
|
||||
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
|
||||
Contact: JustArchi@JustArchi.net
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -22,72 +22,177 @@
|
||||
|
||||
*/
|
||||
|
||||
using SteamAuth;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal sealed class Trading {
|
||||
internal const byte MaxItemsPerTrade = 150; // This is due to limit on POST size in WebBrowser
|
||||
internal const byte MaxTradesPerAccount = 5; // This is limit introduced by Valve
|
||||
|
||||
private static readonly SemaphoreSlim InventorySemaphore = new SemaphoreSlim(1);
|
||||
|
||||
private readonly Bot Bot;
|
||||
private readonly SemaphoreSlim Semaphore = new SemaphoreSlim(1);
|
||||
private volatile byte ParsingTasks = 0;
|
||||
private readonly SemaphoreSlim TradesSemaphore = new SemaphoreSlim(1);
|
||||
|
||||
private byte ParsingTasks;
|
||||
|
||||
internal static async Task LimitInventoryRequestsAsync() {
|
||||
await InventorySemaphore.WaitAsync().ConfigureAwait(false);
|
||||
Task.Run(async () => {
|
||||
await Utilities.SleepAsync(Program.GlobalConfig.InventoryLimiterDelay * 1000).ConfigureAwait(false);
|
||||
InventorySemaphore.Release();
|
||||
}).Forget();
|
||||
}
|
||||
|
||||
internal Trading(Bot bot) {
|
||||
if (bot == null) {
|
||||
throw new ArgumentNullException("bot");
|
||||
}
|
||||
|
||||
Bot = bot;
|
||||
}
|
||||
|
||||
internal async void CheckTrades() {
|
||||
if (ParsingTasks < 2) {
|
||||
internal async Task CheckTrades() {
|
||||
lock (TradesSemaphore) {
|
||||
if (ParsingTasks >= 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
ParsingTasks++;
|
||||
}
|
||||
|
||||
await Semaphore.WaitAsync().ConfigureAwait(false);
|
||||
await ParseActiveTrades().ConfigureAwait(false);
|
||||
Semaphore.Release();
|
||||
await TradesSemaphore.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
await ParseActiveTrades().ConfigureAwait(false);
|
||||
lock (TradesSemaphore) {
|
||||
ParsingTasks--;
|
||||
}
|
||||
|
||||
TradesSemaphore.Release();
|
||||
}
|
||||
|
||||
private async Task ParseActiveTrades() {
|
||||
List<SteamTradeOffer> tradeOffers = Bot.ArchiWebHandler.GetTradeOffers();
|
||||
if (tradeOffers == null) {
|
||||
HashSet<Steam.TradeOffer> tradeOffers = Bot.ArchiWebHandler.GetTradeOffers();
|
||||
if (tradeOffers == null || tradeOffers.Count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<Task> tasks = new List<Task>();
|
||||
foreach (SteamTradeOffer tradeOffer in tradeOffers) {
|
||||
if (tradeOffer.trade_offer_state == SteamTradeOffer.ETradeOfferState.Active) {
|
||||
tasks.Add(Task.Run(async () => await ParseTrade(tradeOffer).ConfigureAwait(false)));
|
||||
await tradeOffers.ForEachAsync(ParseTrade).ConfigureAwait(false);
|
||||
await Bot.AcceptConfirmations(true, Confirmation.ConfirmationType.Trade).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task ParseTrade(Steam.TradeOffer tradeOffer) {
|
||||
if (tradeOffer == null || tradeOffer.State != Steam.TradeOffer.ETradeOfferState.Active) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (await ShouldAcceptTrade(tradeOffer).ConfigureAwait(false)) {
|
||||
Logging.LogGenericInfo("Accepting trade: " + tradeOffer.TradeOfferID, Bot.BotName);
|
||||
await Bot.ArchiWebHandler.AcceptTradeOffer(tradeOffer.TradeOfferID).ConfigureAwait(false);
|
||||
} else {
|
||||
Logging.LogGenericInfo("Ignoring trade: " + tradeOffer.TradeOfferID, Bot.BotName);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<bool> ShouldAcceptTrade(Steam.TradeOffer tradeOffer) {
|
||||
if (tradeOffer == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Always accept trades when we're not losing anything
|
||||
if (tradeOffer.ItemsToGive.Count == 0) {
|
||||
// Unless it's steam fuckup and we're dealing with broken trade
|
||||
return tradeOffer.ItemsToReceive.Count > 0;
|
||||
}
|
||||
|
||||
// Always accept trades from SteamMasterID
|
||||
if (tradeOffer.OtherSteamID64 != 0 && tradeOffer.OtherSteamID64 == Bot.BotConfig.SteamMasterID) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we don't have SteamTradeMatcher enabled, this is the end for us
|
||||
if (!Bot.BotConfig.SteamTradeMatcher) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Rule 1 - We always trade the same amount of items
|
||||
if (tradeOffer.ItemsToGive.Count != tradeOffer.ItemsToReceive.Count) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Rule 2 - We always trade steam cards and only for the same set
|
||||
if (!tradeOffer.IsSteamCardsOnlyTrade() || !tradeOffer.IsPotentiallyDupesTrade()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// At this point we're sure that STM trade is valid
|
||||
// Now check if it's worth for us to do the trade
|
||||
HashSet<Steam.Item> inventory = await Bot.ArchiWebHandler.GetMyTradableInventory().ConfigureAwait(false);
|
||||
if (inventory == null || inventory.Count == 0) {
|
||||
return true; // OK, assume that this trade is valid, we can't check our EQ
|
||||
}
|
||||
|
||||
// Get appIDs we're interested in
|
||||
HashSet<uint> appIDs = new HashSet<uint>();
|
||||
foreach (Steam.Item item in tradeOffer.ItemsToGive) {
|
||||
appIDs.Add(item.RealAppID);
|
||||
}
|
||||
|
||||
// Now remove from our inventory all items we're NOT interested in
|
||||
inventory.RemoveWhere(item => !appIDs.Contains(item.RealAppID));
|
||||
inventory.TrimExcess();
|
||||
|
||||
// If for some reason Valve is talking crap and we can't find mentioned items, assume OK
|
||||
if (inventory.Count == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Now let's create a map which maps items to their amount in our EQ
|
||||
Dictionary<Tuple<ulong, ulong>, uint> amountMap = new Dictionary<Tuple<ulong, ulong>, uint>();
|
||||
foreach (Steam.Item item in inventory) {
|
||||
Tuple<ulong, ulong> key = new Tuple<ulong, ulong>(item.ClassID, item.InstanceID);
|
||||
|
||||
uint amount;
|
||||
if (amountMap.TryGetValue(key, out amount)) {
|
||||
amountMap[key] = amount + item.Amount;
|
||||
} else {
|
||||
amountMap[key] = item.Amount;
|
||||
}
|
||||
}
|
||||
|
||||
await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||
}
|
||||
// Calculate our value of items to give
|
||||
uint itemsToGiveDupesValue = 0;
|
||||
foreach (Steam.Item item in tradeOffer.ItemsToGive) {
|
||||
Tuple<ulong, ulong> key = new Tuple<ulong, ulong>(item.ClassID, item.InstanceID);
|
||||
|
||||
private async Task ParseTrade(SteamTradeOffer tradeOffer) {
|
||||
if (tradeOffer == null) {
|
||||
return;
|
||||
uint amount;
|
||||
if (!amountMap.TryGetValue(key, out amount)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
itemsToGiveDupesValue += amount;
|
||||
}
|
||||
|
||||
ulong tradeID;
|
||||
if (!ulong.TryParse(tradeOffer.tradeofferid, out tradeID)) {
|
||||
return;
|
||||
// Calculate our value of items to receive
|
||||
uint itemsToReceiveDupesValue = 0;
|
||||
foreach (Steam.Item item in tradeOffer.ItemsToReceive) {
|
||||
Tuple<ulong, ulong> key = new Tuple<ulong, ulong>(item.ClassID, item.InstanceID);
|
||||
|
||||
uint amount;
|
||||
if (!amountMap.TryGetValue(key, out amount)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
itemsToReceiveDupesValue += amount;
|
||||
}
|
||||
|
||||
ulong otherSteamID = tradeOffer.OtherSteamID64;
|
||||
bool success = false;
|
||||
bool tradeAccepted = false;
|
||||
|
||||
if (tradeOffer.items_to_give.Count == 0 || otherSteamID == Bot.SteamMasterID) {
|
||||
tradeAccepted = true;
|
||||
success = await Bot.ArchiWebHandler.AcceptTradeOffer(tradeID).ConfigureAwait(false);
|
||||
} else {
|
||||
success = Bot.ArchiWebHandler.DeclineTradeOffer(tradeID);
|
||||
}
|
||||
|
||||
if (tradeAccepted && success) {
|
||||
// Do whatever we want with success
|
||||
}
|
||||
// Trade is worth for us if we're in total trading more of our dupes for less of our dupes (or at least same amount)
|
||||
// Which means that itemsToGiveDupesValue should be greater than itemsToReceiveDupesValue
|
||||
return itemsToGiveDupesValue > itemsToReceiveDupesValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
|
||||
Copyright 2015 Łukasz "JustArchi" Domeradzki
|
||||
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
|
||||
Contact: JustArchi@JustArchi.net
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -22,196 +22,51 @@
|
||||
|
||||
*/
|
||||
|
||||
using HtmlAgilityPack;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal static class Utilities {
|
||||
private static readonly Random Random = new Random();
|
||||
internal static void Forget(this Task task) { }
|
||||
|
||||
internal static async Task SleepAsync(int miliseconds) {
|
||||
await Task.Delay(miliseconds).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
internal static ulong OnlyNumbers(string inputString) {
|
||||
if (string.IsNullOrEmpty(inputString)) {
|
||||
return 0;
|
||||
internal static Task ForEachAsync<T>(this IEnumerable<T> sequence, Func<T, Task> action) {
|
||||
if (action == null) {
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
string resultString;
|
||||
try {
|
||||
Regex regexObj = new Regex(@"[^\d]");
|
||||
resultString = regexObj.Replace(inputString, "");
|
||||
} catch (ArgumentException e) {
|
||||
Logging.LogGenericException("Utilities", e);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ulong.Parse(resultString, CultureInfo.InvariantCulture);
|
||||
return Task.WhenAll(sequence.Select(action));
|
||||
}
|
||||
|
||||
internal static async Task<HttpResponseMessage> UrlToHttpResponse(string websiteAddress, Dictionary<string, string> cookieVariables = null) {
|
||||
if (string.IsNullOrEmpty(websiteAddress)) {
|
||||
internal static string GetCookieValue(this CookieContainer cookieContainer, string URL, string name) {
|
||||
if (string.IsNullOrEmpty(URL) || string.IsNullOrEmpty(name)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
HttpResponseMessage result = null;
|
||||
CookieCollection cookies = cookieContainer.GetCookies(new Uri(URL));
|
||||
if (cookies == null || cookies.Count == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
using (HttpClientHandler clientHandler = new HttpClientHandler { UseCookies = false }) {
|
||||
using (HttpClient client = new HttpClient(clientHandler)) {
|
||||
client.Timeout = TimeSpan.FromSeconds(10);
|
||||
client.DefaultRequestHeaders.UserAgent.ParseAdd("ArchiSteamFarm/1.0");
|
||||
HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Get, websiteAddress);
|
||||
if (cookieVariables != null) {
|
||||
StringBuilder cookie = new StringBuilder();
|
||||
foreach (KeyValuePair<string, string> cookieVariable in cookieVariables) {
|
||||
cookie.Append(cookieVariable.Key + "=" + cookieVariable.Value + ";");
|
||||
}
|
||||
requestMessage.Headers.Add("Cookie", cookie.ToString());
|
||||
}
|
||||
HttpResponseMessage responseMessage = await client.SendAsync(requestMessage).ConfigureAwait(false);
|
||||
if (responseMessage != null) {
|
||||
responseMessage.EnsureSuccessStatusCode();
|
||||
result = responseMessage;
|
||||
}
|
||||
}
|
||||
foreach (Cookie cookie in cookies) {
|
||||
if (!cookie.Name.Equals(name, StringComparison.Ordinal)) {
|
||||
continue;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException("Utilities", e);
|
||||
|
||||
return cookie.Value;
|
||||
}
|
||||
|
||||
return result;
|
||||
return null;
|
||||
}
|
||||
|
||||
internal static async Task<HtmlDocument> UrlToHtmlDocument(string websiteAddress, Dictionary<string, string> cookieVariables = null) {
|
||||
if (string.IsNullOrEmpty(websiteAddress)) {
|
||||
return null;
|
||||
internal static Task SleepAsync(int miliseconds) {
|
||||
if (miliseconds < 0) {
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
HtmlDocument result = null;
|
||||
|
||||
try {
|
||||
HttpResponseMessage responseMessage = await UrlToHttpResponse(websiteAddress, cookieVariables).ConfigureAwait(false);
|
||||
if (responseMessage != null) {
|
||||
string source = await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
if (!string.IsNullOrEmpty(source)) {
|
||||
source = WebUtility.HtmlDecode(source);
|
||||
result = new HtmlDocument();
|
||||
result.LoadHtml(source);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException("Utilities", e);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static async Task<bool> UrlPostRequest(string request, Dictionary<string, string> postData, Dictionary<string, string> cookieVariables = null, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request) || postData == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = false;
|
||||
|
||||
try {
|
||||
using (HttpClientHandler clientHandler = new HttpClientHandler { UseCookies = false }) {
|
||||
using (HttpClient client = new HttpClient(clientHandler)) {
|
||||
client.Timeout = TimeSpan.FromSeconds(10);
|
||||
client.DefaultRequestHeaders.UserAgent.ParseAdd("ArchiSteamFarm/1.0");
|
||||
HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Post, request);
|
||||
requestMessage.Content = new FormUrlEncodedContent(postData);
|
||||
if (cookieVariables != null && cookieVariables.Count > 0) {
|
||||
StringBuilder cookie = new StringBuilder();
|
||||
foreach (KeyValuePair<string, string> cookieVariable in cookieVariables) {
|
||||
cookie.Append(cookieVariable.Key + "=" + cookieVariable.Value + ";");
|
||||
}
|
||||
requestMessage.Headers.Add("Cookie", cookie.ToString());
|
||||
}
|
||||
if (referer != null) {
|
||||
requestMessage.Headers.Referrer = new Uri(referer);
|
||||
}
|
||||
HttpResponseMessage responseMessage = await client.SendAsync(requestMessage).ConfigureAwait(false);
|
||||
if (responseMessage != null) {
|
||||
result = responseMessage.IsSuccessStatusCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException("Utilities", e);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static async Task<HttpResponseMessage> UrlPostRequestWithResponse(string request, Dictionary<string, string> postData, Dictionary<string, string> cookieVariables = null, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request) || postData == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
HttpResponseMessage result = null;
|
||||
|
||||
try {
|
||||
using (HttpClientHandler clientHandler = new HttpClientHandler { UseCookies = false }) {
|
||||
using (HttpClient client = new HttpClient(clientHandler)) {
|
||||
client.Timeout = TimeSpan.FromSeconds(10);
|
||||
client.DefaultRequestHeaders.UserAgent.ParseAdd("ArchiSteamFarm/1.0");
|
||||
HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Post, request);
|
||||
requestMessage.Content = new FormUrlEncodedContent(postData);
|
||||
if (cookieVariables != null && cookieVariables.Count > 0) {
|
||||
StringBuilder cookie = new StringBuilder();
|
||||
foreach (KeyValuePair<string, string> cookieVariable in cookieVariables) {
|
||||
cookie.Append(cookieVariable.Key + "=" + cookieVariable.Value + ";");
|
||||
}
|
||||
requestMessage.Headers.Add("Cookie", cookie.ToString());
|
||||
}
|
||||
if (referer != null) {
|
||||
requestMessage.Headers.Referrer = new Uri(referer);
|
||||
}
|
||||
HttpResponseMessage responseMessage = await client.SendAsync(requestMessage).ConfigureAwait(false);
|
||||
if (responseMessage != null) {
|
||||
result = responseMessage;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException("Utilities", e);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static async Task<JObject> UrlToJObject(string request) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
HttpResponseMessage httpResponseMessage = await UrlToHttpResponse(request, null).ConfigureAwait(false);
|
||||
if (httpResponseMessage == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
string source = await httpResponseMessage.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
if (string.IsNullOrEmpty(source)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
JObject result = null;
|
||||
try {
|
||||
result = JObject.Parse(source);
|
||||
} catch {
|
||||
}
|
||||
|
||||
return result;
|
||||
return Task.Delay(miliseconds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
150
ArchiSteamFarm/WCF.cs
Normal file
150
ArchiSteamFarm/WCF.cs
Normal file
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
_ _ _ ____ _ _____
|
||||
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
|
||||
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
|
||||
Contact: JustArchi@JustArchi.net
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.ServiceModel;
|
||||
using System.ServiceModel.Channels;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
[ServiceContract]
|
||||
internal interface IWCF {
|
||||
[OperationContract]
|
||||
string HandleCommand(string input);
|
||||
}
|
||||
|
||||
internal sealed class WCF : IWCF {
|
||||
|
||||
private static string URL = "http://localhost:1242/ASF";
|
||||
|
||||
private ServiceHost ServiceHost;
|
||||
private Client Client;
|
||||
|
||||
internal static void Init() {
|
||||
if (string.IsNullOrEmpty(Program.GlobalConfig.WCFHostname)) {
|
||||
Program.GlobalConfig.WCFHostname = Program.GetUserInput(Program.EUserInputType.WCFHostname);
|
||||
if (string.IsNullOrEmpty(Program.GlobalConfig.WCFHostname)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
URL = "http://" + Program.GlobalConfig.WCFHostname + ":" + Program.GlobalConfig.WCFPort + "/ASF";
|
||||
}
|
||||
|
||||
internal bool IsServerRunning() {
|
||||
return ServiceHost != null;
|
||||
}
|
||||
|
||||
internal void StartServer() {
|
||||
if (ServiceHost != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Logging.LogGenericInfo("Starting WCF server...");
|
||||
ServiceHost = new ServiceHost(typeof(WCF));
|
||||
ServiceHost.AddServiceEndpoint(typeof(IWCF), new BasicHttpBinding(), URL);
|
||||
|
||||
try {
|
||||
ServiceHost.Open();
|
||||
} catch (AddressAccessDeniedException) {
|
||||
Logging.LogGenericWarning("WCF service could not be started because of AddressAccessDeniedException");
|
||||
Logging.LogGenericWarning("If you want to use WCF service provided by ASF, consider starting ASF as administrator, or giving proper permissions");
|
||||
return;
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException(e);
|
||||
return;
|
||||
}
|
||||
|
||||
Logging.LogGenericInfo("WCF server ready!");
|
||||
}
|
||||
|
||||
internal void StopServer() {
|
||||
if (ServiceHost == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ServiceHost.Close();
|
||||
ServiceHost = null;
|
||||
}
|
||||
|
||||
internal string SendCommand(string input) {
|
||||
if (Client == null) {
|
||||
Client = new Client(new BasicHttpBinding(), new EndpointAddress(URL));
|
||||
}
|
||||
|
||||
return Client.HandleCommand(input);
|
||||
}
|
||||
|
||||
public string HandleCommand(string input) {
|
||||
if (string.IsNullOrEmpty(input)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
string[] args = input.Split(' ');
|
||||
|
||||
string botName;
|
||||
|
||||
if (args.Length > 1) { // If we have args[1] provided, use given botName
|
||||
botName = args[1];
|
||||
} else { // If not, just pick first one
|
||||
botName = Bot.Bots.Keys.FirstOrDefault();
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(botName)) {
|
||||
return "ERROR: Invalid botName: " + botName;
|
||||
}
|
||||
|
||||
Bot bot;
|
||||
if (!Bot.Bots.TryGetValue(botName, out bot)) {
|
||||
return "ERROR: Couldn't find any bot named: " + botName;
|
||||
}
|
||||
|
||||
Logging.LogGenericInfo("Received command: " + input);
|
||||
|
||||
string output;
|
||||
if (Program.GlobalConfig.SteamOwnerID == 0) {
|
||||
output = "Refusing to handle request because SteamOwnerID is not set!";
|
||||
} else {
|
||||
string command = '!' + input;
|
||||
output = bot.Response(Program.GlobalConfig.SteamOwnerID, command).Result; // TODO: This should be asynchronous
|
||||
}
|
||||
|
||||
Logging.LogGenericInfo("Answered to command: " + input + " with: " + output);
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class Client : ClientBase<IWCF>, IWCF {
|
||||
internal Client(Binding binding, EndpointAddress address) : base(binding, address) { }
|
||||
|
||||
public string HandleCommand(string input) {
|
||||
try {
|
||||
return Channel.HandleCommand(input);
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
279
ArchiSteamFarm/WebBrowser.cs
Normal file
279
ArchiSteamFarm/WebBrowser.cs
Normal file
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
_ _ _ ____ _ _____
|
||||
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
|
||||
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
|
||||
Contact: JustArchi@JustArchi.net
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
using HtmlAgilityPack;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal sealed class WebBrowser {
|
||||
internal const byte MaxRetries = 5; // Defines maximum number of retries, UrlRequest() does not handle retry by itself (it's app responsibility)
|
||||
|
||||
private const byte MaxConnections = 10; // Defines maximum number of connections per ServicePoint. Be careful, as it also defines maximum number of sockets in CLOSE_WAIT state
|
||||
private const byte MaxIdleTime = 15; // In seconds, how long socket is allowed to stay in CLOSE_WAIT state after there are no connections to it
|
||||
|
||||
private static readonly string DefaultUserAgent = "ArchiSteamFarm/" + Program.Version;
|
||||
|
||||
internal readonly CookieContainer CookieContainer = new CookieContainer();
|
||||
|
||||
private readonly HttpClient HttpClient;
|
||||
private readonly string Identifier;
|
||||
|
||||
internal static void Init() {
|
||||
// Set max connection limit from default of 2 to desired value
|
||||
ServicePointManager.DefaultConnectionLimit = MaxConnections;
|
||||
|
||||
// Set max idle time from default of 100 seconds (100 * 1000) to desired value
|
||||
ServicePointManager.MaxServicePointIdleTime = MaxIdleTime * 1000;
|
||||
|
||||
// Don't use Expect100Continue, we're sure about our POSTs, save some TCP packets
|
||||
ServicePointManager.Expect100Continue = false;
|
||||
|
||||
#if !__MonoCS__
|
||||
// Reuse ports if possible (since .NET 4.6+)
|
||||
//ServicePointManager.ReusePort = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
internal WebBrowser(string identifier) {
|
||||
if (string.IsNullOrEmpty(identifier)) {
|
||||
throw new ArgumentNullException("identifier");
|
||||
}
|
||||
|
||||
Identifier = identifier;
|
||||
|
||||
HttpClientHandler httpClientHandler = new HttpClientHandler {
|
||||
AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip,
|
||||
CookieContainer = CookieContainer
|
||||
};
|
||||
|
||||
HttpClient = new HttpClient(httpClientHandler) {
|
||||
Timeout = TimeSpan.FromSeconds(Program.GlobalConfig.HttpTimeout)
|
||||
};
|
||||
|
||||
// Most web services expect that UserAgent is set, so we declare it globally
|
||||
HttpClient.DefaultRequestHeaders.UserAgent.ParseAdd(DefaultUserAgent);
|
||||
}
|
||||
|
||||
internal async Task<bool> UrlHead(string request, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
using (HttpResponseMessage response = await UrlHeadToResponse(request, referer).ConfigureAwait(false)) {
|
||||
return response != null;
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task<Uri> UrlHeadToUri(string request, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
using (HttpResponseMessage response = await UrlHeadToResponse(request, referer).ConfigureAwait(false)) {
|
||||
if (response == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return response.RequestMessage.RequestUri;
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task<string> UrlGetToContent(string request, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
using (HttpResponseMessage httpResponse = await UrlGetToResponse(request, referer).ConfigureAwait(false)) {
|
||||
if (httpResponse == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task<byte[]> UrlGetToBytes(string request, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
using (HttpResponseMessage httpResponse = await UrlGetToResponse(request, referer).ConfigureAwait(false)) {
|
||||
if (httpResponse == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return await httpResponse.Content.ReadAsByteArrayAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task<HtmlDocument> UrlGetToHtmlDocument(string request, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
string content = await UrlGetToContent(request, referer).ConfigureAwait(false);
|
||||
if (string.IsNullOrEmpty(content)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
HtmlDocument htmlDocument = new HtmlDocument();
|
||||
htmlDocument.LoadHtml(WebUtility.HtmlDecode(content));
|
||||
return htmlDocument;
|
||||
}
|
||||
|
||||
internal async Task<JObject> UrlGetToJObject(string request, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
string content = await UrlGetToContent(request, referer).ConfigureAwait(false);
|
||||
if (string.IsNullOrEmpty(content)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
JObject jObject;
|
||||
|
||||
try {
|
||||
jObject = JObject.Parse(content);
|
||||
} catch (JsonException e) {
|
||||
Logging.LogGenericException(e, Identifier);
|
||||
return null;
|
||||
}
|
||||
|
||||
return jObject;
|
||||
}
|
||||
|
||||
internal async Task<XmlDocument> UrlGetToXML(string request, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
string content = await UrlGetToContent(request, referer).ConfigureAwait(false);
|
||||
if (string.IsNullOrEmpty(content)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
XmlDocument xmlDocument = new XmlDocument();
|
||||
|
||||
try {
|
||||
xmlDocument.LoadXml(content);
|
||||
} catch (XmlException e) {
|
||||
Logging.LogGenericException(e, Identifier);
|
||||
return null;
|
||||
}
|
||||
|
||||
return xmlDocument;
|
||||
}
|
||||
|
||||
internal async Task<bool> UrlPost(string request, Dictionary<string, string> data = null, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
using (HttpResponseMessage response = await UrlPostToResponse(request, data, referer).ConfigureAwait(false)) {
|
||||
return response != null;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<HttpResponseMessage> UrlGetToResponse(string request, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return await UrlRequest(request, HttpMethod.Get, null, referer).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task<HttpResponseMessage> UrlHeadToResponse(string request, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return await UrlRequest(request, HttpMethod.Head, null, referer).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task<HttpResponseMessage> UrlPostToResponse(string request, Dictionary<string, string> data = null, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return await UrlRequest(request, HttpMethod.Post, data, referer).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task<HttpResponseMessage> UrlRequest(string request, HttpMethod httpMethod, Dictionary<string, string> data = null, string referer = null) {
|
||||
if (string.IsNullOrEmpty(request) || httpMethod == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (request.StartsWith("https://", StringComparison.Ordinal) && Program.GlobalConfig.ForceHttp) {
|
||||
return null;
|
||||
}
|
||||
|
||||
HttpResponseMessage responseMessage;
|
||||
using (HttpRequestMessage requestMessage = new HttpRequestMessage(httpMethod, request)) {
|
||||
if (data != null && data.Count > 0) {
|
||||
try {
|
||||
requestMessage.Content = new FormUrlEncodedContent(data);
|
||||
} catch (UriFormatException e) {
|
||||
Logging.LogGenericException(e, Identifier);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(referer)) {
|
||||
requestMessage.Headers.Referrer = new Uri(referer);
|
||||
}
|
||||
|
||||
try {
|
||||
responseMessage = await HttpClient.SendAsync(requestMessage).ConfigureAwait(false);
|
||||
} catch { // Request failed, we don't need to know the exact reason, swallow exception
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (responseMessage == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!responseMessage.IsSuccessStatusCode) {
|
||||
if (Debugging.IsDebugBuild || Program.GlobalConfig.Debug) {
|
||||
Logging.LogGenericError("Request: " + request + " failed!", Identifier);
|
||||
Logging.LogGenericError("Status code: " + responseMessage.StatusCode, Identifier);
|
||||
Logging.LogGenericError("Content: " + Environment.NewLine + await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false), Identifier);
|
||||
}
|
||||
responseMessage.Dispose();
|
||||
return null;
|
||||
}
|
||||
|
||||
return responseMessage;
|
||||
}
|
||||
}
|
||||
}
|
||||
29
ArchiSteamFarm/config/ASF.json
Normal file
29
ArchiSteamFarm/config/ASF.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"Debug": false,
|
||||
"Headless": false,
|
||||
"AutoUpdates": true,
|
||||
"AutoRestart": true,
|
||||
"UpdateChannel": 1,
|
||||
"SteamProtocol": 6,
|
||||
"SteamOwnerID": 0,
|
||||
"MaxFarmingTime": 10,
|
||||
"IdleFarmingPeriod": 3,
|
||||
"FarmingDelay": 5,
|
||||
"AccountPlayingDelay": 5,
|
||||
"LoginLimiterDelay": 7,
|
||||
"InventoryLimiterDelay": 3,
|
||||
"ForceHttp": false,
|
||||
"HttpTimeout": 60,
|
||||
"WCFHostname": "localhost",
|
||||
"WCFPort": 1242,
|
||||
"LogToFile": true,
|
||||
"Statistics": true,
|
||||
"HackIgnoreMachineID": false,
|
||||
"Blacklist": [
|
||||
267420,
|
||||
303700,
|
||||
335590,
|
||||
368020,
|
||||
425280
|
||||
]
|
||||
}
|
||||
28
ArchiSteamFarm/config/example.json
Normal file
28
ArchiSteamFarm/config/example.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"Enabled": false,
|
||||
"StartOnLaunch": true,
|
||||
"SteamLogin": null,
|
||||
"SteamPassword": null,
|
||||
"SteamParentalPIN": "0",
|
||||
"SteamApiKey": null,
|
||||
"SteamMasterID": 0,
|
||||
"SteamMasterClanID": 0,
|
||||
"CardDropsRestricted": false,
|
||||
"DismissInventoryNotifications": true,
|
||||
"FarmOffline": false,
|
||||
"HandleOfflineMessages": false,
|
||||
"AcceptGifts": false,
|
||||
"SteamTradeMatcher": false,
|
||||
"ForwardKeysToOtherBots": false,
|
||||
"DistributeKeys": false,
|
||||
"UseAsfAsMobileAuthenticator": false,
|
||||
"ShutdownOnFarmingFinished": false,
|
||||
"SendOnFarmingFinished": false,
|
||||
"SteamTradeToken": null,
|
||||
"SendTradePeriod": 0,
|
||||
"AcceptConfirmationsPeriod": 0,
|
||||
"CustomGamePlayedWhileIdle": null,
|
||||
"GamesPlayedWhileIdle": [
|
||||
0
|
||||
]
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
|
||||
<!-- Every bot should have it's own unique .xml configuration file, this is example on which you can base on -->
|
||||
|
||||
<!-- Notice, if you use special characters reserved for XML, you should escape them -->
|
||||
<!-- Escape table: [& - &] | [" - "] | [' - '] | [< - <] | [> - >] -->
|
||||
<!-- So e.g. if your SteamPassword is "foo&" you should write value="foo&" -->
|
||||
|
||||
<!-- Master switch to turn account on and off, set to "true" after you're done -->
|
||||
<!-- TIP: This bot instance won't run unless below switch is set to "true" -->
|
||||
<Enabled type="bool" value="false"/>
|
||||
|
||||
<!-- This is your steam login, the one you use for logging in to steam -->
|
||||
<!-- TIP: You can use "null" if you wish to enter login on every startup -->
|
||||
<SteamLogin type="string" value="null"/>
|
||||
|
||||
<!-- This is your steam password, the one you use for logging in to steam -->
|
||||
<!-- TIP: You can use "null" if you wish to enter password on every startup -->
|
||||
<SteamPassword type="string" value="null"/>
|
||||
|
||||
<!-- This is steam nickname, the one you want to use for bot. Can be anything up to 32 characters -->
|
||||
<!-- TIP: You can use "null" if you wish to preserve your actual nickname -->
|
||||
<SteamNickname type="string" value="null"/>
|
||||
|
||||
<!-- This is your bot's API key, get one at https://steamcommunity.com/dev/apikey while logged in as bot, domain doesn't matter -->
|
||||
<!-- TIP: You can use "null", but it will disable all API-based functionalities such as trading -->
|
||||
<SteamApiKey type="string" value="null"/>
|
||||
|
||||
<!-- This is your parental PIN if you use steam parental functionality -->
|
||||
<!-- TIP: Most likely you don't want to change it. You can use "null" if you wish to enter PIN on every startup, 0 means there is no PIN -->
|
||||
<SteamParentalPIN type="string" value="0"/>
|
||||
|
||||
<!-- This is steamID64 of the bot-master - you, [Example: 76561198006963719] -->
|
||||
<!-- TIP: You can use "0", but bot won't accept steam cd-keys or trades from anybody" -->
|
||||
<SteamMasterID type="ulong" value="0"/>
|
||||
|
||||
<!-- This defines clan of the master, bot will join chatroom of that clan automatically after logging in if set -->
|
||||
<!-- SteamMasterClanID could be found by visiting your group-page -->
|
||||
<!-- [Example: http://steamcommunity.com/groups/hellokitty/] -->
|
||||
<!-- Adding "memberslistxml/?xml=1" to the end of this url so it looks like -->
|
||||
<!-- [Example: http://steamcommunity.com/groups/hellokitty/memberslistxml/?xml=1] -->
|
||||
<!-- Finally replace the SteamMasterClanID value with groupID64 you got from your groups xml-file -->
|
||||
<!-- TIP: Most likely you don't want to change it -->
|
||||
<SteamMasterClanID type="ulong" value="0"/>
|
||||
|
||||
<!-- This switch defines if bot should disconnect once farming is finished -->
|
||||
<!-- When no bots are active, ASF will shutdown as well -->
|
||||
<!-- Some people may want to keep their bots 24/7, other disconnect them after job is done -->
|
||||
<!-- Choose yourself what you prefer -->
|
||||
<ShutdownOnFarmingFinished type="bool" value="false"/>
|
||||
|
||||
<!-- Comma-separated list of IDs that should not be considered for farming -->
|
||||
<!-- TIP: Most likely you don't want to change it -->
|
||||
<Blacklist type="HashSet(uint)" value="303700,335590,368020"/>
|
||||
|
||||
<!-- This enables statistics for me - bot will join Archi's SC Farm group and chat -->
|
||||
<!-- Consider leaving it at "true", this way I can check how many running bots are active -->
|
||||
<!-- TIP: Group link is http://steamcommunity.com/groups/ascfarm -->
|
||||
<Statistics type="bool" value="true"/>
|
||||
|
||||
</configuration>
|
||||
5
ArchiSteamFarm/config/minimal.json
Normal file
5
ArchiSteamFarm/config/minimal.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"Enabled": false,
|
||||
"SteamLogin": null,
|
||||
"SteamPassword": null
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="HtmlAgilityPack" version="1.4.9" targetFramework="net45" />
|
||||
<package id="Newtonsoft.Json" version="8.0.1-beta1" targetFramework="net45" />
|
||||
<package id="Newtonsoft.Json" version="8.0.3" targetFramework="net451" />
|
||||
<package id="protobuf-net" version="2.0.0.668" targetFramework="net45" />
|
||||
<package id="SteamKit2" version="1.6.5" targetFramework="net45" />
|
||||
<package id="SteamKit2" version="1.7.0" targetFramework="net452" />
|
||||
</packages>
|
||||
90
ConfigGenerator/ASFConfig.cs
Normal file
90
ConfigGenerator/ASFConfig.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
_ _ _ ____ _ _____
|
||||
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
|
||||
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
|
||||
Contact: JustArchi@JustArchi.net
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace ConfigGenerator {
|
||||
internal abstract class ASFConfig {
|
||||
internal static readonly HashSet<ASFConfig> ASFConfigs = new HashSet<ASFConfig>();
|
||||
|
||||
internal string FilePath { get; set; }
|
||||
|
||||
protected ASFConfig() {
|
||||
ASFConfigs.Add(this);
|
||||
}
|
||||
|
||||
protected ASFConfig(string filePath) : this() {
|
||||
if (string.IsNullOrEmpty(filePath)) {
|
||||
throw new ArgumentNullException("filePath");
|
||||
}
|
||||
|
||||
FilePath = filePath;
|
||||
}
|
||||
|
||||
internal virtual void Save() {
|
||||
lock (FilePath) {
|
||||
try {
|
||||
File.WriteAllText(FilePath, JsonConvert.SerializeObject(this, Formatting.Indented));
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal virtual void Remove() {
|
||||
string queryPath = Path.GetFileNameWithoutExtension(FilePath);
|
||||
lock (FilePath) {
|
||||
foreach (string botFile in Directory.EnumerateFiles(Program.ConfigDirectory, queryPath + ".*")) {
|
||||
try {
|
||||
File.Delete(botFile);
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
ASFConfigs.Remove(this);
|
||||
}
|
||||
|
||||
internal virtual void Rename(string botName) {
|
||||
if (string.IsNullOrEmpty(botName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
string queryPath = Path.GetFileNameWithoutExtension(FilePath);
|
||||
lock (FilePath) {
|
||||
foreach (string botFile in Directory.EnumerateFiles(Program.ConfigDirectory, queryPath + ".*")) {
|
||||
try {
|
||||
File.Move(botFile, Path.Combine(Program.ConfigDirectory, botName + Path.GetExtension(botFile)));
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException(e);
|
||||
}
|
||||
}
|
||||
FilePath = Path.Combine(Program.ConfigDirectory, botName + ".json");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
6
ConfigGenerator/App.config
Normal file
6
ConfigGenerator/App.config
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.1" />
|
||||
</startup>
|
||||
</configuration>
|
||||
144
ConfigGenerator/BotConfig.cs
Normal file
144
ConfigGenerator/BotConfig.cs
Normal file
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
_ _ _ ____ _ _____
|
||||
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
|
||||
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
|
||||
Contact: JustArchi@JustArchi.net
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
|
||||
namespace ConfigGenerator {
|
||||
internal sealed class BotConfig : ASFConfig {
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public bool Enabled { get; set; } = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public bool StartOnLaunch { get; set; } = true;
|
||||
|
||||
[JsonProperty]
|
||||
public string SteamLogin { get; set; } = null;
|
||||
|
||||
[JsonProperty]
|
||||
[PasswordPropertyText(true)]
|
||||
public string SteamPassword { get; set; } = null;
|
||||
|
||||
[JsonProperty]
|
||||
public string SteamParentalPIN { get; set; } = "0";
|
||||
|
||||
[JsonProperty]
|
||||
public string SteamApiKey { get; set; } = null;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public ulong SteamMasterID { get; set; } = 0;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public ulong SteamMasterClanID { get; set; } = 0;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public bool CardDropsRestricted { get; set; } = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public bool DismissInventoryNotifications { get; set; } = true;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public bool FarmOffline { get; set; } = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public bool HandleOfflineMessages { get; set; } = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public bool AcceptGifts { get; set; } = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public bool SteamTradeMatcher { get; set; } = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public bool ForwardKeysToOtherBots { get; set; } = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public bool DistributeKeys { get; set; } = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public bool UseAsfAsMobileAuthenticator { get; set; } = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public bool ShutdownOnFarmingFinished { get; set; } = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public bool SendOnFarmingFinished { get; set; } = false;
|
||||
|
||||
[JsonProperty]
|
||||
public string SteamTradeToken { get; set; } = null;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public byte SendTradePeriod { get; set; } = 0;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public byte AcceptConfirmationsPeriod { get; set; } = 0;
|
||||
|
||||
[JsonProperty]
|
||||
public string CustomGamePlayedWhileIdle { get; set; } = null;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public List<uint> GamesPlayedWhileIdle { get; set; } = new List<uint>();
|
||||
|
||||
internal static BotConfig Load(string filePath) {
|
||||
if (string.IsNullOrEmpty(filePath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!File.Exists(filePath)) {
|
||||
return new BotConfig(filePath);
|
||||
}
|
||||
|
||||
BotConfig botConfig;
|
||||
try {
|
||||
botConfig = JsonConvert.DeserializeObject<BotConfig>(File.ReadAllText(filePath));
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException(e);
|
||||
return new BotConfig(filePath);
|
||||
}
|
||||
|
||||
if (botConfig == null) {
|
||||
return new BotConfig(filePath);
|
||||
}
|
||||
|
||||
botConfig.FilePath = filePath;
|
||||
|
||||
return botConfig;
|
||||
}
|
||||
|
||||
// This constructor is used only by deserializer
|
||||
private BotConfig() { }
|
||||
|
||||
private BotConfig(string filePath) : base(filePath) {
|
||||
if (string.IsNullOrEmpty(filePath)) {
|
||||
throw new ArgumentNullException("filePath");
|
||||
}
|
||||
|
||||
GamesPlayedWhileIdle.Add(0);
|
||||
Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
122
ConfigGenerator/ConfigGenerator.csproj
Normal file
122
ConfigGenerator/ConfigGenerator.csproj
Normal file
@@ -0,0 +1,122 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{C3F6FE68-5E75-415E-BEA1-1E7C16D6A433}</ProjectGuid>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>ConfigGenerator</RootNamespace>
|
||||
<AssemblyName>ConfigGenerator</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>none</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>
|
||||
</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ApplicationIcon>cirno.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.8.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="ASFConfig.cs" />
|
||||
<Compile Include="BotConfig.cs" />
|
||||
<Compile Include="DialogBox.cs" />
|
||||
<Compile Include="EnhancedPropertyGrid.cs">
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Debugging.cs" />
|
||||
<Compile Include="GlobalConfig.cs" />
|
||||
<Compile Include="ConfigPage.cs">
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Logging.cs" />
|
||||
<Compile Include="MainForm.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="MainForm.Designer.cs">
|
||||
<DependentUpon>MainForm.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Tutorial.cs" />
|
||||
<EmbeddedResource Include="ConfigPage.resx">
|
||||
<DependentUpon>ConfigPage.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="MainForm.resx">
|
||||
<DependentUpon>MainForm.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
<SubType>Designer</SubType>
|
||||
</EmbeddedResource>
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<None Include="packages.config" />
|
||||
<None Include="Properties\Settings.settings">
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
</None>
|
||||
<Compile Include="Properties\Settings.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="cirno.ico" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent Condition=" '$(OS)' != 'Unix' AND '$(ConfigurationName)' == 'Release' ">
|
||||
"$(SolutionDir)tools\ILRepack\ILRepack.exe" /ndebug /internalize /parallel /targetplatform:v4 /wildcards /out:"$(SolutionDir)out\ASF-ConfigGenerator.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll"
|
||||
del "$(SolutionDir)out\ASF-ConfigGenerator.exe.config"
|
||||
</PostBuildEvent>
|
||||
<PostBuildEvent Condition=" '$(OS)' == 'Unix' AND '$(ConfigurationName)' == 'Release' ">
|
||||
mono -O=all "$(SolutionDir)tools/ILRepack/ILRepack.exe" /ndebug /internalize /parallel /targetplatform:v4 /wildcards /out:"$(SolutionDir)out/ASF-ConfigGenerator.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll"
|
||||
rm "$(SolutionDir)out/ASF-ConfigGenerator.exe.config"
|
||||
</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
49
ConfigGenerator/ConfigPage.cs
Normal file
49
ConfigGenerator/ConfigPage.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
_ _ _ ____ _ _____
|
||||
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
|
||||
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
|
||||
Contact: JustArchi@JustArchi.net
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
using System.IO;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace ConfigGenerator {
|
||||
internal sealed class ConfigPage : TabPage {
|
||||
internal readonly ASFConfig ASFConfig;
|
||||
|
||||
internal ConfigPage(ASFConfig config) {
|
||||
if (config == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ASFConfig = config;
|
||||
|
||||
RefreshText();
|
||||
|
||||
EnhancedPropertyGrid enhancedPropertyGrid = new EnhancedPropertyGrid(config);
|
||||
Controls.Add(enhancedPropertyGrid);
|
||||
}
|
||||
|
||||
internal void RefreshText() {
|
||||
Text = Path.GetFileNameWithoutExtension(ASFConfig.FilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
123
ConfigGenerator/ConfigPage.resx
Normal file
123
ConfigGenerator/ConfigPage.resx
Normal file
@@ -0,0 +1,123 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<metadata name="$this.TrayLargeIcon" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>False</value>
|
||||
</metadata>
|
||||
</root>
|
||||
@@ -5,7 +5,7 @@
|
||||
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
|
||||
Copyright 2015 Łukasz "JustArchi" Domeradzki
|
||||
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
|
||||
Contact: JustArchi@JustArchi.net
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -22,16 +22,14 @@
|
||||
|
||||
*/
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal sealed class SteamItem {
|
||||
// REF: https://developer.valvesoftware.com/wiki/Steam_Web_API/IEconService
|
||||
internal string appid { get; set; }
|
||||
internal string contextid { get; set; }
|
||||
internal string assetid { get; set; }
|
||||
internal string currencyid { get; set; }
|
||||
internal string classid { get; set; }
|
||||
internal string instanceid { get; set; }
|
||||
internal string amount { get; set; }
|
||||
internal bool missing { get; set; }
|
||||
namespace ConfigGenerator {
|
||||
internal static class Debugging {
|
||||
#if DEBUG
|
||||
internal static readonly bool IsDebugBuild = true;
|
||||
#else
|
||||
internal static readonly bool IsDebugBuild = false;
|
||||
#endif
|
||||
|
||||
internal static bool IsReleaseBuild => !IsDebugBuild;
|
||||
}
|
||||
}
|
||||
119
ConfigGenerator/DialogBox.cs
Normal file
119
ConfigGenerator/DialogBox.cs
Normal file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
_ _ _ ____ _ _____
|
||||
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
|
||||
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
|
||||
Contact: JustArchi@JustArchi.net
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace ConfigGenerator {
|
||||
internal sealed class DialogBox {
|
||||
internal static DialogResult InputBox(string title, string promptText, out string value) {
|
||||
if (string.IsNullOrEmpty(title) || string.IsNullOrEmpty(promptText)) {
|
||||
value = null;
|
||||
return DialogResult.Abort;
|
||||
}
|
||||
|
||||
Form form = new Form();
|
||||
Label label = new Label();
|
||||
TextBox textBox = new TextBox();
|
||||
|
||||
textBox.Width = 1000;
|
||||
Button buttonOk = new Button();
|
||||
Button buttonCancel = new Button();
|
||||
|
||||
form.Text = title;
|
||||
label.Text = promptText;
|
||||
|
||||
buttonOk.Text = "OK";
|
||||
buttonCancel.Text = "Cancel";
|
||||
buttonOk.DialogResult = DialogResult.OK;
|
||||
buttonCancel.DialogResult = DialogResult.Cancel;
|
||||
|
||||
label.SetBounds(9, 20, 372, 13);
|
||||
textBox.SetBounds(12, 36, 372, 20);
|
||||
buttonOk.SetBounds(228, 72, 75, 23);
|
||||
buttonCancel.SetBounds(309, 72, 75, 23);
|
||||
|
||||
label.AutoSize = true;
|
||||
textBox.Anchor = textBox.Anchor | AnchorStyles.Right;
|
||||
buttonOk.Anchor = AnchorStyles.Bottom | AnchorStyles.Right;
|
||||
buttonCancel.Anchor = AnchorStyles.Bottom | AnchorStyles.Right;
|
||||
|
||||
form.ClientSize = new Size(396, 107);
|
||||
form.Controls.AddRange(new Control[] { label, textBox, buttonOk, buttonCancel });
|
||||
form.ClientSize = new Size(Math.Max(300, label.Right + 10), form.ClientSize.Height);
|
||||
form.FormBorderStyle = FormBorderStyle.FixedDialog;
|
||||
form.StartPosition = FormStartPosition.CenterScreen;
|
||||
form.MinimizeBox = false;
|
||||
form.MaximizeBox = false;
|
||||
form.AcceptButton = buttonOk;
|
||||
form.CancelButton = buttonCancel;
|
||||
|
||||
DialogResult dialogResult = form.ShowDialog();
|
||||
value = textBox.Text;
|
||||
return dialogResult;
|
||||
}
|
||||
|
||||
internal static DialogResult YesNoBox(string title, string promptText) {
|
||||
if (string.IsNullOrEmpty(title) || string.IsNullOrEmpty(promptText)) {
|
||||
return DialogResult.Abort;
|
||||
}
|
||||
|
||||
Form form = new Form();
|
||||
Label label = new Label();
|
||||
|
||||
Button buttonOk = new Button();
|
||||
Button buttonCancel = new Button();
|
||||
|
||||
form.Text = title;
|
||||
label.Text = promptText;
|
||||
|
||||
buttonOk.Text = "Yes";
|
||||
buttonCancel.Text = "No";
|
||||
buttonOk.DialogResult = DialogResult.Yes;
|
||||
buttonCancel.DialogResult = DialogResult.No;
|
||||
|
||||
label.SetBounds(9, 20, 372, 13);
|
||||
buttonOk.SetBounds(228, 50, 75, 23);
|
||||
buttonCancel.SetBounds(309, 50, 75, 23);
|
||||
|
||||
label.AutoSize = true;
|
||||
buttonOk.Anchor = AnchorStyles.Bottom | AnchorStyles.Right;
|
||||
buttonCancel.Anchor = AnchorStyles.Bottom | AnchorStyles.Right;
|
||||
|
||||
form.ClientSize = new Size(396, 80);
|
||||
form.Controls.AddRange(new Control[] { label, buttonOk, buttonCancel });
|
||||
form.ClientSize = new Size(Math.Max(300, label.Right + 10), form.ClientSize.Height);
|
||||
form.FormBorderStyle = FormBorderStyle.FixedDialog;
|
||||
form.StartPosition = FormStartPosition.CenterScreen;
|
||||
form.MinimizeBox = false;
|
||||
form.MaximizeBox = false;
|
||||
form.AcceptButton = buttonOk;
|
||||
form.CancelButton = buttonCancel;
|
||||
|
||||
DialogResult dialogResult = form.ShowDialog();
|
||||
return dialogResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
82
ConfigGenerator/EnhancedPropertyGrid.cs
Normal file
82
ConfigGenerator/EnhancedPropertyGrid.cs
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
_ _ _ ____ _ _____
|
||||
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
|
||||
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
|
||||
Contact: JustArchi@JustArchi.net
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace ConfigGenerator {
|
||||
internal sealed class EnhancedPropertyGrid : PropertyGrid {
|
||||
private readonly ASFConfig ASFConfig;
|
||||
|
||||
internal EnhancedPropertyGrid(ASFConfig config) {
|
||||
if (config == null) {
|
||||
throw new ArgumentNullException("config");
|
||||
}
|
||||
|
||||
ASFConfig = config;
|
||||
|
||||
SelectedObject = config;
|
||||
Anchor = AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top;
|
||||
Dock = DockStyle.Fill;
|
||||
HelpVisible = false;
|
||||
ToolbarVisible = false;
|
||||
}
|
||||
|
||||
protected override void OnPropertyValueChanged(PropertyValueChangedEventArgs e) {
|
||||
if (e == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
base.OnPropertyValueChanged(e);
|
||||
ASFConfig.Save();
|
||||
|
||||
BotConfig botConfig = ASFConfig as BotConfig;
|
||||
if (botConfig != null) {
|
||||
if (botConfig.Enabled) {
|
||||
Tutorial.OnAction(Tutorial.EPhase.BotEnabled);
|
||||
if (!string.IsNullOrEmpty(botConfig.SteamLogin) && !string.IsNullOrEmpty(botConfig.SteamPassword)) {
|
||||
Tutorial.OnAction(Tutorial.EPhase.BotReady);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
GlobalConfig globalConfig = ASFConfig as GlobalConfig;
|
||||
if (globalConfig != null) {
|
||||
if (globalConfig.SteamOwnerID != 0) {
|
||||
Tutorial.OnAction(Tutorial.EPhase.GlobalConfigReady);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnGotFocus(EventArgs e) {
|
||||
if (e == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
base.OnGotFocus(e);
|
||||
ASFConfig.Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
184
ConfigGenerator/GlobalConfig.cs
Normal file
184
ConfigGenerator/GlobalConfig.cs
Normal file
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
_ _ _ ____ _ _____
|
||||
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
|
||||
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
|
||||
Contact: JustArchi@JustArchi.net
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace ConfigGenerator {
|
||||
internal sealed class GlobalConfig : ASFConfig {
|
||||
internal enum EUpdateChannel : byte {
|
||||
Unknown,
|
||||
Stable,
|
||||
Experimental
|
||||
}
|
||||
|
||||
private const byte DefaultMaxFarmingTime = 10;
|
||||
private const byte DefaultFarmingDelay = 5;
|
||||
private const byte DefaultHttpTimeout = 60;
|
||||
private const ushort DefaultWCFPort = 1242;
|
||||
private const ProtocolType DefaultSteamProtocol = ProtocolType.Tcp;
|
||||
|
||||
// This is hardcoded blacklist which should not be possible to change
|
||||
internal static readonly HashSet<uint> GlobalBlacklist = new HashSet<uint> { 267420, 303700, 335590, 368020, 425280 };
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public bool Debug { get; set; } = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public bool Headless { get; set; } = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public bool AutoUpdates { get; set; } = true;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public bool AutoRestart { get; set; } = true;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public EUpdateChannel UpdateChannel { get; set; } = EUpdateChannel.Stable;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public ProtocolType SteamProtocol { get; set; } = DefaultSteamProtocol;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public ulong SteamOwnerID { get; set; } = 0;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public byte MaxFarmingTime { get; set; } = DefaultMaxFarmingTime;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public byte IdleFarmingPeriod { get; set; } = 3;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public byte FarmingDelay { get; set; } = DefaultFarmingDelay;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public byte AccountPlayingDelay { get; set; } = 5;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public byte LoginLimiterDelay { get; set; } = 7;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public byte InventoryLimiterDelay { get; set; } = 3;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public bool ForceHttp { get; set; } = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public byte HttpTimeout { get; set; } = DefaultHttpTimeout;
|
||||
|
||||
[JsonProperty]
|
||||
public string WCFHostname { get; set; } = "localhost";
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public ushort WCFPort { get; set; } = DefaultWCFPort;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public bool LogToFile { get; set; } = true;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public bool Statistics { get; set; } = true;
|
||||
|
||||
// TODO: Please remove me immediately after https://github.com/SteamRE/SteamKit/issues/254 gets fixed
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public bool HackIgnoreMachineID { get; set; } = false;
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
public List<uint> Blacklist { get; set; } = new List<uint>();
|
||||
|
||||
internal static GlobalConfig Load(string filePath) {
|
||||
if (string.IsNullOrEmpty(filePath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!File.Exists(filePath)) {
|
||||
return new GlobalConfig(filePath);
|
||||
}
|
||||
|
||||
GlobalConfig globalConfig;
|
||||
try {
|
||||
globalConfig = JsonConvert.DeserializeObject<GlobalConfig>(File.ReadAllText(filePath));
|
||||
} catch (Exception e) {
|
||||
Logging.LogGenericException(e);
|
||||
return new GlobalConfig(filePath);
|
||||
}
|
||||
|
||||
if (globalConfig == null) {
|
||||
return new GlobalConfig(filePath);
|
||||
}
|
||||
|
||||
globalConfig.FilePath = filePath;
|
||||
|
||||
// SK2 supports only TCP and UDP steam protocols
|
||||
// Ensure that user can't screw this up
|
||||
switch (globalConfig.SteamProtocol) {
|
||||
case ProtocolType.Tcp:
|
||||
case ProtocolType.Udp:
|
||||
break;
|
||||
default:
|
||||
Logging.LogGenericWarning("Configured SteamProtocol is invalid: " + globalConfig.SteamProtocol + ". Value of " + DefaultSteamProtocol + " will be used instead");
|
||||
globalConfig.SteamProtocol = DefaultSteamProtocol;
|
||||
break;
|
||||
}
|
||||
|
||||
// User might not know what he's doing
|
||||
// Ensure that he can't screw core ASF variables
|
||||
if (globalConfig.MaxFarmingTime == 0) {
|
||||
Logging.LogGenericWarning("Configured MaxFarmingTime is invalid: " + globalConfig.MaxFarmingTime + ". Value of " + DefaultMaxFarmingTime + " will be used instead");
|
||||
globalConfig.MaxFarmingTime = DefaultMaxFarmingTime;
|
||||
}
|
||||
|
||||
if (globalConfig.FarmingDelay == 0) {
|
||||
Logging.LogGenericWarning("Configured FarmingDelay is invalid: " + globalConfig.FarmingDelay + ". Value of " + DefaultFarmingDelay + " will be used instead");
|
||||
globalConfig.FarmingDelay = DefaultFarmingDelay;
|
||||
}
|
||||
|
||||
if (globalConfig.HttpTimeout == 0) {
|
||||
Logging.LogGenericWarning("Configured HttpTimeout is invalid: " + globalConfig.HttpTimeout + ". Value of " + DefaultHttpTimeout + " will be used instead");
|
||||
globalConfig.HttpTimeout = DefaultHttpTimeout;
|
||||
}
|
||||
|
||||
if (globalConfig.WCFPort == 0) {
|
||||
Logging.LogGenericWarning("Configured WCFPort is invalid: " + globalConfig.WCFPort + ". Value of " + DefaultWCFPort + " will be used instead");
|
||||
globalConfig.WCFPort = DefaultWCFPort;
|
||||
}
|
||||
|
||||
return globalConfig;
|
||||
}
|
||||
|
||||
// This constructor is used only by deserializer
|
||||
private GlobalConfig() { }
|
||||
|
||||
private GlobalConfig(string filePath) : base(filePath) {
|
||||
if (string.IsNullOrEmpty(filePath)) {
|
||||
throw new ArgumentNullException("filePath");
|
||||
}
|
||||
|
||||
Blacklist.AddRange(GlobalBlacklist);
|
||||
Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
93
ConfigGenerator/Logging.cs
Normal file
93
ConfigGenerator/Logging.cs
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
_ _ _ ____ _ _____
|
||||
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
|
||||
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
|
||||
Contact: JustArchi@JustArchi.net
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace ConfigGenerator {
|
||||
internal static class Logging {
|
||||
internal static void LogGenericInfo(string message) {
|
||||
if (string.IsNullOrEmpty(message)) {
|
||||
return;
|
||||
}
|
||||
|
||||
MessageBox.Show(message, "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
}
|
||||
|
||||
internal static void LogGenericWTF(string message, [CallerMemberName] string previousMethodName = "") {
|
||||
if (string.IsNullOrEmpty(message)) {
|
||||
return;
|
||||
}
|
||||
|
||||
MessageBox.Show(previousMethodName + "() " + message, "WTF", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
|
||||
internal static void LogGenericError(string message, [CallerMemberName] string previousMethodName = "") {
|
||||
if (string.IsNullOrEmpty(message)) {
|
||||
return;
|
||||
}
|
||||
|
||||
MessageBox.Show(previousMethodName + "() " + message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
|
||||
internal static void LogGenericException(Exception exception, [CallerMemberName] string previousMethodName = "") {
|
||||
if (exception == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
MessageBox.Show(previousMethodName + "() " + exception.Message + Environment.NewLine + exception.StackTrace, "Exception", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
|
||||
if (exception.InnerException != null) {
|
||||
LogGenericException(exception.InnerException, previousMethodName);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void LogGenericWarning(string message, [CallerMemberName] string previousMethodName = "") {
|
||||
if (string.IsNullOrEmpty(message)) {
|
||||
return;
|
||||
}
|
||||
|
||||
MessageBox.Show(previousMethodName + "() " + message, "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||
}
|
||||
|
||||
internal static void LogNullError(string nullObjectName, [CallerMemberName] string previousMethodName = "") {
|
||||
if (string.IsNullOrEmpty(nullObjectName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
LogGenericError(nullObjectName + " is null!", previousMethodName);
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
internal static void LogGenericDebug(string message, [CallerMemberName] string previousMethodName = "") {
|
||||
if (string.IsNullOrEmpty(message)) {
|
||||
return;
|
||||
}
|
||||
|
||||
MessageBox.Show(previousMethodName + "() " + message, "Debug", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
}
|
||||
}
|
||||
}
|
||||
78
ConfigGenerator/MainForm.Designer.cs
generated
Normal file
78
ConfigGenerator/MainForm.Designer.cs
generated
Normal file
@@ -0,0 +1,78 @@
|
||||
namespace ConfigGenerator {
|
||||
partial class MainForm {
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing) {
|
||||
if (disposing && (components != null)) {
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent() {
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm));
|
||||
this.MainTab = new System.Windows.Forms.TabControl();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// MainTab
|
||||
//
|
||||
this.MainTab.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||
| System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.MainTab.Appearance = System.Windows.Forms.TabAppearance.Buttons;
|
||||
this.MainTab.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
|
||||
this.MainTab.HotTrack = true;
|
||||
this.MainTab.Location = new System.Drawing.Point(14, 14);
|
||||
this.MainTab.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
|
||||
this.MainTab.Multiline = true;
|
||||
this.MainTab.Name = "MainTab";
|
||||
this.MainTab.SelectedIndex = 0;
|
||||
this.MainTab.Size = new System.Drawing.Size(854, 745);
|
||||
this.MainTab.SizeMode = System.Windows.Forms.TabSizeMode.Fixed;
|
||||
this.MainTab.TabIndex = 1;
|
||||
this.MainTab.Selected += new System.Windows.Forms.TabControlEventHandler(this.MainTab_Selected);
|
||||
this.MainTab.Deselecting += new System.Windows.Forms.TabControlCancelEventHandler(this.MainTab_Deselecting);
|
||||
//
|
||||
// MainForm
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 17F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.AutoScroll = true;
|
||||
this.ClientSize = new System.Drawing.Size(882, 774);
|
||||
this.Controls.Add(this.MainTab);
|
||||
this.Cursor = System.Windows.Forms.Cursors.Default;
|
||||
this.DoubleBuffered = true;
|
||||
this.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
|
||||
this.HelpButton = true;
|
||||
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
|
||||
this.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
|
||||
this.MaximizeBox = false;
|
||||
this.MinimizeBox = false;
|
||||
this.Name = "MainForm";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
||||
this.Text = "ASF Config Generator";
|
||||
this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.MainForm_HelpButtonClicked);
|
||||
this.Load += new System.EventHandler(this.MainForm_Load);
|
||||
this.Shown += new System.EventHandler(this.MainForm_Shown);
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
private System.Windows.Forms.TabControl MainTab;
|
||||
}
|
||||
}
|
||||
|
||||
199
ConfigGenerator/MainForm.cs
Normal file
199
ConfigGenerator/MainForm.cs
Normal file
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
_ _ _ ____ _ _____
|
||||
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
|
||||
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
|
||||
Contact: JustArchi@JustArchi.net
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace ConfigGenerator {
|
||||
internal sealed partial class MainForm : Form {
|
||||
private const byte ReservedTabs = 3;
|
||||
|
||||
private readonly TabPage NewTab = new TabPage { Text = "+" };
|
||||
private readonly TabPage RemoveTab = new TabPage { Text = "-" };
|
||||
private readonly TabPage RenameTab = new TabPage { Text = "~" };
|
||||
|
||||
private ConfigPage ASFTab;
|
||||
private TabPage OldTab;
|
||||
|
||||
internal MainForm() {
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void MainForm_Load(object sender, EventArgs e) {
|
||||
if (sender == null || e == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ASFTab = new ConfigPage(GlobalConfig.Load(Path.Combine(Program.ConfigDirectory, Program.GlobalConfigFile)));
|
||||
|
||||
MainTab.TabPages.Add(ASFTab);
|
||||
|
||||
foreach (var configFile in Directory.EnumerateFiles(Program.ConfigDirectory, "*.json")) {
|
||||
string botName = Path.GetFileNameWithoutExtension(configFile);
|
||||
switch (botName) {
|
||||
case Program.ASF:
|
||||
case "example":
|
||||
case "minimal":
|
||||
continue;
|
||||
}
|
||||
|
||||
MainTab.TabPages.Add(new ConfigPage(BotConfig.Load(configFile)));
|
||||
Tutorial.Enabled = false;
|
||||
}
|
||||
|
||||
MainTab.TabPages.AddRange(new[] { RemoveTab, RenameTab, NewTab });
|
||||
Tutorial.OnAction(Tutorial.EPhase.Start);
|
||||
}
|
||||
|
||||
private void MainTab_Selected(object sender, TabControlEventArgs e) {
|
||||
if (sender == null || e == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.TabPage == RemoveTab) {
|
||||
ConfigPage configPage = OldTab as ConfigPage;
|
||||
if (configPage == null) {
|
||||
MainTab.SelectedIndex = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (configPage == ASFTab) {
|
||||
MainTab.SelectedTab = ASFTab;
|
||||
Logging.LogGenericError("You can't remove global config!");
|
||||
return;
|
||||
}
|
||||
|
||||
MainTab.SelectedTab = configPage;
|
||||
|
||||
if (DialogBox.YesNoBox("Removal", "Do you really want to remove this config?") != DialogResult.Yes) {
|
||||
return;
|
||||
}
|
||||
|
||||
MainTab.SelectedIndex = 0;
|
||||
configPage.ASFConfig.Remove();
|
||||
MainTab.TabPages.Remove(configPage);
|
||||
} else if (e.TabPage == RenameTab) {
|
||||
ConfigPage configPage = OldTab as ConfigPage;
|
||||
if (configPage == null) {
|
||||
MainTab.SelectedIndex = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (configPage == ASFTab) {
|
||||
MainTab.SelectedTab = ASFTab;
|
||||
Logging.LogGenericError("You can't rename global config!");
|
||||
return;
|
||||
}
|
||||
|
||||
MainTab.SelectedTab = configPage;
|
||||
|
||||
string input;
|
||||
if (DialogBox.InputBox("Rename", "Your new bot name:", out input) != DialogResult.OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(input)) {
|
||||
Logging.LogGenericError("Your bot name is empty!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get rid of any potential whitespaces in bot name
|
||||
input = Regex.Replace(input, @"\s+", "");
|
||||
|
||||
configPage.ASFConfig.Rename(input);
|
||||
configPage.RefreshText();
|
||||
} else if (e.TabPage == NewTab) {
|
||||
ConfigPage configPage = OldTab as ConfigPage;
|
||||
if (configPage == null) {
|
||||
MainTab.SelectedIndex = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
MainTab.SelectedTab = configPage;
|
||||
|
||||
Tutorial.OnAction(Tutorial.EPhase.BotNickname);
|
||||
|
||||
string input;
|
||||
if (DialogBox.InputBox("New", "Your new bot name:", out input) != DialogResult.OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(input)) {
|
||||
Logging.LogGenericError("Your bot name is empty!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get rid of any potential whitespaces in bot name
|
||||
input = Regex.Replace(input, @"\s+", "");
|
||||
|
||||
foreach (ASFConfig config in ASFConfig.ASFConfigs) {
|
||||
if (Path.GetFileNameWithoutExtension(config.FilePath).Equals(input)) {
|
||||
Logging.LogGenericError("Bot with such name exists already!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
input = Path.Combine(Program.ConfigDirectory, input + ".json");
|
||||
|
||||
ConfigPage newConfigPage = new ConfigPage(BotConfig.Load(input));
|
||||
MainTab.TabPages.Insert(MainTab.TabPages.Count - ReservedTabs, newConfigPage);
|
||||
MainTab.SelectedTab = newConfigPage;
|
||||
Tutorial.OnAction(Tutorial.EPhase.BotNicknameFinished);
|
||||
} else if (e.TabPage == ASFTab) {
|
||||
Tutorial.OnAction(Tutorial.EPhase.GlobalConfigOpened);
|
||||
}
|
||||
}
|
||||
|
||||
private void MainTab_Deselecting(object sender, TabControlCancelEventArgs e) {
|
||||
if (sender == null || e == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
OldTab = e.TabPage;
|
||||
}
|
||||
|
||||
private void MainForm_Shown(object sender, EventArgs e) {
|
||||
if (sender == null || e == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Tutorial.OnAction(Tutorial.EPhase.Shown);
|
||||
}
|
||||
|
||||
private void MainForm_HelpButtonClicked(object sender, CancelEventArgs e) {
|
||||
if (sender == null || e == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
e.Cancel = true;
|
||||
Tutorial.OnAction(Tutorial.EPhase.Help);
|
||||
Process.Start("https://github.com/JustArchi/ArchiSteamFarm/wiki/Configuration");
|
||||
Tutorial.OnAction(Tutorial.EPhase.HelpFinished);
|
||||
}
|
||||
}
|
||||
}
|
||||
6293
ConfigGenerator/MainForm.resx
Normal file
6293
ConfigGenerator/MainForm.resx
Normal file
File diff suppressed because it is too large
Load Diff
98
ConfigGenerator/Program.cs
Normal file
98
ConfigGenerator/Program.cs
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
_ _ _ ____ _ _____
|
||||
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
|
||||
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
|
||||
Contact: JustArchi@JustArchi.net
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace ConfigGenerator {
|
||||
internal static class Program {
|
||||
internal const string ASF = "ASF";
|
||||
internal const string ConfigDirectory = "config";
|
||||
internal const string GlobalConfigFile = ASF + ".json";
|
||||
|
||||
private const string ASFDirectory = "ArchiSteamFarm";
|
||||
|
||||
private static readonly string ExecutableDirectory = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
|
||||
|
||||
/// <summary>
|
||||
/// The main entry point for the application.
|
||||
/// </summary>
|
||||
[STAThread]
|
||||
private static void Main() {
|
||||
Init();
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
Application.Run(new MainForm());
|
||||
}
|
||||
|
||||
private static void Init() {
|
||||
AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionHandler;
|
||||
TaskScheduler.UnobservedTaskException += UnobservedTaskExceptionHandler;
|
||||
|
||||
Directory.SetCurrentDirectory(ExecutableDirectory);
|
||||
|
||||
// Allow loading configs from source tree if it's a debug build
|
||||
if (Debugging.IsDebugBuild) {
|
||||
|
||||
// Common structure is bin/(x64/)Debug/ArchiSteamFarm.exe, so we allow up to 4 directories up
|
||||
for (byte i = 0; i < 4; i++) {
|
||||
Directory.SetCurrentDirectory("..");
|
||||
if (Directory.Exists(ASFDirectory)) {
|
||||
Directory.SetCurrentDirectory(ASFDirectory);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If config directory doesn't exist after our adjustment, abort all of that
|
||||
if (!Directory.Exists(ConfigDirectory)) {
|
||||
Directory.SetCurrentDirectory(ExecutableDirectory);
|
||||
}
|
||||
}
|
||||
|
||||
if (!Directory.Exists(ConfigDirectory)) {
|
||||
Logging.LogGenericError("Config directory could not be found!");
|
||||
Environment.Exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
private static void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs args) {
|
||||
if (sender == null || args == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Logging.LogGenericException((Exception) args.ExceptionObject);
|
||||
}
|
||||
|
||||
private static void UnobservedTaskExceptionHandler(object sender, UnobservedTaskExceptionEventArgs args) {
|
||||
if (sender == null || args == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Logging.LogGenericException(args.Exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
36
ConfigGenerator/Properties/AssemblyInfo.cs
Normal file
36
ConfigGenerator/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("ConfigGenerator")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("ConfigGenerator")]
|
||||
[assembly: AssemblyCopyright("Copyright © ArchiSteamFarm 2015-2016")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("c3f6fe68-5e75-415e-bea1-1e7c16d6a433")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
62
ConfigGenerator/Properties/Resources.Designer.cs
generated
Normal file
62
ConfigGenerator/Properties/Resources.Designer.cs
generated
Normal file
@@ -0,0 +1,62 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace ConfigGenerator.Properties {
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resources {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if ((resourceMan == null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ConfigGenerator.Properties.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
117
ConfigGenerator/Properties/Resources.resx
Normal file
117
ConfigGenerator/Properties/Resources.resx
Normal file
@@ -0,0 +1,117 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
26
ConfigGenerator/Properties/Settings.Designer.cs
generated
Normal file
26
ConfigGenerator/Properties/Settings.Designer.cs
generated
Normal file
@@ -0,0 +1,26 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace ConfigGenerator.Properties {
|
||||
|
||||
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
|
||||
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
|
||||
|
||||
private static Settings defaultInstance = ((Settings) (global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||
|
||||
public static Settings Default {
|
||||
get {
|
||||
return defaultInstance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
7
ConfigGenerator/Properties/Settings.settings
Normal file
7
ConfigGenerator/Properties/Settings.settings
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
|
||||
<Profiles>
|
||||
<Profile Name="(Default)" />
|
||||
</Profiles>
|
||||
<Settings />
|
||||
</SettingsFile>
|
||||
97
ConfigGenerator/Tutorial.cs
Normal file
97
ConfigGenerator/Tutorial.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
_ _ _ ____ _ _____
|
||||
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
|
||||
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
|
||||
Contact: JustArchi@JustArchi.net
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
namespace ConfigGenerator {
|
||||
internal static class Tutorial {
|
||||
internal enum EPhase : byte {
|
||||
Unknown,
|
||||
Start,
|
||||
Shown,
|
||||
Help,
|
||||
HelpFinished,
|
||||
BotNickname,
|
||||
BotNicknameFinished,
|
||||
BotEnabled,
|
||||
BotReady,
|
||||
GlobalConfigOpened,
|
||||
GlobalConfigReady
|
||||
}
|
||||
|
||||
internal static bool Enabled { get; set; } = true;
|
||||
|
||||
private static EPhase NextPhase = EPhase.Start;
|
||||
|
||||
internal static void OnAction(EPhase phase) {
|
||||
if (!Enabled || phase != NextPhase) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (phase) {
|
||||
case EPhase.Unknown:
|
||||
break;
|
||||
case EPhase.Start:
|
||||
Logging.LogGenericInfo("Hello there! I noticed that you're using ASF Config Generator for the first time, so let me help you a bit.");
|
||||
break;
|
||||
case EPhase.Shown:
|
||||
Logging.LogGenericInfo("You can now notice the main ASF Config Generator screen, it's really easy to use!");
|
||||
Logging.LogGenericInfo("At the top of the window you can notice currently loaded configs, and 3 extra buttons for removing, renaming and adding new ones.");
|
||||
Logging.LogGenericInfo("In the middle of the window you will be able to configure all config properties that are available for you.");
|
||||
Logging.LogGenericInfo("In the top right corner you can find help button [?] which will redirect you to ASF wiki where you can find more information.");
|
||||
Logging.LogGenericInfo("Please click the help button to continue.");
|
||||
break;
|
||||
case EPhase.Help:
|
||||
Logging.LogGenericInfo("Well done! On ASF wiki you can find detailed help about every config property you're going to configure in a moment.");
|
||||
break;
|
||||
case EPhase.HelpFinished:
|
||||
Logging.LogGenericInfo("Alright, let's start configuring our ASF. Click on the plus [+] button to add your first steam account to ASF!");
|
||||
break;
|
||||
case EPhase.BotNickname:
|
||||
Logging.LogGenericInfo("Good job! You'll be asked for your bot name now. A good example would be a nickname that you're using for the steam account you're configuring right now, or any other name of your choice which will be easy for you to connect with bot instance that is being configured. Please don't use spaces in the name.");
|
||||
break;
|
||||
case EPhase.BotNicknameFinished:
|
||||
Logging.LogGenericInfo("As you can see your bot config is now ready to configure!");
|
||||
Logging.LogGenericInfo("First thing that you want to do is switching \"Enabled\" property from False to True, try it!");
|
||||
break;
|
||||
case EPhase.BotEnabled:
|
||||
Logging.LogGenericInfo("Excellent! Now your bot instance is enabled. You need to configure at least 2 more config properties - \"SteamLogin\" and \"SteamPassword\". The tutorial will continue after you're done with it. Remember to visit ASF wiki by clicking the help icon if you're unsure how given property should be configured!");
|
||||
break;
|
||||
case EPhase.BotReady:
|
||||
Logging.LogGenericInfo("If the data you put is proper, then your bot is ready to run! We need to do only one more thing now. Visit global ASF config, which is labelled as \"ASF\" on your config tab.");
|
||||
break;
|
||||
case EPhase.GlobalConfigOpened:
|
||||
Logging.LogGenericInfo("While bot config affects only given bot instance you're configuring, global config affects whole ASF process, including all configured bots.");
|
||||
Logging.LogGenericInfo("In order to fully configure your ASF, I suggest to fill \"SteamOwnerID\" property. Remember, if you don't know what to put, help button is always there for you!");
|
||||
break;
|
||||
case EPhase.GlobalConfigReady:
|
||||
Logging.LogGenericInfo("Your ASF is now ready! Simply launch ASF process by double-clicking ASF.exe binary and if you did everything properly, you should now notice that ASF logs in on your account and starts farming. If you have SteamGuard or 2FA authorization enabled, ASF will ask you for that once");
|
||||
Logging.LogGenericInfo("Congratulations! You've done everything that is needed in order to make ASF \"work\". I highly recommend reading the wiki now, as ASF offers some really neat features for you to configure, such as offline farming or deciding upon most efficient cards farming algorithm.");
|
||||
Logging.LogGenericInfo("If you'd like to add another steam account for farming, simply click the plus [+] button and add another instance. You can also rename bots [~] and remove them [-]. Good luck!");
|
||||
Enabled = false;
|
||||
break;
|
||||
}
|
||||
|
||||
NextPhase++;
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
ConfigGenerator/cirno.ico
Normal file
BIN
ConfigGenerator/cirno.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 361 KiB |
4
ConfigGenerator/packages.config
Normal file
4
ConfigGenerator/packages.config
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Newtonsoft.Json" version="8.0.3" targetFramework="net451" />
|
||||
</packages>
|
||||
6
GUI/App.config
Normal file
6
GUI/App.config
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.1"/>
|
||||
</startup>
|
||||
</configuration>
|
||||
35
GUI/Debugging.cs
Normal file
35
GUI/Debugging.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
_ _ _ ____ _ _____
|
||||
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
|
||||
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
|
||||
Contact: JustArchi@JustArchi.net
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
namespace GUI {
|
||||
internal static class Debugging {
|
||||
#if DEBUG
|
||||
internal static readonly bool IsDebugBuild = true;
|
||||
#else
|
||||
internal static readonly bool IsDebugBuild = false;
|
||||
#endif
|
||||
|
||||
internal static bool IsReleaseBuild => !IsDebugBuild;
|
||||
}
|
||||
}
|
||||
513
GUI/Form1.Designer.cs
generated
Normal file
513
GUI/Form1.Designer.cs
generated
Normal file
@@ -0,0 +1,513 @@
|
||||
namespace GUI {
|
||||
partial class Form1 {
|
||||
/// <summary>
|
||||
/// Erforderliche Designervariable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Verwendete Ressourcen bereinigen.
|
||||
/// </summary>
|
||||
/// <param name="disposing">True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False.</param>
|
||||
protected override void Dispose(bool disposing) {
|
||||
if (disposing && (components != null)) {
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Vom Windows Form-Designer generierter Code
|
||||
|
||||
/// <summary>
|
||||
/// Erforderliche Methode für die Designerunterstützung.
|
||||
/// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden.
|
||||
/// </summary>
|
||||
private void InitializeComponent() {
|
||||
this.components = new System.ComponentModel.Container();
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1));
|
||||
this.button5 = new System.Windows.Forms.Button();
|
||||
this.button4 = new System.Windows.Forms.Button();
|
||||
this.button3 = new System.Windows.Forms.Button();
|
||||
this.button2 = new System.Windows.Forms.Button();
|
||||
this.button1 = new System.Windows.Forms.Button();
|
||||
this.label2 = new System.Windows.Forms.Label();
|
||||
this.label1 = new System.Windows.Forms.Label();
|
||||
this.textBox2 = new System.Windows.Forms.TextBox();
|
||||
this.textBox1 = new System.Windows.Forms.TextBox();
|
||||
this.comboBox1 = new System.Windows.Forms.ComboBox();
|
||||
this.checkBox3 = new System.Windows.Forms.CheckBox();
|
||||
this.checkBox2 = new System.Windows.Forms.CheckBox();
|
||||
this.checkBox1 = new System.Windows.Forms.CheckBox();
|
||||
this.textBox3 = new System.Windows.Forms.TextBox();
|
||||
this.ASFGUI = new System.Windows.Forms.NotifyIcon(this.components);
|
||||
this.checkBox4 = new System.Windows.Forms.CheckBox();
|
||||
this.button6 = new System.Windows.Forms.Button();
|
||||
this.button7 = new System.Windows.Forms.Button();
|
||||
this.button8 = new System.Windows.Forms.Button();
|
||||
this.button9 = new System.Windows.Forms.Button();
|
||||
this.button10 = new System.Windows.Forms.Button();
|
||||
this.button11 = new System.Windows.Forms.Button();
|
||||
this.button12 = new System.Windows.Forms.Button();
|
||||
this.button13 = new System.Windows.Forms.Button();
|
||||
this.button14 = new System.Windows.Forms.Button();
|
||||
this.button15 = new System.Windows.Forms.Button();
|
||||
this.button16 = new System.Windows.Forms.Button();
|
||||
this.button17 = new System.Windows.Forms.Button();
|
||||
this.button18 = new System.Windows.Forms.Button();
|
||||
this.button19 = new System.Windows.Forms.Button();
|
||||
this.label3 = new System.Windows.Forms.Label();
|
||||
this.label4 = new System.Windows.Forms.Label();
|
||||
this.button20 = new System.Windows.Forms.Button();
|
||||
this.label5 = new System.Windows.Forms.Label();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// button5
|
||||
//
|
||||
this.button5.Location = new System.Drawing.Point(86, 204);
|
||||
this.button5.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.button5.Name = "button5";
|
||||
this.button5.Size = new System.Drawing.Size(56, 19);
|
||||
this.button5.TabIndex = 27;
|
||||
this.button5.Text = "2fa ok";
|
||||
this.button5.UseVisualStyleBackColor = true;
|
||||
this.button5.Click += new System.EventHandler(this.button5_Click);
|
||||
//
|
||||
// button4
|
||||
//
|
||||
this.button4.Location = new System.Drawing.Point(26, 204);
|
||||
this.button4.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.button4.Name = "button4";
|
||||
this.button4.Size = new System.Drawing.Size(56, 19);
|
||||
this.button4.TabIndex = 26;
|
||||
this.button4.Text = "2fa code";
|
||||
this.button4.UseVisualStyleBackColor = true;
|
||||
this.button4.Click += new System.EventHandler(this.button4_Click);
|
||||
//
|
||||
// button3
|
||||
//
|
||||
this.button3.Location = new System.Drawing.Point(26, 286);
|
||||
this.button3.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.button3.Name = "button3";
|
||||
this.button3.Size = new System.Drawing.Size(56, 19);
|
||||
this.button3.TabIndex = 24;
|
||||
this.button3.Text = "Redeem";
|
||||
this.button3.UseVisualStyleBackColor = true;
|
||||
this.button3.Click += new System.EventHandler(this.button3_Click);
|
||||
//
|
||||
// button2
|
||||
//
|
||||
this.button2.Location = new System.Drawing.Point(26, 171);
|
||||
this.button2.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.button2.Name = "button2";
|
||||
this.button2.Size = new System.Drawing.Size(56, 19);
|
||||
this.button2.TabIndex = 23;
|
||||
this.button2.Text = "Loot";
|
||||
this.button2.UseVisualStyleBackColor = true;
|
||||
this.button2.Click += new System.EventHandler(this.button2_Click);
|
||||
//
|
||||
// button1
|
||||
//
|
||||
this.button1.Location = new System.Drawing.Point(169, 130);
|
||||
this.button1.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.button1.Name = "button1";
|
||||
this.button1.Size = new System.Drawing.Size(56, 19);
|
||||
this.button1.TabIndex = 22;
|
||||
this.button1.Text = "Send";
|
||||
this.button1.UseVisualStyleBackColor = true;
|
||||
this.button1.Click += new System.EventHandler(this.button1_Click);
|
||||
//
|
||||
// label2
|
||||
//
|
||||
this.label2.AutoSize = true;
|
||||
this.label2.Location = new System.Drawing.Point(579, 13);
|
||||
this.label2.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.label2.Name = "label2";
|
||||
this.label2.Size = new System.Drawing.Size(39, 13);
|
||||
this.label2.TabIndex = 21;
|
||||
this.label2.Text = "Output";
|
||||
//
|
||||
// label1
|
||||
//
|
||||
this.label1.AutoSize = true;
|
||||
this.label1.Location = new System.Drawing.Point(258, 39);
|
||||
this.label1.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.label1.Name = "label1";
|
||||
this.label1.Size = new System.Drawing.Size(31, 13);
|
||||
this.label1.TabIndex = 20;
|
||||
this.label1.Text = "Input";
|
||||
//
|
||||
// textBox2
|
||||
//
|
||||
this.textBox2.Location = new System.Drawing.Point(413, 34);
|
||||
this.textBox2.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.textBox2.Multiline = true;
|
||||
this.textBox2.Name = "textBox2";
|
||||
this.textBox2.ReadOnly = true;
|
||||
this.textBox2.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
|
||||
this.textBox2.Size = new System.Drawing.Size(432, 370);
|
||||
this.textBox2.TabIndex = 19;
|
||||
//
|
||||
// textBox1
|
||||
//
|
||||
this.textBox1.Location = new System.Drawing.Point(169, 61);
|
||||
this.textBox1.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.textBox1.Multiline = true;
|
||||
this.textBox1.Name = "textBox1";
|
||||
this.textBox1.Size = new System.Drawing.Size(223, 65);
|
||||
this.textBox1.TabIndex = 18;
|
||||
//
|
||||
// comboBox1
|
||||
//
|
||||
this.comboBox1.FormattingEnabled = true;
|
||||
this.comboBox1.Location = new System.Drawing.Point(26, 116);
|
||||
this.comboBox1.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.comboBox1.Name = "comboBox1";
|
||||
this.comboBox1.Size = new System.Drawing.Size(92, 21);
|
||||
this.comboBox1.TabIndex = 17;
|
||||
this.comboBox1.SelectedIndexChanged += new System.EventHandler(this.comboBox1_SelectedIndexChanged);
|
||||
//
|
||||
// checkBox3
|
||||
//
|
||||
this.checkBox3.AutoSize = true;
|
||||
this.checkBox3.Location = new System.Drawing.Point(26, 94);
|
||||
this.checkBox3.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.checkBox3.Name = "checkBox3";
|
||||
this.checkBox3.Size = new System.Drawing.Size(102, 17);
|
||||
this.checkBox3.TabIndex = 16;
|
||||
this.checkBox3.Text = "Send to specific";
|
||||
this.checkBox3.UseVisualStyleBackColor = true;
|
||||
this.checkBox3.CheckedChanged += new System.EventHandler(this.checkBox3_CheckedChanged);
|
||||
//
|
||||
// checkBox2
|
||||
//
|
||||
this.checkBox2.AutoSize = true;
|
||||
this.checkBox2.Location = new System.Drawing.Point(26, 72);
|
||||
this.checkBox2.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.checkBox2.Name = "checkBox2";
|
||||
this.checkBox2.Size = new System.Drawing.Size(100, 17);
|
||||
this.checkBox2.TabIndex = 15;
|
||||
this.checkBox2.Text = "Send to all Bots";
|
||||
this.checkBox2.UseVisualStyleBackColor = true;
|
||||
this.checkBox2.CheckedChanged += new System.EventHandler(this.checkBox2_CheckedChanged);
|
||||
//
|
||||
// checkBox1
|
||||
//
|
||||
this.checkBox1.AutoSize = true;
|
||||
this.checkBox1.Location = new System.Drawing.Point(107, 11);
|
||||
this.checkBox1.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.checkBox1.Name = "checkBox1";
|
||||
this.checkBox1.Size = new System.Drawing.Size(74, 17);
|
||||
this.checkBox1.TabIndex = 14;
|
||||
this.checkBox1.Text = "Safemode";
|
||||
this.checkBox1.UseVisualStyleBackColor = true;
|
||||
this.checkBox1.CheckedChanged += new System.EventHandler(this.checkBox1_CheckedChanged);
|
||||
//
|
||||
// textBox3
|
||||
//
|
||||
this.textBox3.Location = new System.Drawing.Point(26, 387);
|
||||
this.textBox3.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.textBox3.Name = "textBox3";
|
||||
this.textBox3.Size = new System.Drawing.Size(366, 20);
|
||||
this.textBox3.TabIndex = 28;
|
||||
this.textBox3.TextChanged += new System.EventHandler(this.textBox3_TextChanged);
|
||||
//
|
||||
// ASFGUI
|
||||
//
|
||||
this.ASFGUI.Text = "notifyIcon1";
|
||||
this.ASFGUI.Visible = true;
|
||||
this.ASFGUI.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.ASFGUI_MouseDoubleClick);
|
||||
//
|
||||
// checkBox4
|
||||
//
|
||||
this.checkBox4.AutoSize = true;
|
||||
this.checkBox4.Location = new System.Drawing.Point(26, 11);
|
||||
this.checkBox4.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.checkBox4.Name = "checkBox4";
|
||||
this.checkBox4.Size = new System.Drawing.Size(83, 17);
|
||||
this.checkBox4.TabIndex = 29;
|
||||
this.checkBox4.Text = "Send to any";
|
||||
this.checkBox4.UseVisualStyleBackColor = true;
|
||||
this.checkBox4.CheckedChanged += new System.EventHandler(this.checkBox4_CheckedChanged);
|
||||
//
|
||||
// button6
|
||||
//
|
||||
this.button6.Location = new System.Drawing.Point(26, 41);
|
||||
this.button6.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.button6.Name = "button6";
|
||||
this.button6.Size = new System.Drawing.Size(94, 27);
|
||||
this.button6.TabIndex = 30;
|
||||
this.button6.Text = "generate Botlist";
|
||||
this.button6.UseVisualStyleBackColor = true;
|
||||
this.button6.Click += new System.EventHandler(this.button6_Click);
|
||||
//
|
||||
// button7
|
||||
//
|
||||
this.button7.Location = new System.Drawing.Point(147, 204);
|
||||
this.button7.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.button7.Name = "button7";
|
||||
this.button7.Size = new System.Drawing.Size(56, 19);
|
||||
this.button7.TabIndex = 31;
|
||||
this.button7.Text = "2fano";
|
||||
this.button7.UseVisualStyleBackColor = true;
|
||||
this.button7.Click += new System.EventHandler(this.button7_Click);
|
||||
//
|
||||
// button8
|
||||
//
|
||||
this.button8.Location = new System.Drawing.Point(87, 286);
|
||||
this.button8.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.button8.Name = "button8";
|
||||
this.button8.Size = new System.Drawing.Size(56, 19);
|
||||
this.button8.TabIndex = 32;
|
||||
this.button8.Text = "2faoff";
|
||||
this.button8.UseVisualStyleBackColor = true;
|
||||
this.button8.Click += new System.EventHandler(this.button8_Click);
|
||||
//
|
||||
// button9
|
||||
//
|
||||
this.button9.Location = new System.Drawing.Point(148, 286);
|
||||
this.button9.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.button9.Name = "button9";
|
||||
this.button9.Size = new System.Drawing.Size(56, 19);
|
||||
this.button9.TabIndex = 33;
|
||||
this.button9.Text = "exit";
|
||||
this.button9.UseVisualStyleBackColor = true;
|
||||
this.button9.Click += new System.EventHandler(this.button9_Click);
|
||||
//
|
||||
// button10
|
||||
//
|
||||
this.button10.Location = new System.Drawing.Point(86, 171);
|
||||
this.button10.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.button10.Name = "button10";
|
||||
this.button10.Size = new System.Drawing.Size(56, 19);
|
||||
this.button10.TabIndex = 34;
|
||||
this.button10.Text = "farm";
|
||||
this.button10.UseVisualStyleBackColor = true;
|
||||
this.button10.Click += new System.EventHandler(this.button10_Click);
|
||||
//
|
||||
// button11
|
||||
//
|
||||
this.button11.Location = new System.Drawing.Point(147, 171);
|
||||
this.button11.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.button11.Name = "button11";
|
||||
this.button11.Size = new System.Drawing.Size(56, 19);
|
||||
this.button11.TabIndex = 35;
|
||||
this.button11.Text = "help";
|
||||
this.button11.UseVisualStyleBackColor = true;
|
||||
this.button11.Click += new System.EventHandler(this.button11_Click);
|
||||
//
|
||||
// button12
|
||||
//
|
||||
this.button12.Location = new System.Drawing.Point(208, 171);
|
||||
this.button12.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.button12.Name = "button12";
|
||||
this.button12.Size = new System.Drawing.Size(56, 19);
|
||||
this.button12.TabIndex = 36;
|
||||
this.button12.Text = "start";
|
||||
this.button12.UseVisualStyleBackColor = true;
|
||||
this.button12.Click += new System.EventHandler(this.button12_Click);
|
||||
//
|
||||
// button13
|
||||
//
|
||||
this.button13.Location = new System.Drawing.Point(268, 171);
|
||||
this.button13.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.button13.Name = "button13";
|
||||
this.button13.Size = new System.Drawing.Size(56, 19);
|
||||
this.button13.TabIndex = 37;
|
||||
this.button13.Text = "stop";
|
||||
this.button13.UseVisualStyleBackColor = true;
|
||||
this.button13.Click += new System.EventHandler(this.button13_Click);
|
||||
//
|
||||
// button14
|
||||
//
|
||||
this.button14.Location = new System.Drawing.Point(329, 171);
|
||||
this.button14.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.button14.Name = "button14";
|
||||
this.button14.Size = new System.Drawing.Size(56, 19);
|
||||
this.button14.TabIndex = 38;
|
||||
this.button14.Text = "pause";
|
||||
this.button14.UseVisualStyleBackColor = true;
|
||||
this.button14.Click += new System.EventHandler(this.button14_Click);
|
||||
//
|
||||
// button15
|
||||
//
|
||||
this.button15.Location = new System.Drawing.Point(268, 204);
|
||||
this.button15.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.button15.Name = "button15";
|
||||
this.button15.Size = new System.Drawing.Size(56, 19);
|
||||
this.button15.TabIndex = 39;
|
||||
this.button15.Text = "status";
|
||||
this.button15.UseVisualStyleBackColor = true;
|
||||
this.button15.Click += new System.EventHandler(this.button15_Click);
|
||||
//
|
||||
// button16
|
||||
//
|
||||
this.button16.Location = new System.Drawing.Point(329, 204);
|
||||
this.button16.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.button16.Name = "button16";
|
||||
this.button16.Size = new System.Drawing.Size(56, 19);
|
||||
this.button16.TabIndex = 40;
|
||||
this.button16.Text = "status all";
|
||||
this.button16.UseVisualStyleBackColor = true;
|
||||
this.button16.Click += new System.EventHandler(this.button16_Click);
|
||||
//
|
||||
// button17
|
||||
//
|
||||
this.button17.Location = new System.Drawing.Point(26, 318);
|
||||
this.button17.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.button17.Name = "button17";
|
||||
this.button17.Size = new System.Drawing.Size(56, 19);
|
||||
this.button17.TabIndex = 41;
|
||||
this.button17.Text = "owns";
|
||||
this.button17.UseVisualStyleBackColor = true;
|
||||
this.button17.Click += new System.EventHandler(this.button17_Click);
|
||||
//
|
||||
// button18
|
||||
//
|
||||
this.button18.Location = new System.Drawing.Point(147, 318);
|
||||
this.button18.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.button18.Name = "button18";
|
||||
this.button18.Size = new System.Drawing.Size(56, 19);
|
||||
this.button18.TabIndex = 42;
|
||||
this.button18.Text = "addlicense";
|
||||
this.button18.UseVisualStyleBackColor = true;
|
||||
this.button18.Click += new System.EventHandler(this.button18_Click);
|
||||
//
|
||||
// button19
|
||||
//
|
||||
this.button19.Location = new System.Drawing.Point(87, 318);
|
||||
this.button19.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.button19.Name = "button19";
|
||||
this.button19.Size = new System.Drawing.Size(56, 19);
|
||||
this.button19.TabIndex = 43;
|
||||
this.button19.Text = "play";
|
||||
this.button19.UseVisualStyleBackColor = true;
|
||||
this.button19.Click += new System.EventHandler(this.button19_Click);
|
||||
//
|
||||
// label3
|
||||
//
|
||||
this.label3.AutoSize = true;
|
||||
this.label3.Location = new System.Drawing.Point(24, 245);
|
||||
this.label3.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.label3.Name = "label3";
|
||||
this.label3.Size = new System.Drawing.Size(235, 13);
|
||||
this.label3.TabIndex = 44;
|
||||
this.label3.Text = "The following do not work with \"Send to all\" and";
|
||||
//
|
||||
// label4
|
||||
//
|
||||
this.label4.AutoSize = true;
|
||||
this.label4.Location = new System.Drawing.Point(24, 259);
|
||||
this.label4.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.label4.Name = "label4";
|
||||
this.label4.Size = new System.Drawing.Size(224, 13);
|
||||
this.label4.TabIndex = 45;
|
||||
this.label4.Text = "require confirmation even without \"Safemode\"";
|
||||
//
|
||||
// button20
|
||||
//
|
||||
this.button20.Location = new System.Drawing.Point(231, 130);
|
||||
this.button20.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.button20.Name = "button20";
|
||||
this.button20.Size = new System.Drawing.Size(56, 19);
|
||||
this.button20.TabIndex = 46;
|
||||
this.button20.Text = "clear";
|
||||
this.button20.UseVisualStyleBackColor = true;
|
||||
this.button20.Click += new System.EventHandler(this.button20_Click);
|
||||
//
|
||||
// label5
|
||||
//
|
||||
this.label5.AutoSize = true;
|
||||
this.label5.Location = new System.Drawing.Point(29, 370);
|
||||
this.label5.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.label5.Name = "label5";
|
||||
this.label5.Size = new System.Drawing.Size(221, 13);
|
||||
this.label5.TabIndex = 47;
|
||||
this.label5.Text = "If you don\'t know what this is... Don\'t touch it!";
|
||||
//
|
||||
// Form1
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(854, 414);
|
||||
this.Controls.Add(this.label5);
|
||||
this.Controls.Add(this.button20);
|
||||
this.Controls.Add(this.label4);
|
||||
this.Controls.Add(this.label3);
|
||||
this.Controls.Add(this.button19);
|
||||
this.Controls.Add(this.button18);
|
||||
this.Controls.Add(this.button17);
|
||||
this.Controls.Add(this.button16);
|
||||
this.Controls.Add(this.button15);
|
||||
this.Controls.Add(this.button14);
|
||||
this.Controls.Add(this.button13);
|
||||
this.Controls.Add(this.button12);
|
||||
this.Controls.Add(this.button11);
|
||||
this.Controls.Add(this.button10);
|
||||
this.Controls.Add(this.button9);
|
||||
this.Controls.Add(this.button8);
|
||||
this.Controls.Add(this.button7);
|
||||
this.Controls.Add(this.button6);
|
||||
this.Controls.Add(this.checkBox4);
|
||||
this.Controls.Add(this.textBox3);
|
||||
this.Controls.Add(this.button5);
|
||||
this.Controls.Add(this.button4);
|
||||
this.Controls.Add(this.button3);
|
||||
this.Controls.Add(this.button2);
|
||||
this.Controls.Add(this.button1);
|
||||
this.Controls.Add(this.label2);
|
||||
this.Controls.Add(this.label1);
|
||||
this.Controls.Add(this.textBox2);
|
||||
this.Controls.Add(this.textBox1);
|
||||
this.Controls.Add(this.comboBox1);
|
||||
this.Controls.Add(this.checkBox3);
|
||||
this.Controls.Add(this.checkBox2);
|
||||
this.Controls.Add(this.checkBox1);
|
||||
this.Icon = ((System.Drawing.Icon) (resources.GetObject("$this.Icon")));
|
||||
this.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.Name = "Form1";
|
||||
this.Text = "Form1";
|
||||
this.Load += new System.EventHandler(this.Form1_Load);
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.Button button5;
|
||||
private System.Windows.Forms.Button button4;
|
||||
private System.Windows.Forms.Button button3;
|
||||
private System.Windows.Forms.Button button2;
|
||||
private System.Windows.Forms.Button button1;
|
||||
private System.Windows.Forms.Label label2;
|
||||
private System.Windows.Forms.Label label1;
|
||||
private System.Windows.Forms.TextBox textBox2;
|
||||
private System.Windows.Forms.TextBox textBox1;
|
||||
private System.Windows.Forms.ComboBox comboBox1;
|
||||
private System.Windows.Forms.CheckBox checkBox3;
|
||||
private System.Windows.Forms.CheckBox checkBox2;
|
||||
private System.Windows.Forms.CheckBox checkBox1;
|
||||
private System.Windows.Forms.TextBox textBox3;
|
||||
private System.Windows.Forms.NotifyIcon ASFGUI;
|
||||
private System.Windows.Forms.CheckBox checkBox4;
|
||||
private System.Windows.Forms.Button button6;
|
||||
private System.Windows.Forms.Button button7;
|
||||
private System.Windows.Forms.Button button8;
|
||||
private System.Windows.Forms.Button button9;
|
||||
private System.Windows.Forms.Button button10;
|
||||
private System.Windows.Forms.Button button11;
|
||||
private System.Windows.Forms.Button button12;
|
||||
private System.Windows.Forms.Button button13;
|
||||
private System.Windows.Forms.Button button14;
|
||||
private System.Windows.Forms.Button button15;
|
||||
private System.Windows.Forms.Button button16;
|
||||
private System.Windows.Forms.Button button17;
|
||||
private System.Windows.Forms.Button button18;
|
||||
private System.Windows.Forms.Button button19;
|
||||
private System.Windows.Forms.Label label3;
|
||||
private System.Windows.Forms.Label label4;
|
||||
private System.Windows.Forms.Button button20;
|
||||
private System.Windows.Forms.Label label5;
|
||||
}
|
||||
}
|
||||
|
||||
310
GUI/Form1.cs
Normal file
310
GUI/Form1.cs
Normal file
@@ -0,0 +1,310 @@
|
||||
/*
|
||||
_ _ _ ____ _ _____
|
||||
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
|
||||
Copyright 2015-2016 Florian "KlappPC" Lang
|
||||
Contact: ichhoeremusik@gmx.net
|
||||
|
||||
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
|
||||
Contact: JustArchi@JustArchi.net
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
using System.ServiceModel;
|
||||
using System.IO;
|
||||
|
||||
namespace GUI {
|
||||
|
||||
public partial class Form1 : Form {
|
||||
private bool safeMode = false;
|
||||
private bool sendAll = false;
|
||||
private bool sendAny = true;
|
||||
private bool sendOne = false;
|
||||
private string botName = "";
|
||||
string[] botList;
|
||||
private string URL = "";
|
||||
ServerProcess proc;
|
||||
private Client Client;
|
||||
public Form1() {
|
||||
InitializeComponent();
|
||||
|
||||
// So either the ASF.exe is in the same directory, or we assume development environment.
|
||||
string ASF = "ASF.exe";
|
||||
if (!File.Exists(ASF)) {
|
||||
ASF = "../../../ArchiSteamFarm/bin/" + (Debugging.IsDebugBuild ? "Debug" : "Release") + "/ArchiSteamFarm.exe";
|
||||
if (!File.Exists(ASF)) {
|
||||
Logging.LogGenericError("ASF binary could not be found!");
|
||||
Environment.Exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
proc = new ServerProcess(ASF, "--server", textBox2);
|
||||
proc.Start();
|
||||
}
|
||||
|
||||
|
||||
protected override void OnFormClosing(FormClosingEventArgs e) {
|
||||
proc.Stop();
|
||||
base.OnFormClosing(e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a single command. can be lead by a ! but it does not have to.
|
||||
*/
|
||||
private string sendCommand(string command) {
|
||||
if (command.StartsWith("!")) {
|
||||
command = command.Substring(1);
|
||||
}
|
||||
if (Client == null) {
|
||||
Client = new Client(new BasicHttpBinding(), new EndpointAddress(URL));
|
||||
}
|
||||
return Client.HandleCommand(command);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maximize again when double clicked on tray icon
|
||||
*/
|
||||
private void ASFGUI_MouseDoubleClick(object sender, MouseEventArgs e) {
|
||||
this.Show();
|
||||
this.WindowState = FormWindowState.Normal;
|
||||
}
|
||||
|
||||
private void Form1_Load(object sender, System.EventArgs e) {
|
||||
this.Resize += new System.EventHandler(this.Form1_Resize);
|
||||
textBox2.ScrollBars = ScrollBars.Vertical;
|
||||
textBox1.ScrollBars = ScrollBars.Vertical;
|
||||
checkBox4.Checked = true;
|
||||
textBox3.Text = "http://localhost:1242/ASF";
|
||||
URL = "http://localhost:1242/ASF";
|
||||
textBox2.Anchor = (AnchorStyles.Right | AnchorStyles.Left);
|
||||
}
|
||||
/**
|
||||
* Minimize to tray instead of taskbar
|
||||
*/
|
||||
private void Form1_Resize(object sender, EventArgs e) {
|
||||
if (FormWindowState.Minimized == this.WindowState) {
|
||||
ASFGUI.Visible = true;
|
||||
this.Hide();
|
||||
} else if (FormWindowState.Normal == this.WindowState) {
|
||||
ASFGUI.Visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* generate a command from a simple command
|
||||
* That means, adds a botName or makes multiple commands for multiple bots.
|
||||
*/
|
||||
private string generateCommand(string command, string arg = "") {
|
||||
if (sendOne)
|
||||
return command + " " + botName + " " + arg;
|
||||
if (sendAll) {
|
||||
string ret = "";
|
||||
foreach (string str in botList) {
|
||||
ret = ret + command + " " + str + " " + arg + "\r\n";
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
return command + arg;
|
||||
}
|
||||
/**
|
||||
* One of the simple buttons got pressed
|
||||
*/
|
||||
private void buttonPressed(string command) {
|
||||
textBox1.Text = generateCommand(command);
|
||||
if (!safeMode)
|
||||
button1_Click(this, null);
|
||||
}
|
||||
/**
|
||||
* One of the complicated buttons was pressed
|
||||
* We get an argumentlist
|
||||
*/
|
||||
private void multiCommand(string command) {
|
||||
if (sendAll)
|
||||
return;
|
||||
string[] arr = textBox1.Lines;
|
||||
string cmd = "";
|
||||
for (int i = 0; i < arr.Length; i++) {
|
||||
if (!String.IsNullOrEmpty(arr[i].Trim())) {
|
||||
cmd = cmd + generateCommand(command, arr[i].Trim()) + "\r\n";
|
||||
}
|
||||
}
|
||||
textBox1.Text = cmd;
|
||||
}
|
||||
|
||||
/**
|
||||
* updates the WCF URL in case of custom URL
|
||||
*/
|
||||
private void textBox3_TextChanged(object sender, EventArgs e) {
|
||||
URL = textBox3.Text;
|
||||
}
|
||||
|
||||
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e) {
|
||||
botName = comboBox1.SelectedItem.ToString();
|
||||
}
|
||||
//Ok, radiobuttons would have been better I guess, to lazy to change now.
|
||||
private void checkBox3_CheckedChanged(object sender, EventArgs e) {
|
||||
//specific
|
||||
if (checkBox3.Checked) {
|
||||
sendAll = false;
|
||||
sendAny = false;
|
||||
sendOne = true;
|
||||
checkBox2.Checked = false;
|
||||
checkBox4.Checked = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void checkBox2_CheckedChanged(object sender, EventArgs e) {
|
||||
//all
|
||||
if (checkBox2.Checked) {
|
||||
sendAll = true;
|
||||
sendAny = false;
|
||||
sendOne = false;
|
||||
checkBox3.Checked = false;
|
||||
checkBox4.Checked = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void checkBox4_CheckedChanged(object sender, EventArgs e) {
|
||||
//any
|
||||
if (checkBox4.Checked) {
|
||||
sendAll = false;
|
||||
sendAny = true;
|
||||
sendOne = false;
|
||||
checkBox2.Checked = false;
|
||||
checkBox3.Checked = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void checkBox1_CheckedChanged(object sender, EventArgs e) {
|
||||
safeMode = checkBox1.Checked;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send command button.
|
||||
*/
|
||||
private void button1_Click(object sender, System.EventArgs e) {
|
||||
for (int i = 0; i < textBox1.Lines.Length; i++) {
|
||||
string command = textBox1.Lines[i];
|
||||
if (!String.IsNullOrEmpty(command.Trim())) {
|
||||
sendCommand(command);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* Update /Generate Botlist button
|
||||
*/
|
||||
private void button6_Click(object sender, EventArgs e) {
|
||||
string ret = sendCommand("statusall");
|
||||
string[] arr = ret.Split('\n');
|
||||
int botAmount = Convert.ToInt16(arr[arr.Length - 1].Split('/')[1].Trim().Split(' ')[0]);
|
||||
botList = new string[botAmount];
|
||||
for (int i = 0; i < botAmount; i++) {
|
||||
botList[i] = arr[arr.Length - 2 - i].Substring(3).Trim().Split(' ')[0];
|
||||
}
|
||||
comboBox1.Items.AddRange(botList);
|
||||
}
|
||||
|
||||
//The Rest are simple buttons.
|
||||
private void button3_Click(object sender, EventArgs e) {
|
||||
multiCommand("redeem");
|
||||
}
|
||||
private void button2_Click(object sender, EventArgs e) {
|
||||
textBox1.Text = generateCommand("loot");
|
||||
if (!safeMode)
|
||||
button1_Click(this, null);
|
||||
}
|
||||
private void button4_Click(object sender, EventArgs e) {
|
||||
//2fa
|
||||
textBox1.Text = generateCommand("2fa");
|
||||
if (!safeMode)
|
||||
button1_Click(this, null);
|
||||
}
|
||||
private void button5_Click(object sender, EventArgs e) {
|
||||
buttonPressed("2faok");
|
||||
}
|
||||
private void button7_Click(object sender, EventArgs e) {
|
||||
buttonPressed("2fano");
|
||||
}
|
||||
private void button8_Click(object sender, EventArgs e) {
|
||||
if (sendAll)
|
||||
return;
|
||||
textBox1.Text = generateCommand("2faoff");
|
||||
}
|
||||
private void button9_Click(object sender, EventArgs e) {
|
||||
textBox1.Text = "exit";
|
||||
}
|
||||
private void button10_Click(object sender, EventArgs e) {
|
||||
buttonPressed("farm");
|
||||
}
|
||||
private void button11_Click(object sender, EventArgs e) {
|
||||
buttonPressed("help");
|
||||
}
|
||||
private void button12_Click(object sender, EventArgs e) {
|
||||
buttonPressed("start");
|
||||
}
|
||||
private void button13_Click(object sender, EventArgs e) {
|
||||
buttonPressed("stop");
|
||||
}
|
||||
private void button14_Click(object sender, EventArgs e) {
|
||||
buttonPressed("pause");
|
||||
}
|
||||
private void button15_Click(object sender, EventArgs e) {
|
||||
buttonPressed("status");
|
||||
}
|
||||
private void button16_Click(object sender, EventArgs e) {
|
||||
textBox1.Text = "statusall";
|
||||
if (!safeMode)
|
||||
button1_Click(this, null);
|
||||
}
|
||||
private void button17_Click(object sender, EventArgs e) {
|
||||
multiCommand("owns");
|
||||
}
|
||||
private void button18_Click(object sender, EventArgs e) {
|
||||
multiCommand("addlicense");
|
||||
}
|
||||
private void button19_Click(object sender, EventArgs e) {
|
||||
multiCommand("play");
|
||||
}
|
||||
private void button20_Click(object sender, EventArgs e) {
|
||||
textBox1.Text = "";
|
||||
}
|
||||
}
|
||||
|
||||
//############### After this point copied from Archie's WCF ###################
|
||||
[ServiceContract]
|
||||
interface IWCF {
|
||||
[OperationContract]
|
||||
string HandleCommand(string input);
|
||||
}
|
||||
class Client : ClientBase<IWCF>, IWCF {
|
||||
internal Client(System.ServiceModel.Channels.Binding binding, EndpointAddress address) : base(binding, address) { }
|
||||
|
||||
public string HandleCommand(string input) {
|
||||
try {
|
||||
return Channel.HandleCommand(input);
|
||||
} catch (Exception e) {
|
||||
//Logging.LogGenericException(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
6296
GUI/Form1.resx
Normal file
6296
GUI/Form1.resx
Normal file
File diff suppressed because it is too large
Load Diff
84
GUI/Form2.Designer.cs
generated
Normal file
84
GUI/Form2.Designer.cs
generated
Normal file
@@ -0,0 +1,84 @@
|
||||
namespace GUI {
|
||||
partial class Form2 {
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing) {
|
||||
if (disposing && (components != null)) {
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent() {
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form2));
|
||||
this.button1 = new System.Windows.Forms.Button();
|
||||
this.textBox1 = new System.Windows.Forms.TextBox();
|
||||
this.label1 = new System.Windows.Forms.Label();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// button1
|
||||
//
|
||||
this.button1.Location = new System.Drawing.Point(134, 93);
|
||||
this.button1.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.button1.Name = "button1";
|
||||
this.button1.Size = new System.Drawing.Size(87, 28);
|
||||
this.button1.TabIndex = 0;
|
||||
this.button1.Text = "OK";
|
||||
this.button1.UseVisualStyleBackColor = true;
|
||||
this.button1.Click += new System.EventHandler(this.button1_Click);
|
||||
//
|
||||
// textBox1
|
||||
//
|
||||
this.textBox1.Location = new System.Drawing.Point(12, 63);
|
||||
this.textBox1.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.textBox1.Name = "textBox1";
|
||||
this.textBox1.Size = new System.Drawing.Size(336, 20);
|
||||
this.textBox1.TabIndex = 1;
|
||||
//
|
||||
// label1
|
||||
//
|
||||
this.label1.AutoSize = true;
|
||||
this.label1.Location = new System.Drawing.Point(10, 7);
|
||||
this.label1.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||
this.label1.Name = "label1";
|
||||
this.label1.Size = new System.Drawing.Size(35, 13);
|
||||
this.label1.TabIndex = 2;
|
||||
this.label1.Text = "label1";
|
||||
//
|
||||
// Form2
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(356, 132);
|
||||
this.Controls.Add(this.label1);
|
||||
this.Controls.Add(this.textBox1);
|
||||
this.Controls.Add(this.button1);
|
||||
this.Icon = ((System.Drawing.Icon) (resources.GetObject("$this.Icon")));
|
||||
this.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
|
||||
this.Name = "Form2";
|
||||
this.Text = "Input";
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.Button button1;
|
||||
private System.Windows.Forms.TextBox textBox1;
|
||||
private System.Windows.Forms.Label label1;
|
||||
}
|
||||
}
|
||||
44
GUI/Form2.cs
Normal file
44
GUI/Form2.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
_ _ _ ____ _ _____
|
||||
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
|
||||
Copyright 2015-2016 Florian "KlappPC" Lang
|
||||
Contact: ichhoeremusik@gmx.net
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace GUI {
|
||||
/**
|
||||
* popup Message when Input is required
|
||||
*/
|
||||
public partial class Form2 : Form {
|
||||
ServerProcess proc;
|
||||
public Form2(ServerProcess proc, string msg) {
|
||||
this.proc = proc;
|
||||
InitializeComponent();
|
||||
label1.Text = msg;
|
||||
button1.DialogResult = DialogResult.OK;
|
||||
}
|
||||
|
||||
private void button1_Click(object sender, EventArgs e) {
|
||||
proc.Write(textBox1.Text);
|
||||
}
|
||||
}
|
||||
}
|
||||
6293
GUI/Form2.resx
Normal file
6293
GUI/Form2.resx
Normal file
File diff suppressed because it is too large
Load Diff
156
GUI/GUI.csproj
Normal file
156
GUI/GUI.csproj
Normal file
@@ -0,0 +1,156 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{599121A9-5887-4522-A3D6-61470B90BAD4}</ProjectGuid>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>GUI</RootNamespace>
|
||||
<AssemblyName>GUI</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<PublishUrl>publish\</PublishUrl>
|
||||
<Install>true</Install>
|
||||
<InstallFrom>Disk</InstallFrom>
|
||||
<UpdateEnabled>false</UpdateEnabled>
|
||||
<UpdateMode>Foreground</UpdateMode>
|
||||
<UpdateInterval>7</UpdateInterval>
|
||||
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
|
||||
<UpdatePeriodically>false</UpdatePeriodically>
|
||||
<UpdateRequired>false</UpdateRequired>
|
||||
<MapFileExtensions>true</MapFileExtensions>
|
||||
<ApplicationRevision>0</ApplicationRevision>
|
||||
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
|
||||
<IsWebBootstrapper>false</IsWebBootstrapper>
|
||||
<UseApplicationTrust>false</UseApplicationTrust>
|
||||
<BootstrapperEnabled>true</BootstrapperEnabled>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>none</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>
|
||||
</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ApplicationIcon>cirno.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.ServiceModel" />
|
||||
<Reference Include="System.ServiceModel.Web" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Debugging.cs" />
|
||||
<Compile Include="Form1.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Form1.Designer.cs">
|
||||
<DependentUpon>Form1.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Form2.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Form2.Designer.cs">
|
||||
<DependentUpon>Form2.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Logging.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="ServerProcess.cs" />
|
||||
<EmbeddedResource Include="Form1.resx">
|
||||
<DependentUpon>Form1.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Form2.resx">
|
||||
<DependentUpon>Form2.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
<SubType>Designer</SubType>
|
||||
</EmbeddedResource>
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
<DesignTime>True</DesignTime>
|
||||
</Compile>
|
||||
<None Include="app.manifest" />
|
||||
<None Include="Properties\Settings.settings">
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
</None>
|
||||
<Compile Include="Properties\Settings.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<WCFMetadata Include="Service References\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<BootstrapperPackage Include=".NETFramework,Version=v4.5">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>Microsoft .NET Framework 4.5 %28x86 and x64%29</ProductName>
|
||||
<Install>true</Install>
|
||||
</BootstrapperPackage>
|
||||
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
|
||||
<Install>false</Install>
|
||||
</BootstrapperPackage>
|
||||
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>.NET Framework 3.5 SP1</ProductName>
|
||||
<Install>false</Install>
|
||||
</BootstrapperPackage>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="cirno.ico" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<!--
|
||||
<PostBuildEvent Condition=" '$(OS)' != 'Unix' AND '$(ConfigurationName)' == 'Release' ">
|
||||
"$(SolutionDir)tools\ILRepack\ILRepack.exe" /ndebug /internalize /parallel /targetplatform:v4 /wildcards /out:"$(SolutionDir)out\ASF-GUI.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll"
|
||||
del "$(SolutionDir)out\ASF-GUI.exe.config"
|
||||
</PostBuildEvent>
|
||||
<PostBuildEvent Condition=" '$(OS)' == 'Unix' AND '$(ConfigurationName)' == 'Release' ">
|
||||
mono -O=all "$(SolutionDir)tools/ILRepack/ILRepack.exe" /ndebug /internalize /parallel /targetplatform:v4 /wildcards /out:"$(SolutionDir)out/ASF-GUI.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll"
|
||||
rm "$(SolutionDir)out/ASF-GUI.exe.config"
|
||||
</PostBuildEvent>
|
||||
-->
|
||||
</PropertyGroup>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
93
GUI/Logging.cs
Normal file
93
GUI/Logging.cs
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
_ _ _ ____ _ _____
|
||||
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
|
||||
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
|
||||
Contact: JustArchi@JustArchi.net
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace GUI {
|
||||
internal static class Logging {
|
||||
internal static void LogGenericInfo(string message) {
|
||||
if (string.IsNullOrEmpty(message)) {
|
||||
return;
|
||||
}
|
||||
|
||||
MessageBox.Show(message, "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
}
|
||||
|
||||
internal static void LogGenericWTF(string message, [CallerMemberName] string previousMethodName = "") {
|
||||
if (string.IsNullOrEmpty(message)) {
|
||||
return;
|
||||
}
|
||||
|
||||
MessageBox.Show(previousMethodName + "() " + message, "WTF", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
|
||||
internal static void LogGenericError(string message, [CallerMemberName] string previousMethodName = "") {
|
||||
if (string.IsNullOrEmpty(message)) {
|
||||
return;
|
||||
}
|
||||
|
||||
MessageBox.Show(previousMethodName + "() " + message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
|
||||
internal static void LogGenericException(Exception exception, [CallerMemberName] string previousMethodName = "") {
|
||||
if (exception == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
MessageBox.Show(previousMethodName + "() " + exception.Message + Environment.NewLine + exception.StackTrace, "Exception", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
|
||||
if (exception.InnerException != null) {
|
||||
LogGenericException(exception.InnerException, previousMethodName);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void LogGenericWarning(string message, [CallerMemberName] string previousMethodName = "") {
|
||||
if (string.IsNullOrEmpty(message)) {
|
||||
return;
|
||||
}
|
||||
|
||||
MessageBox.Show(previousMethodName + "() " + message, "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||
}
|
||||
|
||||
internal static void LogNullError(string nullObjectName, [CallerMemberName] string previousMethodName = "") {
|
||||
if (string.IsNullOrEmpty(nullObjectName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
LogGenericError(nullObjectName + " is null!", previousMethodName);
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
internal static void LogGenericDebug(string message, [CallerMemberName] string previousMethodName = "") {
|
||||
if (string.IsNullOrEmpty(message)) {
|
||||
return;
|
||||
}
|
||||
|
||||
MessageBox.Show(previousMethodName + "() " + message, "Debug", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
}
|
||||
}
|
||||
}
|
||||
16
GUI/Program.cs
Normal file
16
GUI/Program.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace GUI {
|
||||
static class Program {
|
||||
/// <summary>
|
||||
/// Der Haupteinstiegspunkt für die Anwendung.
|
||||
/// </summary>
|
||||
[STAThread]
|
||||
static void Main() {
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
Application.Run(new Form1());
|
||||
}
|
||||
}
|
||||
}
|
||||
36
GUI/Properties/AssemblyInfo.cs
Normal file
36
GUI/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// Allgemeine Informationen über eine Assembly werden über die folgenden
|
||||
// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
|
||||
// die mit einer Assembly verknüpft sind.
|
||||
[assembly: AssemblyTitle("GUI")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("GUI")]
|
||||
[assembly: AssemblyCopyright("Copyright © ArchiSteamFarm 2015-2016")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar
|
||||
// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von
|
||||
// COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
|
||||
[assembly: Guid("18b85645-1c80-4e25-9dcb-e01684a48fca")]
|
||||
|
||||
// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
|
||||
//
|
||||
// Hauptversion
|
||||
// Nebenversion
|
||||
// Buildnummer
|
||||
// Revision
|
||||
//
|
||||
// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern
|
||||
// übernehmen, indem Sie "*" eingeben:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
63
GUI/Properties/Resources.Designer.cs
generated
Normal file
63
GUI/Properties/Resources.Designer.cs
generated
Normal file
@@ -0,0 +1,63 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace GUI.Properties {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resources {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("GUI.Properties.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
117
GUI/Properties/Resources.resx
Normal file
117
GUI/Properties/Resources.resx
Normal file
@@ -0,0 +1,117 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
26
GUI/Properties/Settings.Designer.cs
generated
Normal file
26
GUI/Properties/Settings.Designer.cs
generated
Normal file
@@ -0,0 +1,26 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace GUI.Properties {
|
||||
|
||||
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")]
|
||||
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
|
||||
|
||||
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||
|
||||
public static Settings Default {
|
||||
get {
|
||||
return defaultInstance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
7
GUI/Properties/Settings.settings
Normal file
7
GUI/Properties/Settings.settings
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
|
||||
<Profiles>
|
||||
<Profile Name="(Default)" />
|
||||
</Profiles>
|
||||
<Settings />
|
||||
</SettingsFile>
|
||||
180
GUI/ServerProcess.cs
Normal file
180
GUI/ServerProcess.cs
Normal file
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
_ _ _ ____ _ _____
|
||||
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
||||
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
||||
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
||||
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
||||
|
||||
Copyright 2015-2016 Florian "KlappPC" Lang
|
||||
Contact: ichhoeremusik@gmx.net
|
||||
This file is mostly done by a friend who explicitly does not want to get mentioned in any way.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
using System.Threading;
|
||||
using System.Diagnostics;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace GUI {
|
||||
/*basically a class to run executables as controlled prozess in the background*/
|
||||
public class ServerProcess {
|
||||
//ASF.exe in our case
|
||||
protected Process process;
|
||||
//handling the output.
|
||||
protected Thread outputThread;
|
||||
protected bool stopping;
|
||||
|
||||
//the textbox from our Form, where we want to display output.
|
||||
private TextBox output;
|
||||
|
||||
private object lockObj = new object();
|
||||
|
||||
/**
|
||||
* New SeverProcess for filename with arguments and output to textBox.
|
||||
* Console is hidden and IO redirected.
|
||||
*/
|
||||
public ServerProcess(string fileName, string argumants, TextBox textBox) {
|
||||
|
||||
|
||||
output = textBox;
|
||||
process = new System.Diagnostics.Process();
|
||||
|
||||
process.StartInfo.FileName = fileName;
|
||||
process.StartInfo.Arguments = argumants;
|
||||
process.StartInfo.UseShellExecute = false;
|
||||
process.StartInfo.RedirectStandardInput = true;
|
||||
process.StartInfo.RedirectStandardError = true;
|
||||
process.StartInfo.RedirectStandardOutput = true;
|
||||
process.StartInfo.CreateNoWindow = true;
|
||||
|
||||
}
|
||||
//needed for realizing when input is needed.
|
||||
private int dotcounter = 0;
|
||||
|
||||
/**
|
||||
* I'm not quite happy with this. I could not figure a way to notice when input is required
|
||||
* besides reading char by char and searching for keywords. Will stop working, if the "Please enter"
|
||||
* lines gets changed.
|
||||
* Only tested for "Enter Password."
|
||||
*/
|
||||
private void NewOutput(object sender, char e) {
|
||||
MethodInvoker mi = delegate {
|
||||
output.AppendText(e.ToString());
|
||||
};
|
||||
|
||||
if (e == '.') {
|
||||
dotcounter++;
|
||||
} else if (e == ':') {
|
||||
dotcounter = 3;
|
||||
} else {
|
||||
dotcounter = 0;
|
||||
}
|
||||
if (dotcounter == 3) {
|
||||
string[] arr = output.Lines;
|
||||
string str = arr[arr.Length - 1];
|
||||
if (str.Contains("Hit enter")) {
|
||||
str = arr[arr.Length - 2] + " | " + str;
|
||||
Form f = new Form2(this, str);
|
||||
f.ShowDialog();
|
||||
mi = delegate { output.AppendText(e.ToString() + "\n"); };
|
||||
}
|
||||
if (str.Contains("Please enter")) {
|
||||
Form f = new Form2(this, str);
|
||||
f.ShowDialog();
|
||||
mi = delegate { output.AppendText(e.ToString() + "\n"); };
|
||||
}
|
||||
|
||||
dotcounter = 0;
|
||||
}
|
||||
output.Invoke(mi);
|
||||
|
||||
}
|
||||
|
||||
private void NewOutput(object sender, string e) {
|
||||
MethodInvoker mi = delegate {
|
||||
output.AppendText(e + "\n");
|
||||
};
|
||||
output.Invoke(mi);
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void printOutPut() {
|
||||
char str;
|
||||
int i;
|
||||
string s;
|
||||
while (!stopping) {
|
||||
//thats ugly, but when using readline we can't catch input.
|
||||
while (((i = process.StandardOutput.Read()) != 0)) {
|
||||
str = System.Convert.ToChar(i);
|
||||
NewOutput(this, str);
|
||||
if (stopping)
|
||||
break;
|
||||
}
|
||||
|
||||
while (((s = process.StandardError.ReadLine()) != null)) {
|
||||
NewOutput(this, s);
|
||||
if (stopping)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Write(string msg) {
|
||||
process.StandardInput.WriteLine(msg);
|
||||
process.StandardInput.Flush();
|
||||
}
|
||||
|
||||
public void Stop() {
|
||||
Thread stopThread = new Thread(StopProcess);
|
||||
stopThread.Start();
|
||||
}
|
||||
|
||||
private void StopProcess() {
|
||||
|
||||
if (process == null)
|
||||
return;
|
||||
stopping = true;
|
||||
|
||||
outputThread.Abort();
|
||||
|
||||
Thread.Sleep(1000);
|
||||
|
||||
if (process == null)
|
||||
return;
|
||||
|
||||
if (process.HasExited)
|
||||
process.Close();
|
||||
else
|
||||
process.Kill();
|
||||
|
||||
process = null;
|
||||
}
|
||||
/**
|
||||
* starts the process and a second thread to listen for output.
|
||||
*/
|
||||
public void Start() {
|
||||
outputThread = new Thread(printOutPut);
|
||||
process.Start();
|
||||
outputThread.Start();
|
||||
}
|
||||
|
||||
public Process Process {
|
||||
get {
|
||||
return process;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
58
GUI/app.manifest
Normal file
58
GUI/app.manifest
Normal file
@@ -0,0 +1,58 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||
<security>
|
||||
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<!-- UAC-Manifestoptionen
|
||||
Wenn Sie die Zugangsebene für das Windows-Benutzerkonto ändern möchten, ersetzen Sie den
|
||||
requestedExecutionLevel-Knoten durch eines der folgenden Elemente.
|
||||
|
||||
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
|
||||
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />
|
||||
|
||||
Durch Angeben des requestedExecutionLevel-Knotens wird die Datei- und Registrierungsvirtualisierung deaktiviert.
|
||||
Wenn Sie Datei- und Registrierungsvirtualisierung für Abwärts-
|
||||
kompatibilität verwenden möchten, löschen Sie den requestedExecutionLevel-Knoten.
|
||||
-->
|
||||
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!-- Eine Liste aller Windows-Versionen, mit denen die Anwendung kompatibel ist.
|
||||
Windows wählt automatisch die am stärksten kompatible Umgebung aus.-->
|
||||
|
||||
<!-- Wenn die Anwendung mit Windows Vista kompatibel ist, heben Sie die Auskommentierung des folgenden supportedOS-Knotens auf-->
|
||||
<!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"></supportedOS>-->
|
||||
|
||||
<!-- Wenn die Anwendung mit Windows 7 kompatibel ist, heben Sie die Kommentierung des folgenden supportedOS-Knotens auf.-->
|
||||
<!--<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>-->
|
||||
|
||||
<!-- Wenn die Anwendung mit Windows 8 kompatibel ist, heben Sie die Auskommentierung des folgenden supportedOS-Knotens auf-->
|
||||
<!--<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"></supportedOS>-->
|
||||
|
||||
<!-- Wenn die Anwendung mit Windows 8.1 kompatibel ist, die Kommentierung des folgenden supportedOS-Knotens aufheben.-->
|
||||
<!--<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>-->
|
||||
|
||||
</application>
|
||||
</compatibility>
|
||||
|
||||
<!-- Designs für allgemeine Windows-Steuerelemente und -Dialogfelder (Windows XP und höher) aktivieren -->
|
||||
<!-- <dependency>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity
|
||||
type="win32"
|
||||
name="Microsoft.Windows.Common-Controls"
|
||||
version="6.0.0.0"
|
||||
processorArchitecture="*"
|
||||
publicKeyToken="6595b64144ccf1df"
|
||||
language="*"
|
||||
/>
|
||||
</dependentAssembly>
|
||||
</dependency>-->
|
||||
|
||||
</asmv1:assembly>
|
||||
BIN
GUI/cirno.ico
Normal file
BIN
GUI/cirno.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 361 KiB |
60
README.md
60
README.md
@@ -1,39 +1,53 @@
|
||||
ArchiSteamFarm
|
||||
===================
|
||||
|
||||
Big work-in-progress. This bot allows you to farm steam cards using multiple accounts simultaneously. Each account is defined by it's own XML config in `config` directory and you don´t need any steam-client running in the background.
|
||||
[](https://ci.appveyor.com/project/JustArchi/ArchiSteamFarm)
|
||||
[](https://travis-ci.org/JustArchi/ArchiSteamFarm)
|
||||
[](https://github.com/JustArchi/ArchiSteamFarm/releases/latest)
|
||||
[](https://github.com/JustArchi/ArchiSteamFarm/releases)
|
||||
[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=HD2P2P3WGS5Y4)
|
||||
[](https://steamcommunity.com/tradeoffer/new/?partner=46697991&token=0ix2Ruv_)
|
||||
|
||||
**Current functions:**
|
||||
---
|
||||
|
||||
- Automatically farm steam cards using any number of active accounts
|
||||
- Automatically accept friend requests sent from master
|
||||
- Automatically accept all trades coming from master
|
||||
- Automatically accept all steam cd-keys sent via chat from master
|
||||
- SteamGuard / 2-factor-authentication support
|
||||
- Full Mono support, tested on Debian "9.0" Stretch (testing)
|
||||
ASF is a C# application that allows you to farm steam cards using multiple steam accounts simultaneously. Unlike idle master which works only on one account at given time, requires steam client running in background, and launches additional processes imitiating "game playing" status, ASF doesn't require any steam client running in the background, doesn't launch any additional processes and is made to handle unlimited steam accounts at once. In addition to that, it's meant to be run on servers or other desktop-less machines, and features full Mono support, which makes it possible to launch on any Mono-supported operating system, such as Windows, Linux or OS X. ASF is based on, and possible, thanks to [SteamKit2](https://github.com/SteamRE/SteamKit).
|
||||
|
||||
ASF doesn't require and doesn't interfere in any way with Steam client. In addition to that, it no longer requires exclusive access to given account, which means that you can use your main account in Steam client, and use ASF for farming the same account at the same time. If you decide to launch a game, ASF will get disconnected, and resume farming once you finish playing your game, being as transparent as possible.
|
||||
|
||||
**Core features:**
|
||||
|
||||
- Automatically farm available games using any number of active accounts
|
||||
- Automatically accept friend requests sent from master
|
||||
- Automatically accept all trades coming from master
|
||||
- Automatically accept all steam cd-keys sent via chat from master
|
||||
- Possibility to choose the most efficient cards farming algorithm, based on given account
|
||||
- SteamGuard / SteamParental / 2FA support
|
||||
- Unique ASF 2FA mechanism allowing ASF to act as mobile authenticator (if needed)
|
||||
- ASF update notifications
|
||||
- Full Mono support, cross-OS compatibility
|
||||
|
||||
**Setting up:**
|
||||
|
||||
Detailed setting up instructions are available on **[our wiki](https://github.com/JustArchi/ArchiSteamFarm/wiki/Setting-up)**.
|
||||
|
||||
**Current Commands:**
|
||||
|
||||
- `!farm` Restarts the bot and starts card-farming (again)
|
||||
- `!exit` Stops the bot
|
||||
Detailed documentation of all available commands is available on **[our wiki](https://github.com/JustArchi/ArchiSteamFarm/wiki/Commands)**.
|
||||
|
||||
> You can use chat-commands in group-chat or private-chat with your bot.
|
||||
> The MasterID has to be set for this specific bot / config-file.
|
||||
> Commands can be executed via a private chat with your bot.
|
||||
> Remember that bot accepts commands only from ```SteamMasterID```. That property can be configured in the config.
|
||||
|
||||
**Supported / Tested Operating-Systems:**
|
||||
|
||||
- Windows 10 Enterprise Edition
|
||||
- Windows 10 Professional/Enterprise Edition (Native)
|
||||
- Windows 8.1 Professional (Native)
|
||||
- Windows 7 Ultimate (Native)
|
||||
- Debian 9.0 Stretch (Mono)
|
||||
- Debian 8.1 Jessie (Mono)
|
||||
- OS X 10.11.1 (Mono)
|
||||
|
||||
However, any operating system [listed here](http://www.mono-project.com/docs/about-mono/supported-platforms/) should run ASF flawlessly.
|
||||
|
||||
**TODO**:
|
||||
**Need help or more info?**
|
||||
|
||||
- Smart Multi-Game-Farming
|
||||
- Possible integration with SteamTradeMatcher, bot can detect dupes and trade them automatically. Backend-code is already here, just missing actual implementation.
|
||||
- Automatic sending of steamtrades to master(after game is fully farmed)
|
||||
- Probably much more
|
||||
|
||||
|
||||
> This is big WIP, so feel free to send pull requests if you wish. I'll
|
||||
> release some releases later, when everything is tested and code
|
||||
> cleaned up.
|
||||
Head over to our [wiki](https://github.com/JustArchi/ArchiSteamFarm/wiki) then.
|
||||
|
||||
12
SteamAuth/APIEndpoints.cs
Normal file
12
SteamAuth/APIEndpoints.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace SteamAuth
|
||||
{
|
||||
public static class APIEndpoints
|
||||
{
|
||||
public const string STEAMAPI_BASE = "https://api.steampowered.com";
|
||||
public const string COMMUNITY_BASE = "https://steamcommunity.com";
|
||||
public const string MOBILEAUTH_BASE = STEAMAPI_BASE + "/IMobileAuthService/%s/v0001";
|
||||
public static string MOBILEAUTH_GETWGTOKEN = MOBILEAUTH_BASE.Replace("%s", "GetWGToken");
|
||||
public const string TWO_FACTOR_BASE = STEAMAPI_BASE + "/ITwoFactorService/%s/v0001";
|
||||
public static string TWO_FACTOR_TIME_QUERY = TWO_FACTOR_BASE.Replace("%s", "QueryTime");
|
||||
}
|
||||
}
|
||||
292
SteamAuth/AuthenticatorLinker.cs
Normal file
292
SteamAuth/AuthenticatorLinker.cs
Normal file
@@ -0,0 +1,292 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SteamAuth
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles the linking process for a new mobile authenticator.
|
||||
/// </summary>
|
||||
public class AuthenticatorLinker
|
||||
{
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Set to register a new phone number when linking. If a phone number is not set on the account, this must be set. If a phone number is set on the account, this must be null.
|
||||
/// </summary>
|
||||
public string PhoneNumber = null;
|
||||
|
||||
/// <summary>
|
||||
/// Randomly-generated device ID. Should only be generated once per linker.
|
||||
/// </summary>
|
||||
public string DeviceID { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// After the initial link step, if successful, this will be the SteamGuard data for the account. PLEASE save this somewhere after generating it; it's vital data.
|
||||
/// </summary>
|
||||
public SteamGuardAccount LinkedAccount { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// True if the authenticator has been fully finalized.
|
||||
/// </summary>
|
||||
public bool Finalized = false;
|
||||
|
||||
private SessionData _session;
|
||||
private CookieContainer _cookies;
|
||||
|
||||
public AuthenticatorLinker(SessionData session)
|
||||
{
|
||||
this._session = session;
|
||||
this.DeviceID = GenerateDeviceID();
|
||||
|
||||
this._cookies = new CookieContainer();
|
||||
session.AddCookies(_cookies);
|
||||
}
|
||||
|
||||
public LinkResult AddAuthenticator()
|
||||
{
|
||||
bool hasPhone = _hasPhoneAttached();
|
||||
if (hasPhone && PhoneNumber != null)
|
||||
return LinkResult.MustRemovePhoneNumber;
|
||||
if (!hasPhone && PhoneNumber == null)
|
||||
return LinkResult.MustProvidePhoneNumber;
|
||||
|
||||
if (!hasPhone)
|
||||
{
|
||||
if (!_addPhoneNumber())
|
||||
{
|
||||
return LinkResult.GeneralFailure;
|
||||
}
|
||||
}
|
||||
|
||||
var postData = new NameValueCollection();
|
||||
postData.Add("access_token", _session.OAuthToken);
|
||||
postData.Add("steamid", _session.SteamID.ToString());
|
||||
postData.Add("authenticator_type", "1");
|
||||
postData.Add("device_identifier", this.DeviceID);
|
||||
postData.Add("sms_phone_id", "1");
|
||||
|
||||
string response = SteamWeb.MobileLoginRequest(APIEndpoints.STEAMAPI_BASE + "/ITwoFactorService/AddAuthenticator/v0001", "POST", postData);
|
||||
if (response == null) return LinkResult.GeneralFailure;
|
||||
|
||||
var addAuthenticatorResponse = JsonConvert.DeserializeObject<AddAuthenticatorResponse>(response);
|
||||
if (addAuthenticatorResponse == null || addAuthenticatorResponse.Response == null)
|
||||
{
|
||||
return LinkResult.GeneralFailure;
|
||||
}
|
||||
|
||||
if (addAuthenticatorResponse.Response.Status == 29)
|
||||
{
|
||||
return LinkResult.AuthenticatorPresent;
|
||||
}
|
||||
|
||||
if (addAuthenticatorResponse.Response.Status != 1)
|
||||
{
|
||||
return LinkResult.GeneralFailure;
|
||||
}
|
||||
|
||||
this.LinkedAccount = addAuthenticatorResponse.Response;
|
||||
LinkedAccount.Session = this._session;
|
||||
LinkedAccount.DeviceID = this.DeviceID;
|
||||
|
||||
return LinkResult.AwaitingFinalization;
|
||||
}
|
||||
|
||||
public FinalizeResult FinalizeAddAuthenticator(string smsCode)
|
||||
{
|
||||
//The act of checking the SMS code is necessary for Steam to finalize adding the phone number to the account.
|
||||
//Of course, we only want to check it if we're adding a phone number in the first place...
|
||||
|
||||
if (!String.IsNullOrEmpty(this.PhoneNumber) && !this._checkSMSCode(smsCode))
|
||||
{
|
||||
return FinalizeResult.BadSMSCode;
|
||||
}
|
||||
|
||||
var postData = new NameValueCollection();
|
||||
postData.Add("steamid", _session.SteamID.ToString());
|
||||
postData.Add("access_token", _session.OAuthToken);
|
||||
postData.Add("activation_code", smsCode);
|
||||
int tries = 0;
|
||||
while (tries <= 30)
|
||||
{
|
||||
postData.Set("authenticator_code", LinkedAccount.GenerateSteamGuardCode());
|
||||
postData.Set("authenticator_time", TimeAligner.GetSteamTime().ToString());
|
||||
|
||||
string response = SteamWeb.MobileLoginRequest(APIEndpoints.STEAMAPI_BASE + "/ITwoFactorService/FinalizeAddAuthenticator/v0001", "POST", postData);
|
||||
if (response == null) return FinalizeResult.GeneralFailure;
|
||||
|
||||
var finalizeResponse = JsonConvert.DeserializeObject<FinalizeAuthenticatorResponse>(response);
|
||||
|
||||
if (finalizeResponse == null || finalizeResponse.Response == null)
|
||||
{
|
||||
return FinalizeResult.GeneralFailure;
|
||||
}
|
||||
|
||||
if (finalizeResponse.Response.Status == 89)
|
||||
{
|
||||
return FinalizeResult.BadSMSCode;
|
||||
}
|
||||
|
||||
if (finalizeResponse.Response.Status == 88)
|
||||
{
|
||||
if (tries >= 30)
|
||||
{
|
||||
return FinalizeResult.UnableToGenerateCorrectCodes;
|
||||
}
|
||||
}
|
||||
|
||||
if (!finalizeResponse.Response.Success)
|
||||
{
|
||||
return FinalizeResult.GeneralFailure;
|
||||
}
|
||||
|
||||
if (finalizeResponse.Response.WantMore)
|
||||
{
|
||||
tries++;
|
||||
continue;
|
||||
}
|
||||
|
||||
this.LinkedAccount.FullyEnrolled = true;
|
||||
return FinalizeResult.Success;
|
||||
}
|
||||
|
||||
return FinalizeResult.GeneralFailure;
|
||||
}
|
||||
|
||||
private bool _checkSMSCode(string smsCode)
|
||||
{
|
||||
var postData = new NameValueCollection();
|
||||
postData.Add("op", "check_sms_code");
|
||||
postData.Add("arg", smsCode);
|
||||
postData.Add("sessionid", _session.SessionID);
|
||||
|
||||
string response = SteamWeb.Request(APIEndpoints.COMMUNITY_BASE + "/steamguard/phoneajax", "POST", postData, _cookies);
|
||||
if (response == null) return false;
|
||||
|
||||
var addPhoneNumberResponse = JsonConvert.DeserializeObject<AddPhoneResponse>(response);
|
||||
return addPhoneNumberResponse.Success;
|
||||
}
|
||||
|
||||
private bool _addPhoneNumber()
|
||||
{
|
||||
var postData = new NameValueCollection();
|
||||
postData.Add("op", "add_phone_number");
|
||||
postData.Add("arg", PhoneNumber);
|
||||
postData.Add("sessionid", _session.SessionID);
|
||||
|
||||
string response = SteamWeb.Request(APIEndpoints.COMMUNITY_BASE + "/steamguard/phoneajax", "POST", postData, _cookies);
|
||||
if (response == null) return false;
|
||||
|
||||
var addPhoneNumberResponse = JsonConvert.DeserializeObject<AddPhoneResponse>(response);
|
||||
return addPhoneNumberResponse.Success;
|
||||
}
|
||||
|
||||
private bool _hasPhoneAttached()
|
||||
{
|
||||
var postData = new NameValueCollection();
|
||||
postData.Add("op", "has_phone");
|
||||
postData.Add("arg", "null");
|
||||
postData.Add("sessionid", _session.SessionID);
|
||||
|
||||
string response = SteamWeb.Request(APIEndpoints.COMMUNITY_BASE + "/steamguard/phoneajax", "POST", postData, _cookies);
|
||||
if (response == null) return false;
|
||||
|
||||
var hasPhoneResponse = JsonConvert.DeserializeObject<HasPhoneResponse>(response);
|
||||
return hasPhoneResponse.HasPhone;
|
||||
}
|
||||
|
||||
public enum LinkResult
|
||||
{
|
||||
MustProvidePhoneNumber, //No phone number on the account
|
||||
MustRemovePhoneNumber, //A phone number is already on the account
|
||||
AwaitingFinalization, //Must provide an SMS code
|
||||
GeneralFailure, //General failure (really now!)
|
||||
AuthenticatorPresent
|
||||
}
|
||||
|
||||
public enum FinalizeResult
|
||||
{
|
||||
BadSMSCode,
|
||||
UnableToGenerateCorrectCodes,
|
||||
Success,
|
||||
GeneralFailure
|
||||
}
|
||||
|
||||
private class AddAuthenticatorResponse
|
||||
{
|
||||
[JsonProperty("response")]
|
||||
public SteamGuardAccount Response { get; set; }
|
||||
}
|
||||
|
||||
private class FinalizeAuthenticatorResponse
|
||||
{
|
||||
[JsonProperty("response")]
|
||||
public FinalizeAuthenticatorInternalResponse Response { get; set; }
|
||||
|
||||
internal class FinalizeAuthenticatorInternalResponse
|
||||
{
|
||||
[JsonProperty("status")]
|
||||
public int Status { get; set; }
|
||||
|
||||
[JsonProperty("server_time")]
|
||||
public long ServerTime { get; set; }
|
||||
|
||||
[JsonProperty("want_more")]
|
||||
public bool WantMore { get; set; }
|
||||
|
||||
[JsonProperty("success")]
|
||||
public bool Success { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
private class HasPhoneResponse
|
||||
{
|
||||
[JsonProperty("has_phone")]
|
||||
public bool HasPhone { get; set; }
|
||||
}
|
||||
|
||||
private class AddPhoneResponse
|
||||
{
|
||||
[JsonProperty("success")]
|
||||
public bool Success { get; set; }
|
||||
}
|
||||
|
||||
public static string GenerateDeviceID()
|
||||
{
|
||||
using (var sha1 = new SHA1Managed())
|
||||
{
|
||||
RNGCryptoServiceProvider secureRandom = new RNGCryptoServiceProvider();
|
||||
byte[] randomBytes = new byte[8];
|
||||
secureRandom.GetBytes(randomBytes);
|
||||
|
||||
byte[] hashedBytes = sha1.ComputeHash(randomBytes);
|
||||
string random32 = BitConverter.ToString(hashedBytes).Replace("-", "").Substring(0, 32).ToLower();
|
||||
|
||||
return "android:" + SplitOnRatios(random32, new[] { 8, 4, 4, 4, 12 }, "-");
|
||||
}
|
||||
}
|
||||
|
||||
private static string SplitOnRatios(string str, int[] ratios, string intermediate)
|
||||
{
|
||||
string result = "";
|
||||
|
||||
int pos = 0;
|
||||
for (int index = 0; index < ratios.Length; index++)
|
||||
{
|
||||
result += str.Substring(pos, ratios[index]);
|
||||
pos = ratios[index];
|
||||
|
||||
if (index < ratios.Length - 1)
|
||||
result += intermediate;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
36
SteamAuth/Confirmation.cs
Normal file
36
SteamAuth/Confirmation.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SteamAuth
|
||||
{
|
||||
public class Confirmation
|
||||
{
|
||||
public string ID;
|
||||
public string Key;
|
||||
public string Description;
|
||||
|
||||
public ConfirmationType ConfType
|
||||
{
|
||||
get
|
||||
{
|
||||
if (String.IsNullOrEmpty(Description)) return ConfirmationType.Unknown;
|
||||
if (Description.StartsWith("Confirm ")) return ConfirmationType.GenericConfirmation;
|
||||
if (Description.StartsWith("Trade with ")) return ConfirmationType.Trade;
|
||||
if (Description.StartsWith("Sell -")) return ConfirmationType.MarketSellTransaction;
|
||||
|
||||
return ConfirmationType.Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
public enum ConfirmationType
|
||||
{
|
||||
GenericConfirmation,
|
||||
Trade,
|
||||
MarketSellTransaction,
|
||||
Unknown
|
||||
}
|
||||
}
|
||||
}
|
||||
21
SteamAuth/LICENSE
Normal file
21
SteamAuth/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Joshua Coffey
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
36
SteamAuth/Properties/AssemblyInfo.cs
Normal file
36
SteamAuth/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("SteamAuth")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("SteamAuth")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2015")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("5ad0934e-f6c4-4ae5-83af-c788313b2a87")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
40
SteamAuth/SessionData.cs
Normal file
40
SteamAuth/SessionData.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using System.Net;
|
||||
|
||||
namespace SteamAuth
|
||||
{
|
||||
public class SessionData
|
||||
{
|
||||
public string SessionID { get; set; }
|
||||
|
||||
public string SteamLogin { get; set; }
|
||||
|
||||
public string SteamLoginSecure { get; set; }
|
||||
|
||||
public string WebCookie { get; set; }
|
||||
|
||||
public string OAuthToken { get; set; }
|
||||
|
||||
public ulong SteamID { get; set; }
|
||||
|
||||
public void AddCookies(CookieContainer cookies)
|
||||
{
|
||||
cookies.Add(new Cookie("mobileClientVersion", "0 (2.1.3)", "/", ".steamcommunity.com"));
|
||||
cookies.Add(new Cookie("mobileClient", "android", "/", ".steamcommunity.com"));
|
||||
|
||||
cookies.Add(new Cookie("steamid", SteamID.ToString(), "/", ".steamcommunity.com"));
|
||||
cookies.Add(new Cookie("steamLogin", SteamLogin, "/", ".steamcommunity.com")
|
||||
{
|
||||
HttpOnly = true
|
||||
});
|
||||
|
||||
cookies.Add(new Cookie("steamLoginSecure", SteamLoginSecure, "/", ".steamcommunity.com")
|
||||
{
|
||||
HttpOnly = true,
|
||||
Secure = true
|
||||
});
|
||||
cookies.Add(new Cookie("Steam_Language", "english", "/", ".steamcommunity.com"));
|
||||
cookies.Add(new Cookie("dob", "", "/", ".steamcommunity.com"));
|
||||
cookies.Add(new Cookie("sessionid", this.SessionID, "/", ".steamcommunity.com"));
|
||||
}
|
||||
}
|
||||
}
|
||||
71
SteamAuth/SteamAuth.csproj
Normal file
71
SteamAuth/SteamAuth.csproj
Normal file
@@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{5AD0934E-F6C4-4AE5-83AF-C788313B2A87}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>SteamAuth</RootNamespace>
|
||||
<AssemblyName>SteamAuth</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>none</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>
|
||||
</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.8.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="APIEndpoints.cs" />
|
||||
<Compile Include="AuthenticatorLinker.cs" />
|
||||
<Compile Include="Confirmation.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="SessionData.cs" />
|
||||
<Compile Include="SteamGuardAccount.cs" />
|
||||
<Compile Include="SteamWeb.cs" />
|
||||
<Compile Include="TimeAligner.cs" />
|
||||
<Compile Include="UserLogin.cs" />
|
||||
<Compile Include="Util.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
28
SteamAuth/SteamAuth.sln
Normal file
28
SteamAuth/SteamAuth.sln
Normal file
@@ -0,0 +1,28 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.23107.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SteamAuth", "SteamAuth.csproj", "{5AD0934E-F6C4-4AE5-83AF-C788313B2A87}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestBed", "..\TestBed\TestBed.csproj", "{8A732227-C090-4011-9F0A-51180CFE6271}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{5AD0934E-F6C4-4AE5-83AF-C788313B2A87}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5AD0934E-F6C4-4AE5-83AF-C788313B2A87}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5AD0934E-F6C4-4AE5-83AF-C788313B2A87}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5AD0934E-F6C4-4AE5-83AF-C788313B2A87}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{8A732227-C090-4011-9F0A-51180CFE6271}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8A732227-C090-4011-9F0A-51180CFE6271}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8A732227-C090-4011-9F0A-51180CFE6271}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8A732227-C090-4011-9F0A-51180CFE6271}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
456
SteamAuth/SteamGuardAccount.cs
Normal file
456
SteamAuth/SteamGuardAccount.cs
Normal file
@@ -0,0 +1,456 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Net;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SteamAuth
|
||||
{
|
||||
public class SteamGuardAccount
|
||||
{
|
||||
[JsonProperty("shared_secret")]
|
||||
public string SharedSecret { get; set; }
|
||||
|
||||
[JsonProperty("serial_number")]
|
||||
public string SerialNumber { get; set; }
|
||||
|
||||
[JsonProperty("revocation_code")]
|
||||
public string RevocationCode { get; set; }
|
||||
|
||||
[JsonProperty("uri")]
|
||||
public string URI { get; set; }
|
||||
|
||||
[JsonProperty("server_time")]
|
||||
public long ServerTime { get; set; }
|
||||
|
||||
[JsonProperty("account_name")]
|
||||
public string AccountName { get; set; }
|
||||
|
||||
[JsonProperty("token_gid")]
|
||||
public string TokenGID { get; set; }
|
||||
|
||||
[JsonProperty("identity_secret")]
|
||||
public string IdentitySecret { get; set; }
|
||||
|
||||
[JsonProperty("secret_1")]
|
||||
public string Secret1 { get; set; }
|
||||
|
||||
[JsonProperty("status")]
|
||||
public int Status { get; set; }
|
||||
|
||||
[JsonProperty("device_id")]
|
||||
public string DeviceID { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Set to true if the authenticator has actually been applied to the account.
|
||||
/// </summary>
|
||||
[JsonProperty("fully_enrolled")]
|
||||
public bool FullyEnrolled { get; set; }
|
||||
|
||||
public SessionData Session { get; set; }
|
||||
|
||||
private static byte[] steamGuardCodeTranslations = new byte[] { 50, 51, 52, 53, 54, 55, 56, 57, 66, 67, 68, 70, 71, 72, 74, 75, 77, 78, 80, 81, 82, 84, 86, 87, 88, 89 };
|
||||
|
||||
public bool DeactivateAuthenticator(int scheme = 2)
|
||||
{
|
||||
var postData = new NameValueCollection();
|
||||
postData.Add("steamid", this.Session.SteamID.ToString());
|
||||
postData.Add("steamguard_scheme", scheme.ToString());
|
||||
postData.Add("revocation_code", this.RevocationCode);
|
||||
postData.Add("access_token", this.Session.OAuthToken);
|
||||
|
||||
try
|
||||
{
|
||||
string response = SteamWeb.MobileLoginRequest(APIEndpoints.STEAMAPI_BASE + "/ITwoFactorService/RemoveAuthenticator/v0001", "POST", postData);
|
||||
var removeResponse = JsonConvert.DeserializeObject<RemoveAuthenticatorResponse>(response);
|
||||
|
||||
if (removeResponse == null || removeResponse.Response == null || !removeResponse.Response.Success) return false;
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public string GenerateSteamGuardCode()
|
||||
{
|
||||
return GenerateSteamGuardCodeForTime(TimeAligner.GetSteamTime());
|
||||
}
|
||||
|
||||
public string GenerateSteamGuardCodeForTime(long time)
|
||||
{
|
||||
if (this.SharedSecret == null || this.SharedSecret.Length == 0)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
byte[] sharedSecretArray = Convert.FromBase64String(this.SharedSecret);
|
||||
byte[] timeArray = new byte[8];
|
||||
|
||||
time /= 30L;
|
||||
|
||||
for (int i = 8; i > 0; i--)
|
||||
{
|
||||
timeArray[i - 1] = (byte)time;
|
||||
time >>= 8;
|
||||
}
|
||||
|
||||
HMACSHA1 hmacGenerator = new HMACSHA1();
|
||||
hmacGenerator.Key = sharedSecretArray;
|
||||
byte[] hashedData = hmacGenerator.ComputeHash(timeArray);
|
||||
byte[] codeArray = new byte[5];
|
||||
try
|
||||
{
|
||||
byte b = (byte)(hashedData[19] & 0xF);
|
||||
int codePoint = (hashedData[b] & 0x7F) << 24 | (hashedData[b + 1] & 0xFF) << 16 | (hashedData[b + 2] & 0xFF) << 8 | (hashedData[b + 3] & 0xFF);
|
||||
|
||||
for (int i = 0; i < 5; ++i)
|
||||
{
|
||||
codeArray[i] = steamGuardCodeTranslations[codePoint % steamGuardCodeTranslations.Length];
|
||||
codePoint /= steamGuardCodeTranslations.Length;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return null; //Change later, catch-alls are bad!
|
||||
}
|
||||
return Encoding.UTF8.GetString(codeArray);
|
||||
}
|
||||
|
||||
public Confirmation[] FetchConfirmations()
|
||||
{
|
||||
string url = this.GenerateConfirmationURL();
|
||||
|
||||
CookieContainer cookies = new CookieContainer();
|
||||
this.Session.AddCookies(cookies);
|
||||
|
||||
string response = SteamWeb.Request(url, "GET", null, cookies);
|
||||
|
||||
/*So you're going to see this abomination and you're going to be upset.
|
||||
It's understandable. But the thing is, regex for HTML -- while awful -- makes this way faster than parsing a DOM, plus we don't need another library.
|
||||
And because the data is always in the same place and same format... It's not as if we're trying to naturally understand HTML here. Just extract strings.
|
||||
I'm sorry. */
|
||||
|
||||
Regex confIDRegex = new Regex("data-confid=\"(\\d+)\"");
|
||||
Regex confKeyRegex = new Regex("data-key=\"(\\d+)\"");
|
||||
Regex confDescRegex = new Regex("<div>((Confirm|Trade with|Sell -) .+)</div>");
|
||||
|
||||
if (response == null || !(confIDRegex.IsMatch(response) && confKeyRegex.IsMatch(response) && confDescRegex.IsMatch(response)))
|
||||
{
|
||||
if (response == null || !response.Contains("<div>Nothing to confirm</div>"))
|
||||
{
|
||||
throw new WGTokenInvalidException();
|
||||
}
|
||||
|
||||
return new Confirmation[0];
|
||||
}
|
||||
|
||||
MatchCollection confIDs = confIDRegex.Matches(response);
|
||||
MatchCollection confKeys = confKeyRegex.Matches(response);
|
||||
MatchCollection confDescs = confDescRegex.Matches(response);
|
||||
|
||||
List<Confirmation> ret = new List<Confirmation>();
|
||||
for (int i = 0; i < confIDs.Count; i++)
|
||||
{
|
||||
string confID = confIDs[i].Groups[1].Value;
|
||||
string confKey = confKeys[i].Groups[1].Value;
|
||||
string confDesc = confDescs[i].Groups[1].Value;
|
||||
Confirmation conf = new Confirmation()
|
||||
{
|
||||
Description = confDesc,
|
||||
ID = confID,
|
||||
Key = confKey
|
||||
};
|
||||
ret.Add(conf);
|
||||
}
|
||||
|
||||
return ret.ToArray();
|
||||
}
|
||||
|
||||
public async Task<Confirmation[]> FetchConfirmationsAsync()
|
||||
{
|
||||
string url = this.GenerateConfirmationURL();
|
||||
|
||||
CookieContainer cookies = new CookieContainer();
|
||||
this.Session.AddCookies(cookies);
|
||||
|
||||
string response = await SteamWeb.RequestAsync(url, "GET", null, cookies);
|
||||
|
||||
/*So you're going to see this abomination and you're going to be upset.
|
||||
It's understandable. But the thing is, regex for HTML -- while awful -- makes this way faster than parsing a DOM, plus we don't need another library.
|
||||
And because the data is always in the same place and same format... It's not as if we're trying to naturally understand HTML here. Just extract strings.
|
||||
I'm sorry. */
|
||||
|
||||
Regex confIDRegex = new Regex("data-confid=\"(\\d+)\"");
|
||||
Regex confKeyRegex = new Regex("data-key=\"(\\d+)\"");
|
||||
Regex confDescRegex = new Regex("<div>((Confirm|Trade with|Sell -) .+)</div>");
|
||||
|
||||
if (response == null || !(confIDRegex.IsMatch(response) && confKeyRegex.IsMatch(response) && confDescRegex.IsMatch(response)))
|
||||
{
|
||||
if (response == null || !response.Contains("<div>Nothing to confirm</div>"))
|
||||
{
|
||||
throw new WGTokenInvalidException();
|
||||
}
|
||||
|
||||
return new Confirmation[0];
|
||||
}
|
||||
|
||||
MatchCollection confIDs = confIDRegex.Matches(response);
|
||||
MatchCollection confKeys = confKeyRegex.Matches(response);
|
||||
MatchCollection confDescs = confDescRegex.Matches(response);
|
||||
|
||||
List<Confirmation> ret = new List<Confirmation>();
|
||||
for (int i = 0; i < confIDs.Count; i++)
|
||||
{
|
||||
string confID = confIDs[i].Groups[1].Value;
|
||||
string confKey = confKeys[i].Groups[1].Value;
|
||||
string confDesc = confDescs[i].Groups[1].Value;
|
||||
Confirmation conf = new Confirmation()
|
||||
{
|
||||
Description = confDesc,
|
||||
ID = confID,
|
||||
Key = confKey
|
||||
};
|
||||
ret.Add(conf);
|
||||
}
|
||||
|
||||
return ret.ToArray();
|
||||
}
|
||||
|
||||
public long GetConfirmationTradeOfferID(Confirmation conf)
|
||||
{
|
||||
var confDetails = _getConfirmationDetails(conf);
|
||||
if (confDetails == null || !confDetails.Success) return -1;
|
||||
|
||||
Regex tradeOfferIDRegex = new Regex("<div class=\"tradeoffer\" id=\"tradeofferid_(\\d+)\" >");
|
||||
if(!tradeOfferIDRegex.IsMatch(confDetails.HTML)) return -1;
|
||||
return long.Parse(tradeOfferIDRegex.Match(confDetails.HTML).Groups[1].Value);
|
||||
}
|
||||
|
||||
public bool AcceptConfirmation(Confirmation conf)
|
||||
{
|
||||
return _sendConfirmationAjax(conf, "allow");
|
||||
}
|
||||
|
||||
public bool DenyConfirmation(Confirmation conf)
|
||||
{
|
||||
return _sendConfirmationAjax(conf, "cancel");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refreshes the Steam session. Necessary to perform confirmations if your session has expired or changed.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool RefreshSession()
|
||||
{
|
||||
string url = APIEndpoints.MOBILEAUTH_GETWGTOKEN;
|
||||
NameValueCollection postData = new NameValueCollection();
|
||||
postData.Add("access_token", this.Session.OAuthToken);
|
||||
|
||||
string response = SteamWeb.Request(url, "POST", postData);
|
||||
if (response == null) return false;
|
||||
|
||||
try
|
||||
{
|
||||
var refreshResponse = JsonConvert.DeserializeObject<RefreshSessionDataResponse>(response);
|
||||
if (refreshResponse == null || refreshResponse.Response == null || String.IsNullOrEmpty(refreshResponse.Response.Token))
|
||||
return false;
|
||||
|
||||
string token = this.Session.SteamID + "%7C%7C" + refreshResponse.Response.Token;
|
||||
string tokenSecure = this.Session.SteamID + "%7C%7C" + refreshResponse.Response.TokenSecure;
|
||||
|
||||
this.Session.SteamLogin = token;
|
||||
this.Session.SteamLoginSecure = tokenSecure;
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refreshes the Steam session. Necessary to perform confirmations if your session has expired or changed.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> RefreshSessionAsync()
|
||||
{
|
||||
string url = APIEndpoints.MOBILEAUTH_GETWGTOKEN;
|
||||
NameValueCollection postData = new NameValueCollection();
|
||||
postData.Add("access_token", this.Session.OAuthToken);
|
||||
|
||||
string response = await SteamWeb.RequestAsync(url, "POST", postData);
|
||||
if (response == null) return false;
|
||||
|
||||
try
|
||||
{
|
||||
var refreshResponse = JsonConvert.DeserializeObject<RefreshSessionDataResponse>(response);
|
||||
if (refreshResponse == null || refreshResponse.Response == null || String.IsNullOrEmpty(refreshResponse.Response.Token))
|
||||
return false;
|
||||
|
||||
string token = this.Session.SteamID + "%7C%7C" + refreshResponse.Response.Token;
|
||||
string tokenSecure = this.Session.SteamID + "%7C%7C" + refreshResponse.Response.TokenSecure;
|
||||
|
||||
this.Session.SteamLogin = token;
|
||||
this.Session.SteamLoginSecure = tokenSecure;
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private ConfirmationDetailsResponse _getConfirmationDetails(Confirmation conf)
|
||||
{
|
||||
string url = APIEndpoints.COMMUNITY_BASE + "/mobileconf/details/" + conf.ID + "?";
|
||||
string queryString = GenerateConfirmationQueryParams("details");
|
||||
url += queryString;
|
||||
|
||||
CookieContainer cookies = new CookieContainer();
|
||||
this.Session.AddCookies(cookies);
|
||||
string referer = GenerateConfirmationURL();
|
||||
|
||||
string response = SteamWeb.Request(url, "GET", null, cookies, null);
|
||||
if (String.IsNullOrEmpty(response)) return null;
|
||||
|
||||
var confResponse = JsonConvert.DeserializeObject<ConfirmationDetailsResponse>(response);
|
||||
if (confResponse == null) return null;
|
||||
return confResponse;
|
||||
}
|
||||
|
||||
private bool _sendConfirmationAjax(Confirmation conf, string op)
|
||||
{
|
||||
string url = APIEndpoints.COMMUNITY_BASE + "/mobileconf/ajaxop";
|
||||
string queryString = "?op=" + op + "&";
|
||||
queryString += GenerateConfirmationQueryParams(op);
|
||||
queryString += "&cid=" + conf.ID + "&ck=" + conf.Key;
|
||||
url += queryString;
|
||||
|
||||
CookieContainer cookies = new CookieContainer();
|
||||
this.Session.AddCookies(cookies);
|
||||
string referer = GenerateConfirmationURL();
|
||||
|
||||
string response = SteamWeb.Request(url, "GET", null, cookies, null);
|
||||
if (response == null) return false;
|
||||
|
||||
SendConfirmationResponse confResponse = JsonConvert.DeserializeObject<SendConfirmationResponse>(response);
|
||||
return confResponse.Success;
|
||||
}
|
||||
|
||||
public string GenerateConfirmationURL(string tag = "conf")
|
||||
{
|
||||
string endpoint = APIEndpoints.COMMUNITY_BASE + "/mobileconf/conf?";
|
||||
string queryString = GenerateConfirmationQueryParams(tag);
|
||||
return endpoint + queryString;
|
||||
}
|
||||
|
||||
public string GenerateConfirmationQueryParams(string tag)
|
||||
{
|
||||
if (String.IsNullOrEmpty(DeviceID))
|
||||
throw new ArgumentException("Device ID is not present");
|
||||
|
||||
long time = TimeAligner.GetSteamTime();
|
||||
return "p=" + this.DeviceID + "&a=" + this.Session.SteamID.ToString() + "&k=" + _generateConfirmationHashForTime(time, tag) + "&t=" + time + "&m=android&tag=" + tag;
|
||||
}
|
||||
|
||||
private string _generateConfirmationHashForTime(long time, string tag)
|
||||
{
|
||||
byte[] decode = Convert.FromBase64String(this.IdentitySecret);
|
||||
int n2 = 8;
|
||||
if (tag != null)
|
||||
{
|
||||
if (tag.Length > 32)
|
||||
{
|
||||
n2 = 8 + 32;
|
||||
}
|
||||
else
|
||||
{
|
||||
n2 = 8 + tag.Length;
|
||||
}
|
||||
}
|
||||
byte[] array = new byte[n2];
|
||||
int n3 = 8;
|
||||
while (true)
|
||||
{
|
||||
int n4 = n3 - 1;
|
||||
if (n3 <= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
array[n4] = (byte)time;
|
||||
time >>= 8;
|
||||
n3 = n4;
|
||||
}
|
||||
if (tag != null)
|
||||
{
|
||||
Array.Copy(Encoding.UTF8.GetBytes(tag), 0, array, 8, n2 - 8);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
HMACSHA1 hmacGenerator = new HMACSHA1();
|
||||
hmacGenerator.Key = decode;
|
||||
byte[] hashedData = hmacGenerator.ComputeHash(array);
|
||||
string encodedData = Convert.ToBase64String(hashedData, Base64FormattingOptions.None);
|
||||
string hash = WebUtility.UrlEncode(encodedData);
|
||||
return hash;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return null; //Fix soon: catch-all is BAD!
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Determine how to detect an invalid session.
|
||||
public class WGTokenInvalidException : Exception
|
||||
{
|
||||
}
|
||||
|
||||
private class RefreshSessionDataResponse
|
||||
{
|
||||
[JsonProperty("response")]
|
||||
public RefreshSessionDataInternalResponse Response { get; set; }
|
||||
internal class RefreshSessionDataInternalResponse
|
||||
{
|
||||
[JsonProperty("token")]
|
||||
public string Token { get; set; }
|
||||
|
||||
[JsonProperty("token_secure")]
|
||||
public string TokenSecure { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
private class RemoveAuthenticatorResponse
|
||||
{
|
||||
[JsonProperty("response")]
|
||||
public RemoveAuthenticatorInternalResponse Response { get; set; }
|
||||
|
||||
internal class RemoveAuthenticatorInternalResponse
|
||||
{
|
||||
[JsonProperty("success")]
|
||||
public bool Success { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
private class SendConfirmationResponse
|
||||
{
|
||||
[JsonProperty("success")]
|
||||
public bool Success { get; set; }
|
||||
}
|
||||
|
||||
private class ConfirmationDetailsResponse
|
||||
{
|
||||
[JsonProperty("success")]
|
||||
public bool Success { get; set; }
|
||||
|
||||
[JsonProperty("html")]
|
||||
public string HTML { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
137
SteamAuth/SteamWeb.cs
Normal file
137
SteamAuth/SteamWeb.cs
Normal file
@@ -0,0 +1,137 @@
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SteamAuth
|
||||
{
|
||||
public class SteamWeb
|
||||
{
|
||||
/// <summary>
|
||||
/// Perform a mobile login request
|
||||
/// </summary>
|
||||
/// <param name="url">API url</param>
|
||||
/// <param name="method">GET or POST</param>
|
||||
/// <param name="data">Name-data pairs</param>
|
||||
/// <param name="cookies">current cookie container</param>
|
||||
/// <returns>response body</returns>
|
||||
public static string MobileLoginRequest(string url, string method, NameValueCollection data = null, CookieContainer cookies = null, NameValueCollection headers = null)
|
||||
{
|
||||
return Request(url, method, data, cookies, headers, APIEndpoints.COMMUNITY_BASE + "/mobilelogin?oauth_client_id=DE45CD61&oauth_scope=read_profile%20write_profile%20read_client%20write_client");
|
||||
}
|
||||
|
||||
public static string Request(string url, string method, NameValueCollection data = null, CookieContainer cookies = null, NameValueCollection headers = null, string referer = APIEndpoints.COMMUNITY_BASE)
|
||||
{
|
||||
string query = (data == null ? string.Empty : string.Join("&", Array.ConvertAll(data.AllKeys, key => String.Format("{0}={1}", WebUtility.UrlEncode(key), WebUtility.UrlEncode(data[key])))));
|
||||
if (method == "GET")
|
||||
{
|
||||
url += (url.Contains("?") ? "&" : "?") + query;
|
||||
}
|
||||
|
||||
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
|
||||
request.Method = method;
|
||||
request.Accept = "text/javascript, text/html, application/xml, text/xml, */*";
|
||||
request.UserAgent = "Mozilla/5.0 (Linux; U; Android 4.1.1; en-us; Google Nexus 4 - 4.1.1 - API 16 - 768x1280 Build/JRO03S) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30";
|
||||
request.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;
|
||||
request.Referer = referer;
|
||||
|
||||
if (headers != null)
|
||||
{
|
||||
request.Headers.Add(headers);
|
||||
}
|
||||
|
||||
if (cookies != null)
|
||||
{
|
||||
request.CookieContainer = cookies;
|
||||
}
|
||||
|
||||
if (method == "POST")
|
||||
{
|
||||
request.ContentType = "application/x-www-form-urlencoded; charset=UTF-8";
|
||||
request.ContentLength = query.Length;
|
||||
|
||||
StreamWriter requestStream = new StreamWriter(request.GetRequestStream());
|
||||
requestStream.Write(query);
|
||||
requestStream.Close();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
|
||||
{
|
||||
if (response.StatusCode != HttpStatusCode.OK)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
using (StreamReader responseStream = new StreamReader(response.GetResponseStream()))
|
||||
{
|
||||
string responseData = responseStream.ReadToEnd();
|
||||
return responseData;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (WebException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<string> RequestAsync(string url, string method, NameValueCollection data = null, CookieContainer cookies = null, NameValueCollection headers = null, string referer = APIEndpoints.COMMUNITY_BASE)
|
||||
{
|
||||
string query = (data == null ? string.Empty : string.Join("&", Array.ConvertAll(data.AllKeys, key => String.Format("{0}={1}", WebUtility.UrlEncode(key), WebUtility.UrlEncode(data[key])))));
|
||||
if (method == "GET")
|
||||
{
|
||||
url += (url.Contains("?") ? "&" : "?") + query;
|
||||
}
|
||||
|
||||
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
|
||||
request.Method = method;
|
||||
request.Accept = "text/javascript, text/html, application/xml, text/xml, */*";
|
||||
request.UserAgent = "Mozilla/5.0 (Linux; U; Android 4.1.1; en-us; Google Nexus 4 - 4.1.1 - API 16 - 768x1280 Build/JRO03S) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30";
|
||||
request.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;
|
||||
request.Referer = referer;
|
||||
|
||||
if (headers != null)
|
||||
{
|
||||
request.Headers.Add(headers);
|
||||
}
|
||||
|
||||
if (cookies != null)
|
||||
{
|
||||
request.CookieContainer = cookies;
|
||||
}
|
||||
|
||||
if (method == "POST")
|
||||
{
|
||||
request.ContentType = "application/x-www-form-urlencoded; charset=UTF-8";
|
||||
request.ContentLength = query.Length;
|
||||
|
||||
StreamWriter requestStream = new StreamWriter(request.GetRequestStream());
|
||||
requestStream.Write(query);
|
||||
requestStream.Close();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
HttpWebResponse response = (HttpWebResponse) await request.GetResponseAsync();
|
||||
|
||||
if (response.StatusCode != HttpStatusCode.OK)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
using (StreamReader responseStream = new StreamReader(response.GetResponseStream()))
|
||||
{
|
||||
string responseData = responseStream.ReadToEnd();
|
||||
return responseData;
|
||||
}
|
||||
}
|
||||
catch (WebException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
84
SteamAuth/TimeAligner.cs
Normal file
84
SteamAuth/TimeAligner.cs
Normal file
@@ -0,0 +1,84 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Net;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace SteamAuth
|
||||
{
|
||||
/// <summary>
|
||||
/// Class to help align system time with the Steam server time. Not super advanced; probably not taking some things into account that it should.
|
||||
/// Necessary to generate up-to-date codes. In general, this will have an error of less than a second, assuming Steam is operational.
|
||||
/// </summary>
|
||||
public class TimeAligner
|
||||
{
|
||||
private static bool _aligned = false;
|
||||
private static int _timeDifference = 0;
|
||||
|
||||
public static long GetSteamTime()
|
||||
{
|
||||
if (!TimeAligner._aligned)
|
||||
{
|
||||
TimeAligner.AlignTime();
|
||||
}
|
||||
return Util.GetSystemUnixTime() + _timeDifference;
|
||||
}
|
||||
|
||||
public static async Task<long> GetSteamTimeAsync()
|
||||
{
|
||||
if (!TimeAligner._aligned)
|
||||
{
|
||||
await TimeAligner.AlignTimeAsync();
|
||||
}
|
||||
return Util.GetSystemUnixTime() + _timeDifference;
|
||||
}
|
||||
|
||||
public static void AlignTime()
|
||||
{
|
||||
long currentTime = Util.GetSystemUnixTime();
|
||||
using (WebClient client = new WebClient())
|
||||
{
|
||||
try
|
||||
{
|
||||
string response = client.UploadString(APIEndpoints.TWO_FACTOR_TIME_QUERY, "steamid=0");
|
||||
TimeQuery query = JsonConvert.DeserializeObject<TimeQuery>(response);
|
||||
TimeAligner._timeDifference = (int)(query.Response.ServerTime - currentTime);
|
||||
TimeAligner._aligned = true;
|
||||
}
|
||||
catch (WebException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task AlignTimeAsync()
|
||||
{
|
||||
long currentTime = Util.GetSystemUnixTime();
|
||||
WebClient client = new WebClient();
|
||||
try
|
||||
{
|
||||
string response = await client.UploadStringTaskAsync(new Uri(APIEndpoints.TWO_FACTOR_TIME_QUERY), "steamid=0");
|
||||
TimeQuery query = JsonConvert.DeserializeObject<TimeQuery>(response);
|
||||
TimeAligner._timeDifference = (int)(query.Response.ServerTime - currentTime);
|
||||
TimeAligner._aligned = true;
|
||||
}
|
||||
catch (WebException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
internal class TimeQuery
|
||||
{
|
||||
[JsonProperty("response")]
|
||||
internal TimeQueryResponse Response { get; set; }
|
||||
|
||||
internal class TimeQueryResponse
|
||||
{
|
||||
[JsonProperty("server_time")]
|
||||
public long ServerTime { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
252
SteamAuth/UserLogin.cs
Normal file
252
SteamAuth/UserLogin.cs
Normal file
@@ -0,0 +1,252 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.Net;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace SteamAuth
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles logging the user into the mobile Steam website. Necessary to generate OAuth token and session cookies.
|
||||
/// </summary>
|
||||
public class UserLogin
|
||||
{
|
||||
public string Username;
|
||||
public string Password;
|
||||
public ulong SteamID;
|
||||
|
||||
public bool RequiresCaptcha;
|
||||
public string CaptchaGID = null;
|
||||
public string CaptchaText = null;
|
||||
|
||||
public bool RequiresEmail;
|
||||
public string EmailDomain = null;
|
||||
public string EmailCode = null;
|
||||
|
||||
public bool Requires2FA;
|
||||
public string TwoFactorCode = null;
|
||||
|
||||
public SessionData Session = null;
|
||||
public bool LoggedIn = false;
|
||||
|
||||
private CookieContainer _cookies = new CookieContainer();
|
||||
|
||||
public UserLogin(string username, string password)
|
||||
{
|
||||
this.Username = username;
|
||||
this.Password = password;
|
||||
}
|
||||
|
||||
public LoginResult DoLogin()
|
||||
{
|
||||
var postData = new NameValueCollection();
|
||||
var cookies = _cookies;
|
||||
string response = null;
|
||||
|
||||
if (cookies.Count == 0)
|
||||
{
|
||||
//Generate a SessionID
|
||||
cookies.Add(new Cookie("mobileClientVersion", "0 (2.1.3)", "/", ".steamcommunity.com"));
|
||||
cookies.Add(new Cookie("mobileClient", "android", "/", ".steamcommunity.com"));
|
||||
cookies.Add(new Cookie("Steam_Language", "english", "/", ".steamcommunity.com"));
|
||||
|
||||
NameValueCollection headers = new NameValueCollection();
|
||||
headers.Add("X-Requested-With", "com.valvesoftware.android.steam.community");
|
||||
|
||||
SteamWeb.MobileLoginRequest("https://steamcommunity.com/login?oauth_client_id=DE45CD61&oauth_scope=read_profile%20write_profile%20read_client%20write_client", "GET", null, cookies, headers);
|
||||
}
|
||||
|
||||
postData.Add("username", this.Username);
|
||||
response = SteamWeb.MobileLoginRequest(APIEndpoints.COMMUNITY_BASE + "/login/getrsakey", "POST", postData, cookies);
|
||||
if (response == null || response.Contains("<BODY>\nAn error occurred while processing your request.")) return LoginResult.GeneralFailure;
|
||||
|
||||
var rsaResponse = JsonConvert.DeserializeObject<RSAResponse>(response);
|
||||
|
||||
if (!rsaResponse.Success)
|
||||
{
|
||||
return LoginResult.BadRSA;
|
||||
}
|
||||
|
||||
RNGCryptoServiceProvider secureRandom = new RNGCryptoServiceProvider();
|
||||
byte[] encryptedPasswordBytes;
|
||||
using (var rsaEncryptor = new RSACryptoServiceProvider())
|
||||
{
|
||||
var passwordBytes = Encoding.ASCII.GetBytes(this.Password);
|
||||
var rsaParameters = rsaEncryptor.ExportParameters(false);
|
||||
rsaParameters.Exponent = Util.HexStringToByteArray(rsaResponse.Exponent);
|
||||
rsaParameters.Modulus = Util.HexStringToByteArray(rsaResponse.Modulus);
|
||||
rsaEncryptor.ImportParameters(rsaParameters);
|
||||
encryptedPasswordBytes = rsaEncryptor.Encrypt(passwordBytes, false);
|
||||
}
|
||||
|
||||
string encryptedPassword = Convert.ToBase64String(encryptedPasswordBytes);
|
||||
|
||||
postData.Clear();
|
||||
postData.Add("username", this.Username);
|
||||
postData.Add("password", encryptedPassword);
|
||||
|
||||
postData.Add("twofactorcode", this.TwoFactorCode ?? "");
|
||||
|
||||
postData.Add("captchagid", this.RequiresCaptcha ? this.CaptchaGID : "-1");
|
||||
postData.Add("captcha_text", this.RequiresCaptcha ? this.CaptchaText : "");
|
||||
|
||||
postData.Add("emailsteamid", (this.Requires2FA || this.RequiresEmail) ? this.SteamID.ToString() : "");
|
||||
postData.Add("emailauth", this.RequiresEmail ? this.EmailCode : "");
|
||||
|
||||
postData.Add("rsatimestamp", rsaResponse.Timestamp);
|
||||
postData.Add("remember_login", "false");
|
||||
postData.Add("oauth_client_id", "DE45CD61");
|
||||
postData.Add("oauth_scope", "read_profile write_profile read_client write_client");
|
||||
postData.Add("loginfriendlyname", "#login_emailauth_friendlyname_mobile");
|
||||
postData.Add("donotcache", Util.GetSystemUnixTime().ToString());
|
||||
|
||||
response = SteamWeb.MobileLoginRequest(APIEndpoints.COMMUNITY_BASE + "/login/dologin", "POST", postData, cookies);
|
||||
if (response == null) return LoginResult.GeneralFailure;
|
||||
|
||||
var loginResponse = JsonConvert.DeserializeObject<LoginResponse>(response);
|
||||
|
||||
if (loginResponse.Message != null && loginResponse.Message.Contains("Incorrect login"))
|
||||
{
|
||||
return LoginResult.BadCredentials;
|
||||
}
|
||||
|
||||
if (loginResponse.CaptchaNeeded)
|
||||
{
|
||||
this.RequiresCaptcha = true;
|
||||
this.CaptchaGID = loginResponse.CaptchaGID;
|
||||
return LoginResult.NeedCaptcha;
|
||||
}
|
||||
|
||||
if (loginResponse.EmailAuthNeeded)
|
||||
{
|
||||
this.RequiresEmail = true;
|
||||
this.SteamID = loginResponse.EmailSteamID;
|
||||
return LoginResult.NeedEmail;
|
||||
}
|
||||
|
||||
if (loginResponse.TwoFactorNeeded && !loginResponse.Success)
|
||||
{
|
||||
this.Requires2FA = true;
|
||||
return LoginResult.Need2FA;
|
||||
}
|
||||
|
||||
if (loginResponse.Message != null && loginResponse.Message.Contains("too many login failures"))
|
||||
{
|
||||
return LoginResult.TooManyFailedLogins;
|
||||
}
|
||||
|
||||
if (loginResponse.OAuthData == null || loginResponse.OAuthData.OAuthToken == null || loginResponse.OAuthData.OAuthToken.Length == 0)
|
||||
{
|
||||
return LoginResult.GeneralFailure;
|
||||
}
|
||||
|
||||
if (!loginResponse.LoginComplete)
|
||||
{
|
||||
return LoginResult.BadCredentials;
|
||||
}
|
||||
else
|
||||
{
|
||||
var readableCookies = cookies.GetCookies(new Uri("https://steamcommunity.com"));
|
||||
var oAuthData = loginResponse.OAuthData;
|
||||
|
||||
SessionData session = new SessionData();
|
||||
session.OAuthToken = oAuthData.OAuthToken;
|
||||
session.SteamID = oAuthData.SteamID;
|
||||
session.SteamLogin = session.SteamID + "%7C%7C" + oAuthData.SteamLogin;
|
||||
session.SteamLoginSecure = session.SteamID + "%7C%7C" + oAuthData.SteamLoginSecure;
|
||||
session.WebCookie = oAuthData.Webcookie;
|
||||
session.SessionID = readableCookies["sessionid"].Value;
|
||||
this.Session = session;
|
||||
this.LoggedIn = true;
|
||||
return LoginResult.LoginOkay;
|
||||
}
|
||||
}
|
||||
|
||||
private class LoginResponse
|
||||
{
|
||||
[JsonProperty("success")]
|
||||
public bool Success { get; set; }
|
||||
|
||||
[JsonProperty("login_complete")]
|
||||
public bool LoginComplete { get; set; }
|
||||
|
||||
[JsonProperty("oauth")]
|
||||
public string OAuthDataString { get; set; }
|
||||
|
||||
public OAuth OAuthData
|
||||
{
|
||||
get
|
||||
{
|
||||
return OAuthDataString != null ? JsonConvert.DeserializeObject<OAuth>(OAuthDataString) : null;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonProperty("captcha_needed")]
|
||||
public bool CaptchaNeeded { get; set; }
|
||||
|
||||
[JsonProperty("captcha_gid")]
|
||||
public string CaptchaGID { get; set; }
|
||||
|
||||
[JsonProperty("emailsteamid")]
|
||||
public ulong EmailSteamID { get; set; }
|
||||
|
||||
[JsonProperty("emailauth_needed")]
|
||||
public bool EmailAuthNeeded { get; set; }
|
||||
|
||||
[JsonProperty("requires_twofactor")]
|
||||
public bool TwoFactorNeeded { get; set; }
|
||||
|
||||
[JsonProperty("message")]
|
||||
public string Message { get; set; }
|
||||
|
||||
internal class OAuth
|
||||
{
|
||||
[JsonProperty("steamid")]
|
||||
public ulong SteamID { get; set; }
|
||||
|
||||
[JsonProperty("oauth_token")]
|
||||
public string OAuthToken { get; set; }
|
||||
|
||||
[JsonProperty("wgtoken")]
|
||||
public string SteamLogin { get; set; }
|
||||
|
||||
[JsonProperty("wgtoken_secure")]
|
||||
public string SteamLoginSecure { get; set; }
|
||||
|
||||
[JsonProperty("webcookie")]
|
||||
public string Webcookie { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
private class RSAResponse
|
||||
{
|
||||
[JsonProperty("success")]
|
||||
public bool Success { get; set; }
|
||||
|
||||
[JsonProperty("publickey_exp")]
|
||||
public string Exponent { get; set; }
|
||||
|
||||
[JsonProperty("publickey_mod")]
|
||||
public string Modulus { get; set; }
|
||||
|
||||
[JsonProperty("timestamp")]
|
||||
public string Timestamp { get; set; }
|
||||
|
||||
[JsonProperty("steamid")]
|
||||
public ulong SteamID { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
public enum LoginResult
|
||||
{
|
||||
LoginOkay,
|
||||
GeneralFailure,
|
||||
BadRSA,
|
||||
BadCredentials,
|
||||
NeedCaptcha,
|
||||
Need2FA,
|
||||
NeedEmail,
|
||||
TooManyFailedLogins,
|
||||
}
|
||||
}
|
||||
24
SteamAuth/Util.cs
Normal file
24
SteamAuth/Util.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
|
||||
namespace SteamAuth
|
||||
{
|
||||
public class Util
|
||||
{
|
||||
public static long GetSystemUnixTime()
|
||||
{
|
||||
return (long)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds;
|
||||
}
|
||||
|
||||
public static byte[] HexStringToByteArray(string hex)
|
||||
{
|
||||
int hexLen = hex.Length;
|
||||
byte[] ret = new byte[hexLen / 2];
|
||||
for (int i = 0; i < hexLen; i += 2)
|
||||
{
|
||||
ret[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
4
SteamAuth/packages.config
Normal file
4
SteamAuth/packages.config
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Newtonsoft.Json" version="8.0.3" targetFramework="net451" />
|
||||
</packages>
|
||||
9
appveyor.yml
Normal file
9
appveyor.yml
Normal file
@@ -0,0 +1,9 @@
|
||||
version: 1.0.{build}-{branch}
|
||||
image: Visual Studio 2015
|
||||
configuration: Release
|
||||
platform: Any CPU
|
||||
clone_depth: 10
|
||||
build:
|
||||
project: ArchiSteamFarm.sln
|
||||
parallel: true
|
||||
verbosity: minimal
|
||||
66
cc.sh
Executable file
66
cc.sh
Executable file
@@ -0,0 +1,66 @@
|
||||
#!/bin/bash
|
||||
set -eu
|
||||
|
||||
BUILD="Release"
|
||||
CLEAN=0
|
||||
|
||||
MONO_ARGS=("--aot" "--llvm" "--server" "-O=all")
|
||||
XBUILD_ARGS=("/nologo")
|
||||
BINARIES=("ArchiSteamFarm/bin/Release/ArchiSteamFarm.exe")
|
||||
SOLUTION="ArchiSteamFarm.sln"
|
||||
|
||||
PRINT_USAGE() {
|
||||
echo "Usage: $0 [--clean] [debug/release]"
|
||||
exit 1
|
||||
}
|
||||
|
||||
for ARG in "$@"; do
|
||||
case "$ARG" in
|
||||
release|Release) BUILD="Release" ;;
|
||||
debug|Debug) BUILD="Debug" ;;
|
||||
--clean) CLEAN=1 ;;
|
||||
*) PRINT_USAGE
|
||||
esac
|
||||
done
|
||||
|
||||
XBUILD_ARGS+=("/p:Configuration=$BUILD")
|
||||
|
||||
cd "$(dirname "$(readlink -f "$0")")"
|
||||
|
||||
if [[ -d ".git" ]] && hash git &>/dev/null; then
|
||||
git pull
|
||||
fi
|
||||
|
||||
if [[ ! -f "$SOLUTION" ]]; then
|
||||
echo "ERROR: $SOLUTION could not be found!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if hash nuget &>/dev/null; then
|
||||
nuget restore "$SOLUTION"
|
||||
fi
|
||||
|
||||
if [[ "$CLEAN" -eq 1 ]]; then
|
||||
rm -rf out
|
||||
xbuild "${XBUILD_ARGS[@]}" "/t:Clean" "$SOLUTION"
|
||||
fi
|
||||
|
||||
xbuild "${XBUILD_ARGS[@]}" "$SOLUTION"
|
||||
|
||||
if [[ ! -f "${BINARIES[0]}" ]]; then
|
||||
echo "ERROR: ${BINARIES[0]} binary could not be found!"
|
||||
fi
|
||||
|
||||
# If it's release build, use Mono AOT for output binaries
|
||||
if [[ "$BUILD" = "Release" ]]; then
|
||||
for BINARY in "${BINARIES[@]}"; do
|
||||
if [[ ! -f "$BINARY" ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
mono "${MONO_ARGS[@]}" "$BINARY"
|
||||
done
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "Compilation finished successfully! :)"
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
packages/Newtonsoft.Json.8.0.3/Newtonsoft.Json.8.0.3.nupkg
vendored
Normal file
BIN
packages/Newtonsoft.Json.8.0.3/Newtonsoft.Json.8.0.3.nupkg
vendored
Normal file
Binary file not shown.
BIN
packages/Newtonsoft.Json.8.0.3/lib/net20/Newtonsoft.Json.dll
vendored
Normal file
BIN
packages/Newtonsoft.Json.8.0.3/lib/net20/Newtonsoft.Json.dll
vendored
Normal file
Binary file not shown.
@@ -76,38 +76,6 @@
|
||||
<param name="readRootValueAsArray">if set to <c>true</c> the root object will be read as a JSON array.</param>
|
||||
<param name="dateTimeKindHandling">The <see cref="T:System.DateTimeKind" /> used when reading <see cref="T:System.DateTime"/> values from BSON.</param>
|
||||
</member>
|
||||
<member name="M:Newtonsoft.Json.Bson.BsonReader.ReadAsBytes">
|
||||
<summary>
|
||||
Reads the next JSON token from the stream as a <see cref="T:System.Byte"/>[].
|
||||
</summary>
|
||||
<returns>
|
||||
A <see cref="T:System.Byte"/>[] or a null reference if the next JSON token is null. This method will return <c>null</c> at the end of an array.
|
||||
</returns>
|
||||
</member>
|
||||
<member name="M:Newtonsoft.Json.Bson.BsonReader.ReadAsDecimal">
|
||||
<summary>
|
||||
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
|
||||
</summary>
|
||||
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
|
||||
</member>
|
||||
<member name="M:Newtonsoft.Json.Bson.BsonReader.ReadAsInt32">
|
||||
<summary>
|
||||
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
|
||||
</summary>
|
||||
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
|
||||
</member>
|
||||
<member name="M:Newtonsoft.Json.Bson.BsonReader.ReadAsString">
|
||||
<summary>
|
||||
Reads the next JSON token from the stream as a <see cref="T:System.String"/>.
|
||||
</summary>
|
||||
<returns>A <see cref="T:System.String"/>. This method will return <c>null</c> at the end of an array.</returns>
|
||||
</member>
|
||||
<member name="M:Newtonsoft.Json.Bson.BsonReader.ReadAsDateTime">
|
||||
<summary>
|
||||
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
|
||||
</summary>
|
||||
<returns>A <see cref="T:System.String"/>. This method will return <c>null</c> at the end of an array.</returns>
|
||||
</member>
|
||||
<member name="M:Newtonsoft.Json.Bson.BsonReader.Read">
|
||||
<summary>
|
||||
Reads the next JSON token from the stream.
|
||||
@@ -636,6 +604,12 @@
|
||||
Initializes a new instance of the <see cref="T:Newtonsoft.Json.Converters.StringEnumConverter"/> class.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Newtonsoft.Json.Converters.StringEnumConverter.#ctor(System.Boolean)">
|
||||
<summary>
|
||||
Initializes a new instance of the <see cref="T:Newtonsoft.Json.Converters.StringEnumConverter"/> class.
|
||||
</summary>
|
||||
<param name="camelCaseText"><c>true</c> if the written enum text will be camel case; otherwise, <c>false</c>.</param>
|
||||
</member>
|
||||
<member name="M:Newtonsoft.Json.Converters.StringEnumConverter.WriteJson(Newtonsoft.Json.JsonWriter,System.Object,Newtonsoft.Json.JsonSerializer)">
|
||||
<summary>
|
||||
Writes the JSON representation of the object.
|
||||
@@ -926,6 +900,25 @@
|
||||
Causes child objects to be indented according to the <see cref="P:Newtonsoft.Json.JsonTextWriter.Indentation"/> and <see cref="P:Newtonsoft.Json.JsonTextWriter.IndentChar"/> settings.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Newtonsoft.Json.IArrayPool`1">
|
||||
<summary>
|
||||
Provides an interface for using pooled arrays.
|
||||
</summary>
|
||||
<typeparam name="T">The array type content.</typeparam>
|
||||
</member>
|
||||
<member name="M:Newtonsoft.Json.IArrayPool`1.Rent(System.Int32)">
|
||||
<summary>
|
||||
Rent a array from the pool. This array must be returned when it is no longer needed.
|
||||
</summary>
|
||||
<param name="minimumLength">The minimum required length of the array. The returned array may be longer.</param>
|
||||
<returns>The rented array from the pool. This array must be returned when it is no longer needed.</returns>
|
||||
</member>
|
||||
<member name="M:Newtonsoft.Json.IArrayPool`1.Return(`0[])">
|
||||
<summary>
|
||||
Return an array to the pool.
|
||||
</summary>
|
||||
<param name="array">The array that is being returned.</param>
|
||||
</member>
|
||||
<member name="T:Newtonsoft.Json.JsonConstructorAttribute">
|
||||
<summary>
|
||||
Instructs the <see cref="T:Newtonsoft.Json.JsonSerializer"/> to use the specified constructor when deserializing that object.
|
||||
@@ -1028,6 +1021,21 @@
|
||||
Load comments as a <see cref="T:Newtonsoft.Json.Linq.JValue"/> with type <see cref="F:Newtonsoft.Json.Linq.JTokenType.Comment"/>.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Newtonsoft.Json.Linq.LineInfoHandling">
|
||||
<summary>
|
||||
Specifies how line information is handled when loading JSON.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:Newtonsoft.Json.Linq.LineInfoHandling.Ignore">
|
||||
<summary>
|
||||
Ignore line information.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:Newtonsoft.Json.Linq.LineInfoHandling.Load">
|
||||
<summary>
|
||||
Load line information.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Newtonsoft.Json.Linq.JsonLoadSettings">
|
||||
<summary>
|
||||
Specifies the settings used when loading JSON.
|
||||
@@ -1039,6 +1047,12 @@
|
||||
</summary>
|
||||
<value>The JSON comment handling.</value>
|
||||
</member>
|
||||
<member name="P:Newtonsoft.Json.Linq.JsonLoadSettings.LineInfoHandling">
|
||||
<summary>
|
||||
Gets or sets how JSON line info is handled when loading JSON.
|
||||
</summary>
|
||||
<value>The JSON line info handling.</value>
|
||||
</member>
|
||||
<member name="T:Newtonsoft.Json.Linq.JsonMergeSettings">
|
||||
<summary>
|
||||
Specifies the settings used when merging JSON.
|
||||
@@ -1050,6 +1064,12 @@
|
||||
</summary>
|
||||
<value>The method used when merging JSON arrays.</value>
|
||||
</member>
|
||||
<member name="P:Newtonsoft.Json.Linq.JsonMergeSettings.MergeNullValueHandling">
|
||||
<summary>
|
||||
Gets or sets how how null value properties are merged.
|
||||
</summary>
|
||||
<value>How null value properties are merged.</value>
|
||||
</member>
|
||||
<member name="T:Newtonsoft.Json.Linq.MergeArrayHandling">
|
||||
<summary>
|
||||
Specifies how JSON arrays are merged together.
|
||||
@@ -1067,6 +1087,21 @@
|
||||
<member name="F:Newtonsoft.Json.Linq.MergeArrayHandling.Merge">
|
||||
<summary>Merge array items together, matched by index.</summary>
|
||||
</member>
|
||||
<member name="T:Newtonsoft.Json.Linq.MergeNullValueHandling">
|
||||
<summary>
|
||||
Specifies how null value properties are merged.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:Newtonsoft.Json.Linq.MergeNullValueHandling.Ignore">
|
||||
<summary>
|
||||
The content's null value properties will be ignored during merging.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:Newtonsoft.Json.Linq.MergeNullValueHandling.Merge">
|
||||
<summary>
|
||||
The content's null value properties will be merged.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Newtonsoft.Json.Linq.JPropertyDescriptor">
|
||||
<summary>
|
||||
Represents a view of a <see cref="T:Newtonsoft.Json.Linq.JProperty"/>.
|
||||
@@ -2137,38 +2172,6 @@
|
||||
</summary>
|
||||
<param name="token">The token to read from.</param>
|
||||
</member>
|
||||
<member name="M:Newtonsoft.Json.Linq.JTokenReader.ReadAsBytes">
|
||||
<summary>
|
||||
Reads the next JSON token from the stream as a <see cref="T:System.Byte"/>[].
|
||||
</summary>
|
||||
<returns>
|
||||
A <see cref="T:System.Byte"/>[] or a null reference if the next JSON token is null. This method will return <c>null</c> at the end of an array.
|
||||
</returns>
|
||||
</member>
|
||||
<member name="M:Newtonsoft.Json.Linq.JTokenReader.ReadAsDecimal">
|
||||
<summary>
|
||||
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
|
||||
</summary>
|
||||
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
|
||||
</member>
|
||||
<member name="M:Newtonsoft.Json.Linq.JTokenReader.ReadAsInt32">
|
||||
<summary>
|
||||
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
|
||||
</summary>
|
||||
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
|
||||
</member>
|
||||
<member name="M:Newtonsoft.Json.Linq.JTokenReader.ReadAsString">
|
||||
<summary>
|
||||
Reads the next JSON token from the stream as a <see cref="T:System.String"/>.
|
||||
</summary>
|
||||
<returns>A <see cref="T:System.String"/>. This method will return <c>null</c> at the end of an array.</returns>
|
||||
</member>
|
||||
<member name="M:Newtonsoft.Json.Linq.JTokenReader.ReadAsDateTime">
|
||||
<summary>
|
||||
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
|
||||
</summary>
|
||||
<returns>A <see cref="T:System.String"/>. This method will return <c>null</c> at the end of an array.</returns>
|
||||
</member>
|
||||
<member name="M:Newtonsoft.Json.Linq.JTokenReader.Read">
|
||||
<summary>
|
||||
Reads the next JSON token from the stream.
|
||||
@@ -3535,9 +3538,9 @@
|
||||
</member>
|
||||
<member name="M:Newtonsoft.Json.Linq.JValue.CreateUndefined">
|
||||
<summary>
|
||||
Creates a <see cref="T:Newtonsoft.Json.Linq.JValue"/> null value.
|
||||
Creates a <see cref="T:Newtonsoft.Json.Linq.JValue"/> undefined value.
|
||||
</summary>
|
||||
<returns>A <see cref="T:Newtonsoft.Json.Linq.JValue"/> null value.</returns>
|
||||
<returns>A <see cref="T:Newtonsoft.Json.Linq.JValue"/> undefined value.</returns>
|
||||
</member>
|
||||
<member name="P:Newtonsoft.Json.Linq.JValue.Type">
|
||||
<summary>
|
||||
@@ -4449,9 +4452,9 @@
|
||||
</member>
|
||||
<member name="P:Newtonsoft.Json.Serialization.JsonProperty.Order">
|
||||
<summary>
|
||||
Gets or sets the order of serialization and deserialization of a member.
|
||||
Gets or sets the order of serialization of a member.
|
||||
</summary>
|
||||
<value>The numeric order of serialization or deserialization.</value>
|
||||
<value>The numeric order of serialization.</value>
|
||||
</member>
|
||||
<member name="P:Newtonsoft.Json.Serialization.JsonProperty.UnderlyingName">
|
||||
<summary>
|
||||
@@ -4766,6 +4769,11 @@
|
||||
Gets or sets the extension data getter.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Newtonsoft.Json.Serialization.JsonObjectContract.ExtensionDataValueType">
|
||||
<summary>
|
||||
Gets or sets the extension data value type.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Newtonsoft.Json.Serialization.JsonObjectContract.#ctor(System.Type)">
|
||||
<summary>
|
||||
Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.JsonObjectContract"/> class.
|
||||
@@ -6074,6 +6082,11 @@
|
||||
The property must be defined in JSON and cannot be a null value.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:Newtonsoft.Json.Required.DisallowNull">
|
||||
<summary>
|
||||
The property is not required but it cannot be a null value.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:Newtonsoft.Json.PreserveReferencesHandling">
|
||||
<summary>
|
||||
Specifies reference handling options for the <see cref="T:Newtonsoft.Json.JsonSerializer"/>.
|
||||
@@ -6373,7 +6386,7 @@
|
||||
</member>
|
||||
<member name="P:Newtonsoft.Json.JsonSerializerSettings.Converters">
|
||||
<summary>
|
||||
Gets or sets a collection <see cref="T:Newtonsoft.Json.JsonConverter"/> that will be used during serialization.
|
||||
Gets or sets a <see cref="T:Newtonsoft.Json.JsonConverter"/> collection that will be used during serialization.
|
||||
</summary>
|
||||
<value>The converters.</value>
|
||||
</member>
|
||||
@@ -6387,6 +6400,11 @@
|
||||
<summary>
|
||||
Gets or sets how type name writing and reading is handled by the serializer.
|
||||
</summary>
|
||||
<remarks>
|
||||
<see cref="P:Newtonsoft.Json.JsonSerializerSettings.TypeNameHandling"/> should be used with caution when your application deserializes JSON from an external source.
|
||||
Incoming types should be validated with a custom <see cref="T:System.Runtime.Serialization.SerializationBinder"/>
|
||||
when deserializing with a value other than <c>TypeNameHandling.None</c>.
|
||||
</remarks>
|
||||
<value>The type name handling.</value>
|
||||
</member>
|
||||
<member name="P:Newtonsoft.Json.JsonSerializerSettings.MetadataPropertyHandling">
|
||||
@@ -6610,6 +6628,18 @@
|
||||
</summary>
|
||||
<returns>A <see cref="T:System.Nullable`1"/>.</returns>
|
||||
</member>
|
||||
<member name="M:Newtonsoft.Json.JsonValidatingReader.ReadAsDouble">
|
||||
<summary>
|
||||
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
|
||||
</summary>
|
||||
<returns>A <see cref="T:System.Nullable`1"/>.</returns>
|
||||
</member>
|
||||
<member name="M:Newtonsoft.Json.JsonValidatingReader.ReadAsBoolean">
|
||||
<summary>
|
||||
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
|
||||
</summary>
|
||||
<returns>A <see cref="T:System.Nullable`1"/>.</returns>
|
||||
</member>
|
||||
<member name="M:Newtonsoft.Json.JsonValidatingReader.ReadAsString">
|
||||
<summary>
|
||||
Reads the next JSON token from the stream as a <see cref="T:System.String"/>.
|
||||
@@ -6620,7 +6650,7 @@
|
||||
<summary>
|
||||
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
|
||||
</summary>
|
||||
<returns>A <see cref="T:System.String"/>. This method will return <c>null</c> at the end of an array.</returns>
|
||||
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
|
||||
</member>
|
||||
<member name="M:Newtonsoft.Json.JsonValidatingReader.Read">
|
||||
<summary>
|
||||
@@ -6643,7 +6673,7 @@
|
||||
</member>
|
||||
<member name="F:Newtonsoft.Json.MemberSerialization.OptIn">
|
||||
<summary>
|
||||
Only members must be marked with <see cref="T:Newtonsoft.Json.JsonPropertyAttribute"/> or <see cref="!:DataMemberAttribute"/> are serialized.
|
||||
Only members marked with <see cref="T:Newtonsoft.Json.JsonPropertyAttribute"/> or <see cref="!:DataMemberAttribute"/> are serialized.
|
||||
This member serialization mode can also be set by marking the class with <see cref="!:DataContractAttribute"/>.
|
||||
</summary>
|
||||
</member>
|
||||
@@ -6685,6 +6715,11 @@
|
||||
</summary>
|
||||
<param name="reader">The <c>TextReader</c> containing the XML data to read.</param>
|
||||
</member>
|
||||
<member name="P:Newtonsoft.Json.JsonTextReader.ArrayPool">
|
||||
<summary>
|
||||
Gets or sets the reader's character buffer pool.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:Newtonsoft.Json.JsonTextReader.Read">
|
||||
<summary>
|
||||
Reads the next JSON token from the stream.
|
||||
@@ -6693,21 +6728,13 @@
|
||||
true if the next token was read successfully; false if there are no more tokens to read.
|
||||
</returns>
|
||||
</member>
|
||||
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsBytes">
|
||||
<summary>
|
||||
Reads the next JSON token from the stream as a <see cref="T:System.Byte"/>[].
|
||||
</summary>
|
||||
<returns>
|
||||
A <see cref="T:System.Byte"/>[] or a null reference if the next JSON token is null. This method will return <c>null</c> at the end of an array.
|
||||
</returns>
|
||||
</member>
|
||||
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsDecimal">
|
||||
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsInt32">
|
||||
<summary>
|
||||
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
|
||||
</summary>
|
||||
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
|
||||
</member>
|
||||
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsInt32">
|
||||
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsDateTime">
|
||||
<summary>
|
||||
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
|
||||
</summary>
|
||||
@@ -6719,11 +6746,29 @@
|
||||
</summary>
|
||||
<returns>A <see cref="T:System.String"/>. This method will return <c>null</c> at the end of an array.</returns>
|
||||
</member>
|
||||
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsDateTime">
|
||||
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsBytes">
|
||||
<summary>
|
||||
Reads the next JSON token from the stream as a <see cref="T:System.Byte"/>[].
|
||||
</summary>
|
||||
<returns>A <see cref="T:System.Byte"/>[] or a null reference if the next JSON token is null. This method will return <c>null</c> at the end of an array.</returns>
|
||||
</member>
|
||||
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsBoolean">
|
||||
<summary>
|
||||
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
|
||||
</summary>
|
||||
<returns>A <see cref="T:System.String"/>. This method will return <c>null</c> at the end of an array.</returns>
|
||||
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
|
||||
</member>
|
||||
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsDecimal">
|
||||
<summary>
|
||||
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
|
||||
</summary>
|
||||
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
|
||||
</member>
|
||||
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsDouble">
|
||||
<summary>
|
||||
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
|
||||
</summary>
|
||||
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
|
||||
</member>
|
||||
<member name="M:Newtonsoft.Json.JsonTextReader.Close">
|
||||
<summary>
|
||||
@@ -6814,9 +6859,9 @@
|
||||
</member>
|
||||
<member name="P:Newtonsoft.Json.JsonPropertyAttribute.Order">
|
||||
<summary>
|
||||
Gets or sets the order of serialization and deserialization of a member.
|
||||
Gets or sets the order of serialization of a member.
|
||||
</summary>
|
||||
<value>The numeric order of serialization or deserialization.</value>
|
||||
<value>The numeric order of serialization.</value>
|
||||
</member>
|
||||
<member name="P:Newtonsoft.Json.JsonPropertyAttribute.Required">
|
||||
<summary>
|
||||
@@ -6871,6 +6916,11 @@
|
||||
Represents a writer that provides a fast, non-cached, forward-only way of generating JSON data.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Newtonsoft.Json.JsonTextWriter.ArrayPool">
|
||||
<summary>
|
||||
Gets or sets the writer's character array pool.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:Newtonsoft.Json.JsonTextWriter.Indentation">
|
||||
<summary>
|
||||
Gets or sets how many IndentChars to write for each level in the hierarchy when <see cref="T:Newtonsoft.Json.Formatting"/> is set to <c>Formatting.Indented</c>.
|
||||
@@ -7460,6 +7510,18 @@
|
||||
</summary>
|
||||
<returns>A <see cref="T:System.Byte"/>[] or a null reference if the next JSON token is null. This method will return <c>null</c> at the end of an array.</returns>
|
||||
</member>
|
||||
<member name="M:Newtonsoft.Json.JsonReader.ReadAsDouble">
|
||||
<summary>
|
||||
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
|
||||
</summary>
|
||||
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
|
||||
</member>
|
||||
<member name="M:Newtonsoft.Json.JsonReader.ReadAsBoolean">
|
||||
<summary>
|
||||
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
|
||||
</summary>
|
||||
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
|
||||
</member>
|
||||
<member name="M:Newtonsoft.Json.JsonReader.ReadAsDecimal">
|
||||
<summary>
|
||||
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
|
||||
@@ -7470,7 +7532,7 @@
|
||||
<summary>
|
||||
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
|
||||
</summary>
|
||||
<returns>A <see cref="T:System.String"/>. This method will return <c>null</c> at the end of an array.</returns>
|
||||
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
|
||||
</member>
|
||||
<member name="M:Newtonsoft.Json.JsonReader.Skip">
|
||||
<summary>
|
||||
@@ -8066,6 +8128,11 @@
|
||||
<summary>
|
||||
Gets or sets how type name writing and reading is handled by the serializer.
|
||||
</summary>
|
||||
<remarks>
|
||||
<see cref="P:Newtonsoft.Json.JsonSerializer.TypeNameHandling"/> should be used with caution when your application deserializes JSON from an external source.
|
||||
Incoming types should be validated with a custom <see cref="T:System.Runtime.Serialization.SerializationBinder"/>
|
||||
when deserializing with a value other than <c>TypeNameHandling.None</c>.
|
||||
</remarks>
|
||||
</member>
|
||||
<member name="P:Newtonsoft.Json.JsonSerializer.TypeNameAssemblyFormat">
|
||||
<summary>
|
||||
@@ -8981,6 +9048,11 @@
|
||||
<summary>
|
||||
Specifies type name handling options for the <see cref="T:Newtonsoft.Json.JsonSerializer"/>.
|
||||
</summary>
|
||||
<remarks>
|
||||
<see cref="T:Newtonsoft.Json.TypeNameHandling"/> should be used with caution when your application deserializes JSON from an external source.
|
||||
Incoming types should be validated with a custom <see cref="T:System.Runtime.Serialization.SerializationBinder"/>
|
||||
when deserializing with a value other than <c>TypeNameHandling.None</c>.
|
||||
</remarks>
|
||||
</member>
|
||||
<member name="F:Newtonsoft.Json.TypeNameHandling.None">
|
||||
<summary>
|
||||
@@ -9535,6 +9607,12 @@
|
||||
</summary>
|
||||
<param name="ws">The string of white space characters.</param>
|
||||
</member>
|
||||
<member name="M:Newtonsoft.Json.JsonWriter.Dispose(System.Boolean)">
|
||||
<summary>
|
||||
Releases unmanaged and - optionally - managed resources
|
||||
</summary>
|
||||
<param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
|
||||
</member>
|
||||
<member name="M:Newtonsoft.Json.JsonWriter.SetWriteState(Newtonsoft.Json.JsonToken,System.Object)">
|
||||
<summary>
|
||||
Sets the state of the JsonWriter,
|
||||
BIN
packages/Newtonsoft.Json.8.0.3/lib/net35/Newtonsoft.Json.dll
vendored
Normal file
BIN
packages/Newtonsoft.Json.8.0.3/lib/net35/Newtonsoft.Json.dll
vendored
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user