Compare commits

...

359 Commits

Author SHA1 Message Date
JustArchi
85dea3ab70 Mono: Prefer performance over memory
Every user can further fine-tune it however he wishes with https://github.com/JustArchi/ArchiSteamFarm/wiki/Low-memory-setup
2016-06-03 00:16:31 +02:00
JustArchi
aa9d78af95 Misc 2016-06-03 00:06:13 +02:00
JustArchi
550effd7c1 EXPERIMENTAL: Base key on classID only
I've just received trade offer from Endorlight game which used different combinations of classID/instanceID for the same items, such as <1450080555, 246376127> and <1450080555, 0> for Birdy trading card.
This is the first time I've seen something like that, as up to this point all classID + instanceID matched nicely. It's not good because it results in possibly wrong results regarding numbers of the same cards.
It's not any major problem scamming-wise, as ASF does other checks (including appID checks) so in worst case we could accept a trade that is not neutral+ for us, such as 1 -> 2 dupes one.

This commit fixes wrong logic for that particular trade I've encountered, and many similar ones, but I have no idea if we can really base key on classID only.
Number seems big enough to fit all steam items, but if there ever happens item that will have same classID as other one, we'll be in deep sh*t.

I ran a test on my Steam account with 7.5k+ items and didn't find any item which could cause such conflict, so I can at least assume that the issue doesn't exist for majority of Steam users.
Sadly there doesn't seem to be any good way to solve it either, as if such conflicts happens, then we will have 3 items such as <7, 1>, <7, 4> and <7, 0>. We can't know if 0 should be 1 or 4 (thanks GabeN).
This seems to be best solution for that problem, and until we find clasID conflict, we can sleep in peace.
2016-06-02 23:56:59 +02:00
JustArchi
548146cb65 Closes #240 2016-06-02 08:21:44 +02:00
JustArchi
4ca39da1f2 Misc 2016-06-01 22:45:51 +02:00
JustArchi
f406034c63 Mono: Prefer more aggressive GC 2016-06-01 22:43:11 +02:00
JustArchi
2b5b38aa07 Packages update 2016-06-01 14:37:50 +02:00
JustArchi
b8dbddd6f4 Misc 2016-06-01 02:34:38 +02:00
JustArchi
8ce70889d7 Misc 2016-06-01 02:19:22 +02:00
JustArchi
93d235c1b2 Bump 2016-05-30 23:30:00 +02:00
JustArchi
73af6b369a Fix ASF accidentally dismissing profile notifications 2016-05-30 23:25:50 +02:00
JustArchi
2d91a1ed26 Misc 2016-05-30 13:13:24 +02:00
JustArchi
7af0027c66 Correct custom games played while idle with new event-based mechanism 2016-05-30 13:12:22 +02:00
JustArchi
2ce54d9d0a Correct debug leftovers 2016-05-30 13:11:29 +02:00
JustArchi
88d722c14b Fix mono quirks
At least it already works on nightly...
2016-05-30 02:24:56 +02:00
JustArchi
5c46069c67 Bump 2016-05-30 02:11:23 +02:00
JustArchi
c2a1c160e0 Misc 2016-05-30 02:06:04 +02:00
JustArchi
ff971f7615 Misc 2016-05-30 02:03:33 +02:00
JustArchi
cdcaa9b06c Code review 2016-05-30 01:57:06 +02:00
JustArchi
9403985b14 LoggedInElsewhere improvements
Previously we had rather half-blind mechanism of trying to play given games, expecting a disconnect, and reconnecting in AccountPlayingDelay minutes. Now instead we have proper event-based handling - ASF listens on each PlayingSessionState event and updates availability of farming accordingly, not only resuming farming immediately after it's possible, but also limiting number of reconnects and other issues which could be caused by previous approach.

I bet this can be even further improved to not receive LoggedInElsewhere and disconnect error when user starts playing a game, but that's misc compared to gigantic improvement in this commit
2016-05-30 00:09:42 +02:00
JustArchi
a0215d2ac4 Misc 2016-05-28 13:20:42 +02:00
JustArchi
2ef99461d6 Packages update 2016-05-28 13:10:32 +02:00
JustArchi
8e91031510 Slightly improve logic of games played while idle 2016-05-28 13:08:53 +02:00
Łukasz Domeradzki
254cd3843e Mono doesn't support Windows XP anymore 2016-05-25 10:11:41 +02:00
Łukasz Domeradzki
450fc579f7 Update README.md 2016-05-23 07:32:58 +02:00
JustArchi
98f9e716d2 Readme update 2016-05-22 12:25:30 +02:00
JustArchi
86c8aa9b74 Bump 2016-05-18 05:03:51 +02:00
JustArchi
33e0675177 Skip first shutdown in ShutdownOnFarmingFinished, #229 2016-05-18 04:33:17 +02:00
JustArchi
f840f28657 Run debug version with Mono debug 2016-05-18 02:46:29 +02:00
JustArchi
64eb4a51b6 Misc 2016-05-18 02:26:29 +02:00
JustArchi
97affa7b54 Closes #229 2016-05-18 02:25:31 +02:00
JustArchi
65f1d790da Misc 2016-05-16 21:40:20 +02:00
JustArchi
0107185f2a Misc optimization 2016-05-16 03:14:57 +02:00
JustArchi
b8e98f58ac Misc 2016-05-14 00:11:41 +02:00
JustArchi
be9217f493 Bump 2016-05-13 19:51:44 +02:00
JustArchi
b60b864aca Misc 2016-05-13 19:43:48 +02:00
JustArchi
4c64141462 Misc 2016-05-13 19:40:55 +02:00
JustArchi
4b50596709 Enhance concurrent access with ArchiBoT concurrent hashset 2016-05-13 19:39:54 +02:00
JustArchi
622f060575 Prefer flags over comments 2016-05-13 19:20:24 +02:00
JustArchi
20038e8c86 Derp 2016-05-13 06:33:25 +02:00
JustArchi
b8faca2517 Gigantic code review (with ReSharper) 2016-05-13 06:32:42 +02:00
JustArchi
6f93139a18 Bump 2016-05-13 01:43:21 +02:00
JustArchi
50b5c7b87f Correct STM algorithm when overpaying with cards from the same game
us: X X Y
them: Y Y Z

trade:
Y -> X + Z

New:
toGive: 1
toReceive: 2 + 0 -> 0 + 2

diff: 1 - 0 = 1
1 > 0 ? True

Old:
toGive: 1
toReceive: 2

1 > 2 ? False
2016-05-13 01:35:17 +02:00
JustArchi
b60448ef4c Bump 2016-05-12 19:00:26 +02:00
JustArchi
fad08a1fa9 Fix linking new authenticator with null password 2016-05-12 16:32:04 +02:00
JustArchi
a180c100c6 Make use of CellID, closes #223 2016-05-08 15:52:57 +02:00
JustArchi
9d97ca16e8 Misc WCF code review 2016-05-07 15:24:09 +02:00
JustArchi
e833415718 Closes #221 2016-05-07 15:08:24 +02:00
JustArchi
6bb296a674 Bump 2016-05-06 23:58:50 +02:00
JustArchi
f1e5874868 Misc 2016-05-06 23:54:47 +02:00
JustArchi
38e2088d09 Misc 2016-05-06 23:48:33 +02:00
JustArchi
8447e07aa0 Allow overpaying also in steam cards 2016-05-06 23:38:22 +02:00
JustArchi
fbe4e4bc6d Allow STM overpaying 2016-05-06 23:31:00 +02:00
JustArchi
f1213607ce Don't react in any way to failed commands sent by non-owner 2016-05-06 18:07:03 +02:00
JustArchi
de832c530b Misc 2016-05-04 16:43:16 +02:00
JustArchi
0c872b17e2 Misc 2016-05-04 16:41:00 +02:00
JustArchi
5fcbb85b4c Bump 2016-05-03 15:54:26 +02:00
JustArchi
3cdc93d373 Cut SendOnFarmingFinished spam
Allow only one trade to be sent if we didn't farm anything
2016-05-03 15:37:11 +02:00
JustArchi
36e99d9139 Code review 2016-05-03 07:26:41 +02:00
JustArchi
7bee2d468b Bump 2016-04-29 16:52:57 +02:00
JustArchi
8630cc40c8 Small WebBrowser enhancements, closes #212 2016-04-29 16:37:42 +02:00
JustArchi
3683195a0e Misc 2016-04-29 15:47:07 +02:00
JustArchi
7f5b946645 Use API is possible in GetOwnedGames, closes #213 2016-04-29 15:44:11 +02:00
JustArchi
fdb194fe67 Misc 2016-04-27 20:28:36 +02:00
JustArchi
9063b9206b Derp 2016-04-27 20:06:49 +02:00
JustArchi
8d300894e5 Add add proper notice if it still fails 2016-04-27 20:06:17 +02:00
JustArchi
3a0d3c444e Make ASF 2FA less prone to steam fuckups 2016-04-27 20:03:48 +02:00
JustArchi
8118fe0690 Misc 2016-04-26 22:05:58 +02:00
JustArchi
344c2ad23d Misc 2016-04-26 18:01:19 +02:00
JustArchi
d1a6613541 Use asterisks for password field 2016-04-26 14:23:44 +02:00
JustArchi
3dc88c65aa Revert recent trades
When steam is going crazy we're also forgetting trades that simply failed to accept
Also, trade that is not valid currently, might be valid in future, especially STM
2016-04-25 14:34:21 +02:00
JustArchi
46384829c9 Misc 2016-04-25 11:42:46 +02:00
JustArchi
415ee8cc57 Misc 2016-04-25 11:10:39 +02:00
JustArchi
351d45e049 Fix more regressions 2016-04-25 10:46:29 +02:00
JustArchi
f81bbc60c5 Fix regression 2016-04-25 10:40:36 +02:00
JustArchi
eb6e93a172 Code review 2016-04-24 23:32:23 +02:00
JustArchi
e17c3ecf2a Enhance status, closes #207 2016-04-23 19:14:31 +02:00
JustArchi
048b0fb538 Misc 2016-04-23 16:36:43 +02:00
JustArchi
ac7ecb6bb4 Misc 2016-04-23 16:34:00 +02:00
JustArchi
fcaf038dac Bump 2016-04-23 16:24:39 +02:00
JustArchi
f3da5d6afc Fix small regression caused by d0cc10f3c6 2016-04-23 16:21:12 +02:00
JustArchi
84f33fcef4 Bump 2016-04-23 16:12:57 +02:00
JustArchi
22f0d423a3 Closes #205 2016-04-23 14:47:39 +02:00
JustArchi
f1d7609796 Send trades also when we didn't farm anything
Previously I avoided that because of !loot looting entire steam EQ, but now when we loot only cards and boosters, that shouldn't be as annoying as before
2016-04-23 02:15:01 +02:00
JustArchi
77386ecae5 Misc 2016-04-22 17:51:13 +02:00
JustArchi
4e86d21ef8 Misc 2016-04-22 17:50:01 +02:00
JustArchi
044fc87691 Closes #201 2016-04-21 20:14:15 +02:00
JustArchi
79fad62a4d Bump 2016-04-21 19:26:47 +02:00
JustArchi
6622d3b147 Misc 2016-04-21 14:48:24 +02:00
JustArchi
6b6d5429ad Fix 1+1 -> 0+2, #84 2016-04-21 04:50:57 +02:00
JustArchi
d59d0a8415 Misc 2016-04-21 02:37:40 +02:00
JustArchi
e3100d3938 Implement super-smart STM calculations, #84
It's excellent now, also fixed inventory not returning all of my 6k items (sigh)
2016-04-21 02:32:36 +02:00
JustArchi
42c020e552 Add AppVeyor config 2016-04-21 01:09:52 +02:00
JustArchi
554273833b Make travis less annoying 2016-04-21 01:02:07 +02:00
JustArchi
093a29df62 Loot only steam cards and boosters, closes #111 2016-04-21 00:56:15 +02:00
JustArchi
31aa6b2e4a Remove my debug 2016-04-21 00:51:55 +02:00
JustArchi
9f7ecdf054 Add support for inventory descriptions, #111 2016-04-21 00:51:22 +02:00
JustArchi
7c4c74bf84 Bump 2016-04-20 23:57:06 +02:00
JustArchi
b8f03abd8b Fix fuckups 2016-04-20 23:48:33 +02:00
JustArchi
47f846540b Closes #200, thanks to @GUiHKX 2016-04-20 23:44:20 +02:00
JustArchi
bc14713079 Bump 2016-04-20 23:22:16 +02:00
JustArchi
770a8fee66 But keep trading only of cards + foils 2016-04-20 23:16:15 +02:00
JustArchi
1df9af08e6 Bugfixes + other types for STM 2016-04-20 23:02:02 +02:00
JustArchi
27464f6120 Add recent trades optimization 2016-04-20 22:19:31 +02:00
JustArchi
dfd45c6e25 Bump 2016-04-20 21:40:08 +02:00
JustArchi
adc1759cee Misc 2016-04-20 21:34:40 +02:00
JustArchi
88369ec71a Put massive amount of work into STM integration, #84 2016-04-20 21:27:57 +02:00
JustArchi
a5d8ae53dd Bump 2016-04-19 19:54:39 +02:00
JustArchi
cd7b65868a Misc 2016-04-19 12:24:07 +02:00
JustArchi
7575704a01 Misc 2016-04-19 12:23:24 +02:00
JustArchi
b6ce8f435c Use more optimized Slim manual reset events 2016-04-18 18:43:58 +02:00
JustArchi
565acca9fb Add AutoRestart property 2016-04-18 18:38:48 +02:00
JustArchi
610954ba73 Alter logic of key distribution, closes #199 2016-04-18 18:01:49 +02:00
JustArchi
74a748b03f Bump 2016-04-17 00:47:30 +02:00
JustArchi
585a075ec9 Closes #198
It was possible that we initiated a loop for bot that was connected, and it got disconnected shortly after, which could result in infinite loop if DistributeKeys was disabled (and nothing would change that bot to other one)
2016-04-17 00:36:38 +02:00
JustArchi
891d40afe1 Fix potential bug found by zinnerz
We might !stop account waiting in invalid password or game playing condition, which will then initiate connect without checking if it's still valid to do so
2016-04-16 19:24:04 +02:00
JustArchi
387f0dd1c7 Bump 2016-04-15 21:33:58 +02:00
JustArchi
8b4d3c219c Remove GUI app from final zip until I'm happy with the way how it works 2016-04-15 21:29:27 +02:00
JustArchi
365877ec89 Misc 2016-04-15 15:08:50 +02:00
JustArchi
f03a43d573 Misc 2016-04-15 00:32:55 +02:00
JustArchi
d15a9cbfca Misc 2016-04-15 00:18:24 +02:00
JustArchi
acfad624fb Bump 2016-04-14 22:44:35 +02:00
JustArchi
03a7d5f4ac Fix my tests 2016-04-14 22:25:10 +02:00
JustArchi
0f96d84d36 Fix disposed streams for ASF update 2016-04-14 22:23:37 +02:00
JustArchi
0edc9f4ff6 Bump 2016-04-14 21:30:17 +02:00
JustArchi
d11e141ece Misc 2016-04-14 21:16:44 +02:00
JustArchi
9ff3834ed7 Setting cookie should no longer be needed with new WebBrowser 2016-04-14 20:58:33 +02:00
JustArchi
9806703cfa Closes #196 2016-04-14 19:50:53 +02:00
JustArchi
ca0857abd1 Derp 2016-04-13 21:48:49 +02:00
JustArchi
d32d3bfd36 Bump 2016-04-13 21:40:11 +02:00
JustArchi
6efb07eada Properly detect dev environment 2016-04-13 21:30:56 +02:00
JustArchi
2b28f01b1c Restructurize 2016-04-13 21:20:21 +02:00
JustArchi
fb350fe792 Launch shutdown event also when there's nothing to farm 2016-04-13 20:56:37 +02:00
JustArchi
5e73d18cd2 #195 fixes 2016-04-13 16:48:11 +02:00
Łukasz Domeradzki
3ff95995b9 Merge pull request #195 from KlappPc/master
GUI (WCF Client)
2016-04-13 16:24:20 +02:00
tscherub
17b2bc2259 GUI Project and a small change to ASF (Console.Clear goes boom, when output is redirected).
Frontend docu: http://tscherub.de/ASFGUI.html
2016-04-13 15:36:10 +02:00
JustArchi
4a36345635 Code review 2016-04-12 19:12:45 +02:00
JustArchi
e9d8f271a2 Fix collections not triggering save 2016-04-12 18:42:16 +02:00
JustArchi
6c84f8eb4f Bump 2016-04-12 17:17:50 +02:00
JustArchi
54ad58a22d Misc 2016-04-12 17:09:57 +02:00
JustArchi
790e6baf46 ASF-specific WebBrowser enhancements, closes #192 2016-04-12 16:58:45 +02:00
JustArchi
c8fb715558 Fix for new code 2016-04-12 16:37:49 +02:00
JustArchi
2ab5e6013d Misc 2016-04-12 16:36:09 +02:00
JustArchi
3e0c34e62c Misc 2016-04-12 07:44:59 +02:00
JustArchi
65b31e5537 Bring in latest ArchiBoT WebBrowser
Highlights include: Support for automatic decompression via GZip/Deflate, more aggressive disposal of response messages, rewrite of cookie handling from dictionary to cookiecontainer, debug log of also timed out messages and more.
We sacrifice the performance and scalability of single HttpClient doing the work for being less error prone on eventual steam fuckups (CookieContainer can adapt to always changing structure)
2016-04-12 07:40:02 +02:00
JustArchi
62a6e38e47 Misc 2016-04-10 18:25:22 +02:00
JustArchi
0b50a45336 Checking update does not depend on old binary removal error 2016-04-09 22:15:04 +02:00
JustArchi
8d1d162b02 Misc 2016-04-09 22:11:11 +02:00
JustArchi
dbe13a1965 Bump 2016-04-09 00:06:39 +02:00
JustArchi
fc13633f5e Misc 2016-04-08 17:33:00 +02:00
JustArchi
d0cc10f3c6 Always provide 2FA code when ASF 2FA is enabled 2016-04-08 17:12:08 +02:00
JustArchi
567931a4cc Misc 2016-04-08 04:30:51 +02:00
JustArchi
396dc17ab2 Bump 2016-04-08 04:22:41 +02:00
JustArchi
288cc29338 Be consistent 2016-04-08 03:58:46 +02:00
JustArchi
844ca7da94 Enhance !owns command
Now doesn't only support multiple games for querying, but also a mixed combination of appIDs and strings
2016-04-08 03:57:03 +02:00
JustArchi
b14b9f87c7 Handle really long messages properly 2016-04-07 03:14:49 +02:00
JustArchi
8aa086cc27 Fix rare crash 2016-04-07 01:45:08 +02:00
JustArchi
cf00989d84 Misc 2016-04-07 01:39:02 +02:00
JustArchi
8f2f85282c Bump 2016-04-06 17:05:49 +02:00
JustArchi
fa12ffd9d0 Add headlness mode 2016-04-06 16:37:45 +02:00
JustArchi
ecb27adedd Bump 2016-04-06 15:37:55 +02:00
JustArchi
6e9be09944 Misc 2016-04-06 15:30:03 +02:00
JustArchi
6a79a89a10 Allow offline bot to handle keys redeeming as well 2016-04-06 15:28:28 +02:00
JustArchi
d67be4f092 Fix ResponseRedeem() and make it possible to redeem multiple keys 2016-04-06 15:25:52 +02:00
JustArchi
a8e1039e32 Add !2fano 2016-04-05 03:41:59 +02:00
JustArchi
2ad9d9e197 Bump 2016-04-03 17:00:40 +02:00
JustArchi
fd6e2c72d7 Major cleanup & code review of ResponseRedeem() 2016-04-02 19:23:09 +02:00
JustArchi
1eed0f7647 Misc 2016-04-02 13:41:53 +02:00
JustArchi
c018c08260 Misc WCF enhancements 2016-04-02 13:41:08 +02:00
JustArchi
72fa98cb89 Improve exit/restart 2016-04-02 13:08:43 +02:00
JustArchi
71215d695e Bump 2016-04-02 12:33:41 +02:00
JustArchi
75b785d4b6 Misc 2016-04-01 20:19:17 +02:00
JustArchi
12e32692cb Code review 2016-04-01 20:18:21 +02:00
JustArchi
6158a9268d Misc 2016-04-01 17:14:03 +02:00
JustArchi
a48e53585a Pretty sure those are no longer needed 2016-03-30 13:10:00 +02:00
JustArchi
33383633ea Fix ultra rare crash 2016-03-30 01:41:53 +02:00
JustArchi
3f7359c608 Misc 2016-03-29 23:32:12 +02:00
JustArchi
d8b59c6889 Add Linux scripts 2016-03-29 22:58:18 +02:00
JustArchi
0f71b788cb Use /my/ steamcommunity trick 2016-03-29 14:48:31 +02:00
JustArchi
d012255a21 EXPERIMENTAL: Steam session improvements
1. Make sure that every call to steamcommunity has active session
2. Move whole userspace logic for session handling to ArchiWebHandler (and Bot)
3. Implement session caching and TTL so we won't send IsLoggedIn() on each ArchiWebHandler call
4. Instead of restarting whole steam account, just refresh the session via ArchiWebHandler instead
2016-03-29 14:33:05 +02:00
JustArchi
8fc39a44cd Misc 2016-03-28 15:34:59 +02:00
JustArchi
c6fe424fcc Refuse to handle https requests when ForceHttp is true 2016-03-28 15:34:10 +02:00
JustArchi
12488dafd3 Misc 2016-03-28 13:11:02 +02:00
JustArchi
3b53491567 Bump 2016-03-28 00:18:07 +02:00
JustArchi
e337bd3856 Derp 2016-03-27 23:38:49 +02:00
JustArchi
cc47c0a764 Add new property to config generator 2016-03-27 23:27:02 +02:00
JustArchi
22e4c0cd40 Misc 2016-03-27 23:18:01 +02:00
JustArchi
20b47e1787 Correct config property 2016-03-27 23:09:17 +02:00
JustArchi
941da0658d Misc 2016-03-27 23:07:37 +02:00
JustArchi
8048d4a7aa Add a feature of accepting steam gifts, closes #18 2016-03-27 23:07:00 +02:00
JustArchi
2554794daa Code review 2016-03-27 15:08:43 +02:00
JustArchi
4fcee90b99 Code review 2016-03-26 22:51:19 +01:00
JustArchi
761d73eb90 Remove old converter, closes #153
Decided to remove it faster
2016-03-26 22:23:35 +01:00
JustArchi
c163e5e2f3 Bump 2016-03-26 22:13:24 +01:00
JustArchi
8fd41cc587 Fix broken bot databases 2016-03-26 22:12:55 +01:00
JustArchi
f195563ba5 Misc 2016-03-26 19:39:46 +01:00
JustArchi
33df54365a Add !help + code review 2016-03-26 02:47:25 +01:00
JustArchi
4a6ae3064a Put NetHook2 in it's own directory + add small readme 2016-03-25 12:42:34 +01:00
JustArchi
84857e060b Add !pause + misc 2016-03-24 14:18:07 +01:00
JustArchi
3311f2a703 Make number of hours human-readable 2016-03-24 08:14:26 +01:00
JustArchi
5666ebf891 Bump 2016-03-23 19:04:27 +01:00
JustArchi
0ded4f3b80 Bump 2016-03-23 18:53:51 +01:00
JustArchi
4c9354308c Improve UI a little 2016-03-23 18:38:19 +01:00
JustArchi
0bcf2f35b7 Misc 2016-03-23 13:59:54 +01:00
JustArchi
b93d5187d6 Misc 2016-03-23 13:48:59 +01:00
JustArchi
1426922854 Misc 2016-03-23 13:42:16 +01:00
JustArchi
d8925f9409 Sync ConfigGenerator 2016-03-23 13:39:56 +01:00
JustArchi
4302029bee Be consistent 2016-03-23 13:31:11 +01:00
JustArchi
01a7800a44 Final touch on travis + readme 2016-03-23 13:26:03 +01:00
JustArchi
ab3cf5b8d0 Add travis build status 2016-03-23 13:13:04 +01:00
JustArchi
2ab9741fa8 Add travis integration 2016-03-23 13:09:17 +01:00
JustArchi
f1c3339764 Misc 2016-03-23 12:18:16 +01:00
JustArchi
da01d4eab3 Use self-compiled ILRepack 2016-03-23 12:09:26 +01:00
JustArchi
cd9bd5f0dd Misc 2016-03-22 20:20:04 +01:00
JustArchi
2602ac3623 Improve Start/Stop mechanisms 2016-03-22 20:18:47 +01:00
JustArchi
2fe8db712e Enable enhanced logging when Debug is enabled, #176 2016-03-22 18:45:30 +01:00
JustArchi
687b20bcb5 Misc 2016-03-22 17:33:08 +01:00
JustArchi
23d9dcffe7 Enable AccountPlayingDelay of 0, WCFHostname of null and add code for fixing fuckups 2016-03-22 17:26:39 +01:00
JustArchi
42623ffb5a Misc 2016-03-22 16:49:17 +01:00
JustArchi
214003edbd Misc 2016-03-22 16:42:53 +01:00
JustArchi
c21333b6e9 Further code optimizations 2016-03-22 16:41:17 +01:00
JustArchi
630f1c008c Bump 2016-03-22 16:09:33 +01:00
JustArchi
68977b7907 Code review 2016-03-22 16:04:15 +01:00
JustArchi
6dade5440e Greatly enhance Complex algorithm by reading hours from pages directly 2016-03-22 15:52:27 +01:00
JustArchi
10fcfae171 Misc 2016-03-22 13:02:31 +01:00
JustArchi
320055680b Bump 2016-03-22 11:39:20 +01:00
JustArchi
84a99c3696 Never load example and minimal configs
I'm getting tired of people not reading wiki
2016-03-22 11:28:59 +01:00
JustArchi
df415c8db5 Code review 2016-03-22 11:21:45 +01:00
JustArchi
9bc04640a1 Closes #175 2016-03-22 11:10:34 +01:00
JustArchi
d6c1bdb9b5 Misc 2016-03-22 09:01:12 +01:00
JustArchi
7276350f74 Fix rare restart issue 2016-03-22 08:53:55 +01:00
JustArchi
7d3c05088a Code review 2016-03-21 16:31:59 +01:00
JustArchi
41c2bd9131 Fix WCF in client mode 2016-03-21 06:34:26 +01:00
JustArchi
514a9f38c8 Don't allow to !farm when bot is not connected 2016-03-21 03:39:35 +01:00
JustArchi
5cc884639f Add tutorial and le voila 2016-03-20 10:01:04 +01:00
JustArchi
f77cb6d33b First nice version 2016-03-20 08:29:27 +01:00
JustArchi
b642f4b240 Fix fuckups 2016-03-20 06:42:19 +01:00
JustArchi
6de721089c Starts looking good 2016-03-20 06:41:12 +01:00
JustArchi
4bd48c399a Bump 2016-03-20 05:47:45 +01:00
JustArchi
2b3a5ff337 Work on extra GUI app 2016-03-20 05:27:30 +01:00
JustArchi
0387eb3746 Misc 2016-03-19 15:33:07 +01:00
JustArchi
3ebc6d618a Misc 2016-03-19 15:31:54 +01:00
JustArchi
92d9e10cb8 Bump 2016-03-19 12:03:39 +01:00
JustArchi
d0d670f1a5 Add warning for invalid protocols 2016-03-19 11:48:51 +01:00
JustArchi
3597c8f138 Make it possible to run steam client with UDP 2016-03-19 11:33:39 +01:00
JustArchi
8a8ec29b41 Fix OnBotShutdown() firing too fast 2016-03-18 20:56:23 +01:00
JustArchi
a3352d032d Bump 2016-03-18 20:45:31 +01:00
JustArchi
d5f8647fcc Misc 2016-03-18 20:36:13 +01:00
JustArchi
79a700d786 Fix group chat commands, #165 2016-03-18 20:35:42 +01:00
JustArchi
bc223f0644 Misc 2016-03-18 14:54:42 +01:00
JustArchi
2b0d82453b Speedup login by doing independent tasks in parallel 2016-03-18 14:47:52 +01:00
JustArchi
fa0150e745 Code review 2016-03-18 14:39:59 +01:00
JustArchi
c95d11ef66 Actually do not respond to non-authorized users at all 2016-03-18 14:13:04 +01:00
JustArchi
63b268f9c4 Bump 2016-03-18 14:09:24 +01:00
JustArchi
03d38f8a2a Fix derp, #165 2016-03-18 14:05:18 +01:00
JustArchi
c88d9bf123 Implement SteamOwnerID 2016-03-18 09:02:09 +01:00
JustArchi
053ebe15bb Prepare for SteamOwnerID 2016-03-18 03:52:36 +01:00
JustArchi
b03bfb6bbc Don't touch old log when disabled 2016-03-18 03:48:00 +01:00
JustArchi
e48867000e Add LogToFile, closes #168 2016-03-18 03:45:59 +01:00
JustArchi
e08cdbd74d Bump 2016-03-16 19:58:21 +01:00
JustArchi
0b4c585a58 Bump 2016-03-16 19:55:16 +01:00
JustArchi
6213e77fab Time to sleep 2016-03-15 07:09:30 +01:00
JustArchi
fccca6a4fc Misc 2016-03-15 07:04:31 +01:00
JustArchi
d4f4ec3401 Misc 2016-03-15 05:16:22 +01:00
JustArchi
b23dd3612d Misc 2016-03-15 05:15:22 +01:00
JustArchi
ca4fa4ac27 Packages update 2016-03-15 05:03:50 +01:00
JustArchi
5e53a208d5 Misc 2016-03-15 04:51:51 +01:00
JustArchi
895c69642d Add !owns, closes #159 2016-03-15 04:20:28 +01:00
JustArchi
765f9d29bb Fix version comparison bug (and bump to 2.0.1.0 to fix legacy versions) 2016-03-14 22:40:19 +01:00
JustArchi
99af5c86a5 Bump 2016-03-14 22:20:06 +01:00
JustArchi
0bb2b3ed5f Add UnobservedTaskExceptionHandler, thank you @Netshroud 2016-03-14 20:51:29 +01:00
JustArchi
4181f3ba21 Add workaround for broken GenerateMachineID(), #154 2016-03-14 20:38:17 +01:00
JustArchi
c189b398c0 Improve readability 2016-03-14 18:24:03 +01:00
JustArchi
de4fbf8b04 Accept only trade confirmations when dealing with trades 2016-03-14 18:13:33 +01:00
JustArchi
f91a558314 Bump 2016-03-13 22:58:52 +01:00
JustArchi
7587ff024c Add more debugging for SK2 2016-03-13 21:22:52 +01:00
JustArchi
06778c9bb0 Misc 2016-03-13 20:56:23 +01:00
JustArchi
7a97045412 Add ForceHttp 2016-03-13 20:19:52 +01:00
JustArchi
5d9bedfd28 Misc 2016-03-13 18:16:43 +01:00
JustArchi
575992c25d Increase HttpTimeout to 60 2016-03-13 17:03:15 +01:00
JustArchi
72db5bc9f3 We don't need that delay anymore 2016-03-12 17:03:53 +01:00
JustArchi
b015720a3e Add workaround for Volvo fuckup, #135 2016-03-12 17:02:02 +01:00
JustArchi
84a6d4501b Bump 2016-03-12 06:39:08 +01:00
JustArchi
07049e71c0 Misc 2016-03-12 06:01:55 +01:00
JustArchi
4710d9c1eb Add support for WinAuth, #144 2016-03-12 05:58:51 +01:00
JustArchi
d08462745a I swear no more bumps tonight 2016-03-11 21:42:16 +01:00
JustArchi
eb4e9ee077 Fix remaining crash 2016-03-11 21:40:25 +01:00
JustArchi
601a486b13 Bump 2016-03-11 19:58:03 +01:00
JustArchi
14867f470d Derp 2016-03-11 19:49:52 +01:00
JustArchi
187f0800b2 Add !update 2016-03-11 19:48:14 +01:00
JustArchi
3afa202d0b Misc 2016-03-11 19:42:15 +01:00
JustArchi
d33e76c8b0 Add !farm, closes #151 2016-03-11 19:39:25 +01:00
JustArchi
3061c55eaf Bump 2016-03-11 19:15:56 +01:00
JustArchi
621a1dc2cb Do not crash on bad configs 2016-03-11 18:49:29 +01:00
JustArchi
1a832780a2 Bump 2016-03-11 18:35:42 +01:00
JustArchi
ab531c80df Misc 2016-03-11 02:15:25 +01:00
JustArchi
f20ea0a87f Add FarmingPeriod 2016-03-11 02:07:20 +01:00
JustArchi
3e7f726afb Bump 2016-03-11 00:52:15 +01:00
JustArchi
d247515a03 Confirmations should be minutes-based 2016-03-10 22:54:59 +01:00
JustArchi
f084a3f219 Mark inventory on login if needed 2016-03-10 21:44:45 +01:00
JustArchi
4d3673c305 Forgot to add 2016-03-10 21:19:21 +01:00
JustArchi
0253c3bf7b Add AcceptConfirmationsPeriod 2016-03-10 21:17:48 +01:00
JustArchi
0eae895676 SteamAuth update 2016-03-10 21:11:54 +01:00
JustArchi
045acb362d Code review 2016-03-10 02:21:28 +01:00
JustArchi
ebd65da3ff Misc 2016-03-10 01:23:32 +01:00
JustArchi
1de3c5bec0 Code review 2016-03-10 01:20:17 +01:00
JustArchi
b334c939df Add !2faok 2016-03-10 00:40:30 +01:00
JustArchi
8e6100e236 Add MaxFarmingTime, FarmingDelay 2016-03-09 19:25:45 +01:00
JustArchi
8cb512b6e5 Add InventoryLimiterDelay, rename RequestLimiterDelay to LoginLimiterDelay 2016-03-09 18:58:14 +01:00
JustArchi
6a28946205 Refresh farming queue when farming is finished 2016-03-09 18:50:54 +01:00
JustArchi
c632c025cb Move Statistics to global config 2016-03-09 05:02:07 +01:00
JustArchi
1403ffe190 This actually DOES work on Mono, hooray! 2016-03-09 03:52:43 +01:00
JustArchi
71ae9a84da Fix misc bug, #48 2016-03-09 03:52:04 +01:00
JustArchi
a823471771 Bump version to test updates 2016-03-09 03:40:57 +01:00
JustArchi
dac057d242 World Domination is close 2016-03-09 03:28:05 +01:00
JustArchi
3d9fe36245 The end is near... #48 2016-03-09 03:10:33 +01:00
JustArchi
5c1da24def Misc 2016-03-08 03:29:48 +01:00
JustArchi
1e961a5945 Add CustomGamePlayedWhileIdle 2016-03-08 03:18:50 +01:00
JustArchi
84898146d2 Add more debugging tools 2016-03-08 02:22:00 +01:00
JustArchi
552613e977 Add AccountPlayingDelay, closes #122 2016-03-07 23:10:07 +01:00
JustArchi
07687df91f Add debugging and NetHookAnalyzer 2016-03-07 18:12:05 +01:00
JustArchi
1a4d941a2c Fix derp, #141 2016-03-07 16:40:34 +01:00
JustArchi
cfbd880995 Add more support for SDA 2016-03-07 15:53:46 +01:00
JustArchi
09abe77495 Support SDA files 2016-03-07 15:42:38 +01:00
JustArchi
ac9943ff94 Misc 2016-03-07 14:48:59 +01:00
JustArchi
741dd2adb7 Add Holiday Sale 2013 to blacklist 2016-03-07 14:47:28 +01:00
JustArchi
1ad5d3676f Add GlobalDatabase 2016-03-07 02:39:55 +01:00
JustArchi
27254aa31e Copy ASF.json to out 2016-03-06 23:34:46 +01:00
JustArchi
bb90dc1c01 Bugfixes 2016-03-06 23:32:17 +01:00
JustArchi
292ec97b1c Work on GlobalConfig, #131 2016-03-06 23:28:56 +01:00
JustArchi
238cc2ad46 Code review 2016-03-06 22:14:02 +01:00
JustArchi
b9064bbfda Fix awaiting null tasks, closes #128 2016-03-06 21:49:34 +01:00
Łukasz Domeradzki
eddcc2816a Update README.md 2016-03-06 21:17:21 +01:00
JustArchi
52360a682a Add DismissInventoryNotifications, closes #132 2016-03-06 14:15:59 +01:00
JustArchi
709ce6489b Add only enabled bots to collection, closes #139 2016-03-06 14:09:08 +01:00
JustArchi
220a9baa7d More bugfixes 2016-03-06 02:27:43 +01:00
JustArchi
a7f2dd99c3 Minify minimal.json as it should be 2016-03-06 02:22:19 +01:00
JustArchi
3481adb3c5 ASF: Big revolution #131 2016-03-06 02:21:03 +01:00
Łukasz Domeradzki
9f5197a426 Update README.md 2016-03-05 20:32:51 +01:00
Łukasz Domeradzki
f6a631a33a Update README.md 2016-03-05 20:14:31 +01:00
Łukasz Domeradzki
9dc04ff5a0 Update README.md 2016-03-05 19:39:39 +01:00
Łukasz Domeradzki
248d200764 Update README.md 2016-03-05 19:36:03 +01:00
JustArchi
e3d08eca3f Closes #138 2016-03-05 18:56:24 +01:00
JustArchi
dc5d2483c1 Merge branch 'master' of https://github.com/JustArchi/ArchiSteamFarm 2016-03-03 16:12:01 +01:00
JustArchi
93aacad350 Closes #135 2016-03-03 16:11:46 +01:00
Łukasz Domeradzki
1b822bee91 Update README.md 2016-03-03 03:35:10 +01:00
JustArchi
c65d6fee5a Final fix for #130 2016-02-28 21:24:50 +01:00
JustArchi
6bc715575f Add GamesPlayedWhileIdle, closes #121 2016-02-28 04:15:52 +01:00
JustArchi
335044e38a Misc 2016-02-28 03:43:01 +01:00
JustArchi
6a6232b655 Mark inventory after item drops, #130
Mark is untested yet
2016-02-28 03:40:59 +01:00
JustArchi
75ff6b4f11 Bump 2016-02-26 22:43:10 +01:00
JustArchi
9cea6e35e4 Misc 2016-02-26 22:39:03 +01:00
JustArchi
52d8dc88d2 Don't sign assemblies yet, #129
I'll sign them with stable releases manually for now
2016-02-26 22:29:01 +01:00
JustArchi
1d5a50bcca Sign the application + bump, #127 2016-02-25 03:43:13 +01:00
JustArchi
44a595c1df Code review 2016-02-24 06:20:35 +01:00
JustArchi
a528dee670 Add !rejoinchat, closes #123 2016-02-24 06:06:09 +01:00
JustArchi
b6e96d8711 Fix trading bugs 2016-02-24 05:57:30 +01:00
JustArchi
c6cc4e2c9d Misc 2016-02-23 12:58:58 +01:00
JustArchi
6974e51148 Improve SendOnFarmingFinished + increase farming interval 2016-02-22 20:18:31 +01:00
118 changed files with 38170 additions and 3410 deletions

11
.gitignore vendored
View File

@@ -4,11 +4,16 @@
# Ignore all config files, apart from ones we want to include
ArchiSteamFarm/config/*
!ArchiSteamFarm/config/example.xml
!ArchiSteamFarm/config/minimal.xml
!ArchiSteamFarm/config/ASF.json
!ArchiSteamFarm/config/example.json
!ArchiSteamFarm/config/minimal.json
# Ignore local debugging log file
# Ignore local debugging
ArchiSteamFarm/log.txt
ArchiSteamFarm/debug/*
# Ignore out
out/
#################
## Eclipse

12
.travis.yml Normal file
View File

@@ -0,0 +1,12 @@
language: csharp
solution: ArchiSteamFarm.sln
git:
depth: 10
mono:
- weekly
- latest
notifications:
email: false

View File

@@ -1,12 +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
@@ -21,6 +31,14 @@ Global
{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

View File

@@ -24,12 +24,26 @@
using SteamKit2;
using SteamKit2.Internal;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
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(nameof(bot));
}
Bot = bot;
}
/*
____ _ _ _ _
/ ___| __ _ | || || |__ __ _ ___ | | __ ___
@@ -40,51 +54,73 @@ namespace ArchiSteamFarm {
*/
internal sealed class NotificationsCallback : CallbackMsg {
internal sealed class Notification {
internal enum ENotificationType {
Unknown = 0,
Trading = 1,
}
internal ENotificationType NotificationType { get; set; }
internal enum ENotification : byte {
[SuppressMessage("ReSharper", "UnusedMember.Global")]
Unknown = 0,
Trading = 1,
// Only custom below, different than ones available as user_notification_type
Items = 255
}
internal readonly List<Notification> Notifications;
internal readonly HashSet<ENotification> Notifications;
internal NotificationsCallback(JobID jobID, CMsgClientUserNotifications msg) {
JobID = jobID;
if (msg == null) {
return;
if ((jobID == null) || (msg == null)) {
throw new ArgumentNullException(nameof(jobID) + " || " + nameof(msg));
}
Notifications = new List<Notification>();
foreach (var notification in msg.notifications) {
Notifications.Add(new Notification {
NotificationType = (Notification.ENotificationType) notification.user_notification_type
});
JobID = jobID;
Notifications = new HashSet<ENotification>();
foreach (CMsgClientUserNotifications.Notification 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(nameof(jobID) + " || " + nameof(msg));
}
JobID = jobID;
if (msg.count_new_items > 0) {
Notifications = new HashSet<ENotification> {
ENotification.Items
};
}
}
}
internal sealed class OfflineMessageCallback : CallbackMsg {
internal readonly uint OfflineMessages;
internal readonly List<uint> Users;
internal readonly uint OfflineMessagesCount;
internal OfflineMessageCallback(JobID jobID, CMsgClientOfflineMessageNotification msg) {
JobID = jobID;
if (msg == null) {
return;
if ((jobID == null) || (msg == null)) {
throw new ArgumentNullException(nameof(jobID) + " || " + nameof(msg));
}
OfflineMessages = msg.offline_messages;
Users = msg.friends_with_offline_messages;
JobID = jobID;
OfflineMessagesCount = msg.offline_messages;
}
}
internal sealed class PlayingSessionStateCallback : CallbackMsg {
internal readonly bool PlayingBlocked;
internal PlayingSessionStateCallback(JobID jobID, CMsgClientPlayingSessionState msg) {
if ((jobID == null) || (msg == null)) {
throw new ArgumentNullException(nameof(jobID) + " || " + nameof(msg));
}
JobID = jobID;
PlayingBlocked = msg.playing_blocked;
}
}
internal sealed class PurchaseResponseCallback : CallbackMsg {
internal enum EPurchaseResult {
internal enum EPurchaseResult : sbyte {
[SuppressMessage("ReSharper", "UnusedMember.Global")]
Unknown = -1,
OK = 0,
AlreadyOwned = 9,
@@ -95,35 +131,36 @@ namespace ArchiSteamFarm {
OnCooldown = 53
}
internal readonly EResult Result;
internal readonly EPurchaseResult PurchaseResult;
internal readonly KeyValue ReceiptInfo;
internal readonly Dictionary<uint, string> Items;
internal PurchaseResponseCallback(JobID jobID, CMsgClientPurchaseResponse msg) {
JobID = jobID;
if ((jobID == null) || (msg == null)) {
throw new ArgumentNullException(nameof(jobID) + " || " + nameof(msg));
}
if (msg == null) {
JobID = jobID;
PurchaseResult = (EPurchaseResult) msg.purchase_result_details;
if (msg.purchase_receipt_info == null) {
return;
}
Result = (EResult) msg.eresult;
PurchaseResult = (EPurchaseResult) msg.purchase_result_details;
ReceiptInfo = new KeyValue();
KeyValue receiptInfo = new KeyValue();
using (MemoryStream ms = new MemoryStream(msg.purchase_receipt_info)) {
if (!ReceiptInfo.TryReadAsBinary(ms)) {
if (!receiptInfo.TryReadAsBinary(ms)) {
Logging.LogNullError(nameof(ms));
return;
}
List<KeyValue> lineItems = ReceiptInfo["lineitems"].Children;
List<KeyValue> 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"].AsString();
gameName = WebUtility.UrlDecode(gameName); // Apparently steam expects client to decode sent HTML
Items.Add(appID, gameName);
string gameName = lineItem["ItemDescription"].Value;
gameName = WebUtility.HtmlDecode(gameName); // Apparently steam expects client to decode sent HTML
Items[appID] = gameName;
}
}
}
@@ -138,79 +175,110 @@ namespace ArchiSteamFarm {
*/
internal void AcceptClanInvite(ulong clanID) {
if (clanID == 0 || !Client.IsConnected) {
return;
}
var request = new ClientMsg<CMsgClientClanInviteAction>((int) EMsg.ClientAcknowledgeClanInvite);
request.Body.GroupID = clanID;
request.Body.AcceptInvite = true;
Client.Send(request);
}
internal void DeclineClanInvite(ulong clanID) {
if (clanID == 0 || !Client.IsConnected) {
return;
}
var request = new ClientMsg<CMsgClientClanInviteAction>((int) EMsg.ClientAcknowledgeClanInvite);
request.Body.GroupID = clanID;
request.Body.AcceptInvite = false;
Client.Send(request);
}
internal void PlayGames(params uint[] gameIDs) {
internal void PlayGame(string gameName) {
if (!Client.IsConnected) {
return;
}
var request = new ClientMsgProtobuf<CMsgClientGamesPlayed>(EMsg.ClientGamesPlayed);
foreach (uint gameID in gameIDs) {
if (gameID == 0) {
continue;
}
ClientMsgProtobuf<CMsgClientGamesPlayed> request = new ClientMsgProtobuf<CMsgClientGamesPlayed>(EMsg.ClientGamesPlayed);
if (!string.IsNullOrEmpty(gameName)) {
request.Body.games_played.Add(new CMsgClientGamesPlayed.GamePlayed {
game_id = new GameID(gameID),
game_extra_info = gameName,
game_id = new GameID {
AppType = GameID.GameType.Shortcut,
ModID = uint.MaxValue
}
});
}
Client.Send(request);
}
internal void PlayGames(ICollection<uint> gameIDs) {
if (gameIDs == null || gameIDs.Count == 0 || !Client.IsConnected) {
internal void PlayGames(uint gameID) {
if (!Client.IsConnected) {
return;
}
var request = new ClientMsgProtobuf<CMsgClientGamesPlayed>(EMsg.ClientGamesPlayed);
foreach (uint gameID in gameIDs) {
if (gameID == 0) {
continue;
}
PlayGames(new HashSet<uint> { gameID });
}
internal void PlayGames(ICollection<uint> gameIDs) {
if (gameIDs == null) {
Logging.LogNullError(nameof(gameIDs), Bot.BotName);
return;
}
if (!Client.IsConnected) {
return;
}
ClientMsgProtobuf<CMsgClientGamesPlayed> request = new ClientMsgProtobuf<CMsgClientGamesPlayed>(EMsg.ClientGamesPlayed);
foreach (uint gameID in gameIDs.Where(gameID => gameID != 0)) {
request.Body.games_played.Add(new CMsgClientGamesPlayed.GamePlayed {
game_id = new GameID(gameID),
game_id = new GameID(gameID)
});
}
Client.Send(request);
}
internal AsyncJob<PurchaseResponseCallback> RedeemKey(string key) {
if (string.IsNullOrEmpty(key) || !Client.IsConnected) {
internal async Task<PurchaseResponseCallback> RedeemKey(string key) {
if (string.IsNullOrEmpty(key)) {
Logging.LogNullError(nameof(key), Bot.BotName);
return null;
}
var request = new ClientMsgProtobuf<CMsgClientRegisterKey>(EMsg.ClientRegisterKey);
request.SourceJobID = Client.GetNextJobID();
if (!Client.IsConnected) {
return null;
}
ClientMsgProtobuf<CMsgClientRegisterKey> request = new ClientMsgProtobuf<CMsgClientRegisterKey>(EMsg.ClientRegisterKey) {
SourceJobID = Client.GetNextJobID()
};
request.Body.key = key;
Client.Send(request);
return new AsyncJob<PurchaseResponseCallback>(Client, request.SourceJobID);
try {
return await new AsyncJob<PurchaseResponseCallback>(Client, request.SourceJobID);
} catch (Exception e) {
Logging.LogGenericException(e, Bot.BotName);
return null;
}
}
// 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);
ClientMsgProtobuf<CMsgClientLogon> logon = new ClientMsgProtobuf<CMsgClientLogon>(EMsg.ClientLogon);
if (details.LoginID != null) {
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);
}
/*
@@ -222,8 +290,9 @@ namespace ArchiSteamFarm {
*/
public sealed override void HandleMsg(IPacketMsg packetMsg) {
public override void HandleMsg(IPacketMsg packetMsg) {
if (packetMsg == null) {
Logging.LogNullError(nameof(packetMsg), Bot.BotName);
return;
}
@@ -231,6 +300,12 @@ namespace ArchiSteamFarm {
case EMsg.ClientFSOfflineMessageNotification:
HandleFSOfflineMessageNotification(packetMsg);
break;
case EMsg.ClientItemAnnouncements:
HandleItemAnnouncements(packetMsg);
break;
case EMsg.ClientPlayingSessionState:
HandlePlayingSessionState(packetMsg);
break;
case EMsg.ClientPurchaseResponse:
HandlePurchaseResponse(packetMsg);
break;
@@ -242,40 +317,51 @@ namespace ArchiSteamFarm {
private void HandleFSOfflineMessageNotification(IPacketMsg packetMsg) {
if (packetMsg == null) {
Logging.LogNullError(nameof(packetMsg), Bot.BotName);
return;
}
var response = new ClientMsgProtobuf<CMsgClientOfflineMessageNotification>(packetMsg);
if (response == null) {
return;
}
ClientMsgProtobuf<CMsgClientOfflineMessageNotification> response = new ClientMsgProtobuf<CMsgClientOfflineMessageNotification>(packetMsg);
Client.PostCallback(new OfflineMessageCallback(packetMsg.TargetJobID, response.Body));
}
private void HandleItemAnnouncements(IPacketMsg packetMsg) {
if (packetMsg == null) {
Logging.LogNullError(nameof(packetMsg), Bot.BotName);
return;
}
ClientMsgProtobuf<CMsgClientItemAnnouncements> response = new ClientMsgProtobuf<CMsgClientItemAnnouncements>(packetMsg);
Client.PostCallback(new NotificationsCallback(packetMsg.TargetJobID, response.Body));
}
private void HandlePlayingSessionState(IPacketMsg packetMsg) {
if (packetMsg == null) {
Logging.LogNullError(nameof(packetMsg), Bot.BotName);
return;
}
ClientMsgProtobuf<CMsgClientPlayingSessionState> response = new ClientMsgProtobuf<CMsgClientPlayingSessionState>(packetMsg);
Client.PostCallback(new PlayingSessionStateCallback(packetMsg.TargetJobID, response.Body));
}
private void HandlePurchaseResponse(IPacketMsg packetMsg) {
if (packetMsg == null) {
Logging.LogNullError(nameof(packetMsg), Bot.BotName);
return;
}
var response = new ClientMsgProtobuf<CMsgClientPurchaseResponse>(packetMsg);
if (response == null) {
return;
}
ClientMsgProtobuf<CMsgClientPurchaseResponse> response = new ClientMsgProtobuf<CMsgClientPurchaseResponse>(packetMsg);
Client.PostCallback(new PurchaseResponseCallback(packetMsg.TargetJobID, response.Body));
}
private void HandleUserNotifications(IPacketMsg packetMsg) {
if (packetMsg == null) {
Logging.LogNullError(nameof(packetMsg), Bot.BotName);
return;
}
var response = new ClientMsgProtobuf<CMsgClientUserNotifications>(packetMsg);
if (response == null) {
return;
}
ClientMsgProtobuf<CMsgClientUserNotifications> response = new ClientMsgProtobuf<CMsgClientUserNotifications>(packetMsg);
Client.PostCallback(new NotificationsCallback(packetMsg.TargetJobID, response.Body));
}
}

View File

@@ -12,6 +12,7 @@
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<IsWebBootstrapper>false</IsWebBootstrapper>
<TargetFrameworkProfile />
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
@@ -26,7 +27,6 @@
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
@@ -36,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>
@@ -60,13 +60,23 @@
<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.2\lib\net45\Newtonsoft.Json.dll</HintPath>
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.9.0.1-beta1\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">
@@ -80,10 +90,7 @@
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.ServiceModel" />
<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>
@@ -91,16 +98,19 @@
<Compile Include="ArchiHandler.cs" />
<Compile Include="ArchiWebHandler.cs" />
<Compile Include="Bot.cs" />
<Compile Include="BotConfig.cs" />
<Compile Include="ConcurrentEnumerator.cs" />
<Compile Include="ConcurrentHashSet.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="SteamItemList.cs" />
<Compile Include="SteamTradeOffer.cs" />
<Compile Include="SteamTradeOfferRequest.cs" />
<Compile Include="Trading.cs" />
<Compile Include="Utilities.cs" />
<Compile Include="WCF.cs" />
@@ -108,6 +118,15 @@
</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>
@@ -124,12 +143,6 @@
</ItemGroup>
<ItemGroup>
<Content Include="cirno.ico" />
<Content Include="config\example.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="config\minimal.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SteamAuth\SteamAuth.csproj">
@@ -137,6 +150,7 @@
<Name>SteamAuth</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PreBuildEvent>
@@ -144,18 +158,20 @@
</PropertyGroup>
<PropertyGroup>
<PostBuildEvent Condition=" '$(OS)' != 'Unix' AND '$(ConfigurationName)' == 'Release' ">
mkdir "$(TargetDir)out" "$(TargetDir)out\config"
copy "$(TargetDir)config\example.xml" "$(TargetDir)out\config"
copy "$(TargetDir)config\minimal.xml" "$(TargetDir)out\config"
"$(SolutionDir)tools\ILRepack.exe" /ndebug /internalize /parallel /targetplatform:v4 /wildcards /out:"$(TargetDir)out\ASF.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll"
del "$(TargetDir)out\ASF.exe.config"
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 "$(TargetDir)out" "$(TargetDir)out/config"
cp "$(TargetDir)config/example.xml" "$(TargetDir)out/config"
cp "$(TargetDir)config/minimal.xml" "$(TargetDir)out/config"
mono -O=all "$(SolutionDir)tools/ILRepack.exe" /ndebug /internalize /parallel /targetplatform:v4 /wildcards /out:"$(TargetDir)out/ASF.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll"
rm "$(TargetDir)out/ASF.exe.config"
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 --llvm --server -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.

View File

@@ -27,43 +27,114 @@ using HtmlAgilityPack;
using SteamKit2;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Threading;
using ArchiSteamFarm.JSON;
namespace ArchiSteamFarm {
internal sealed class ArchiWebHandler {
private const int Timeout = 1000 * WebBrowser.HttpTimeout; // In miliseconds
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 Dictionary<string, string> Cookie = new Dictionary<string, string>(3);
private readonly SemaphoreSlim SessionSemaphore = new SemaphoreSlim(1);
private readonly WebBrowser WebBrowser;
private ulong SteamID;
private DateTime LastSessionRefreshCheck = DateTime.MinValue;
internal ArchiWebHandler(Bot bot, string apiKey) {
Bot = bot;
internal static void Init() {
Timeout = Program.GlobalConfig.HttpTimeout * 1000;
SteamCommunityURL = (Program.GlobalConfig.ForceHttp ? "http://" : "https://") + SteamCommunityHost;
}
if (!string.IsNullOrEmpty(apiKey) && !apiKey.Equals("null")) {
ApiKey = apiKey;
private static uint GetAppIDFromMarketHashName(string hashName) {
if (string.IsNullOrEmpty(hashName)) {
Logging.LogNullError(nameof(hashName));
return 0;
}
int index = hashName.IndexOf('-');
if (index < 1) {
Logging.LogNullError(nameof(index));
return 0;
}
uint appID;
if (uint.TryParse(hashName.Substring(0, index), out appID)) {
return appID;
}
Logging.LogNullError(nameof(appID));
return 0;
}
private static Steam.Item.EType GetItemType(string name) {
if (string.IsNullOrEmpty(name)) {
Logging.LogNullError(nameof(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;
}
if (name.EndsWith("Foil Trading Card", StringComparison.Ordinal)) {
return Steam.Item.EType.FoilTradingCard;
}
if (name.EndsWith("Profile Background", StringComparison.Ordinal)) {
return Steam.Item.EType.ProfileBackground;
}
return name.EndsWith("Trading Card", StringComparison.Ordinal) ? Steam.Item.EType.TradingCard : Steam.Item.EType.Unknown;
}
}
internal async Task<bool> Init(SteamClient steamClient, string webAPIUserNonce, string parentalPin) {
if (steamClient == null || steamClient.SteamID == null || string.IsNullOrEmpty(webAPIUserNonce)) {
internal ArchiWebHandler(Bot bot) {
if (bot == null) {
throw new ArgumentNullException(nameof(bot));
}
Bot = bot;
WebBrowser = new WebBrowser(bot.BotName);
}
internal bool Init(SteamClient steamClient, string webAPIUserNonce, string parentalPin) {
if ((steamClient == null) || string.IsNullOrEmpty(webAPIUserNonce)) {
Logging.LogNullError(nameof(steamClient) + " || " + nameof(webAPIUserNonce), Bot.BotName);
return false;
}
SteamID = steamClient.SteamID;
ulong steamID = steamClient.SteamID;
if (steamID == 0) {
return false;
}
string sessionID = Convert.ToBase64String(Encoding.UTF8.GetBytes(SteamID.ToString()));
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);
}
@@ -84,11 +155,11 @@ namespace ArchiSteamFarm {
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(e, Bot.BotName);
@@ -97,67 +168,183 @@ namespace ArchiSteamFarm {
}
if (authResult == null) {
Logging.LogNullError(nameof(authResult), Bot.BotName);
return false;
}
Logging.LogGenericInfo("Success!", Bot.BotName);
string steamLogin = authResult["token"].AsString();
string steamLoginSecure = authResult["tokensecure"].AsString();
WebBrowser.CookieContainer.Add(new Cookie("sessionid", sessionID, "/", "." + SteamCommunityHost));
Cookie["sessionid"] = sessionID;
Cookie["steamLogin"] = steamLogin;
Cookie["steamLoginSecure"] = steamLoginSecure;
string steamLogin = authResult["token"].Value;
WebBrowser.CookieContainer.Add(new Cookie("steamLogin", steamLogin, "/", "." + SteamCommunityHost));
await UnlockParentalAccount(parentalPin).ConfigureAwait(false);
return true;
}
string steamLoginSecure = authResult["tokensecure"].Value;
WebBrowser.CookieContainer.Add(new Cookie("steamLoginSecure", steamLoginSecure, "/", "." + SteamCommunityHost));
internal async Task<bool?> IsLoggedIn() {
if (SteamID == 0) {
if (!UnlockParentalAccount(parentalPin).Result) {
return false;
}
HtmlDocument htmlDocument = null;
for (byte i = 0; i < WebBrowser.MaxRetries && htmlDocument == null; i++) {
htmlDocument = await WebBrowser.UrlGetToHtmlDocument("https://steamcommunity.com/my/profile", Cookie).ConfigureAwait(false);
LastSessionRefreshCheck = DateTime.Now;
return true;
}
internal async Task<bool> AcceptGift(ulong gid) {
if (gid == 0) {
Logging.LogNullError(nameof(gid), Bot.BotName);
return false;
}
if (htmlDocument == null) {
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
return false;
}
string sessionID = WebBrowser.CookieContainer.GetCookieValue(SteamCommunityURL, "sessionid");
if (string.IsNullOrEmpty(sessionID)) {
Logging.LogNullError(nameof(sessionID), Bot.BotName);
return false;
}
string request = SteamCommunityURL + "/gifts/" + gid + "/acceptunpack";
Dictionary<string, string> data = new Dictionary<string, string>(1) {
{ "sessionid", sessionID }
};
return await WebBrowser.UrlPostRetry(request, data).ConfigureAwait(false);
}
internal async Task<bool> JoinGroup(ulong groupID) {
if (groupID == 0) {
Logging.LogNullError(nameof(groupID), Bot.BotName);
return false;
}
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
return false;
}
string sessionID = WebBrowser.CookieContainer.GetCookieValue(SteamCommunityURL, "sessionid");
if (string.IsNullOrEmpty(sessionID)) {
Logging.LogNullError(nameof(sessionID), Bot.BotName);
return false;
}
string request = SteamCommunityURL + "/gid/" + groupID;
Dictionary<string, string> data = new Dictionary<string, string>(2) {
{ "sessionID", sessionID },
{ "action", "join" }
};
return await WebBrowser.UrlPostRetry(request, data).ConfigureAwait(false);
}
internal async Task<Dictionary<uint, string>> GetOwnedGames() {
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
return null;
}
HtmlNode htmlNode = htmlDocument.DocumentNode.SelectSingleNode("//span[@id='account_pulldown']");
return htmlNode != null;
}
string request = SteamCommunityURL + "/my/games/?xml=1";
internal async Task<bool> ReconnectIfNeeded() {
bool? isLoggedIn = await IsLoggedIn().ConfigureAwait(false);
if (isLoggedIn.HasValue && !isLoggedIn.Value) {
Logging.LogGenericInfo("Reconnecting because our sessionID expired!", Bot.BotName);
var restart = Task.Run(async () => await Bot.Restart().ConfigureAwait(false));
return true;
XmlDocument response = await WebBrowser.UrlGetToXMLRetry(request).ConfigureAwait(false);
if (response == null) {
return null;
}
return false;
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) {
Logging.LogNullError(nameof(appNode), Bot.BotName);
continue;
}
uint appID;
if (!uint.TryParse(appNode.InnerText, out appID)) {
Logging.LogNullError(nameof(appID), Bot.BotName);
continue;
}
XmlNode nameNode = xmlNode.SelectSingleNode("name");
if (nameNode == null) {
Logging.LogNullError(nameof(nameNode), Bot.BotName);
continue;
}
result[appID] = nameNode.InnerText;
}
return result;
}
internal List<SteamTradeOffer> GetTradeOffers() {
if (ApiKey == null) {
internal Dictionary<uint, string> GetOwnedGames(ulong steamID) {
if ((steamID == 0) || string.IsNullOrEmpty(Bot.BotConfig.SteamApiKey)) {
// TODO: Correct this when Mono 4.4+ will be a latest stable one | https://bugzilla.xamarin.com/show_bug.cgi?id=39455
Logging.LogNullError("steamID || SteamApiKey", Bot.BotName);
//Logging.LogNullError(nameof(steamID) + " || " + nameof(Bot.BotConfig.SteamApiKey), Bot.BotName);
return null;
}
KeyValue response = null;
using (dynamic iEconService = WebAPI.GetInterface("IEconService", ApiKey)) {
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, Bot.BotName);
}
}
}
if (response == null) {
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries");
return null;
}
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) {
Logging.LogNullError(nameof(appID));
continue;
}
result[appID] = game["name"].Value;
}
return result;
}
internal HashSet<Steam.TradeOffer> GetTradeOffers() {
if (string.IsNullOrEmpty(Bot.BotConfig.SteamApiKey)) {
// TODO: Correct this when Mono 4.4+ will be a latest stable one | https://bugzilla.xamarin.com/show_bug.cgi?id=39455
Logging.LogNullError("SteamApiKey", Bot.BotName);
//Logging.LogNullError(nameof(Bot.BotConfig.SteamApiKey), Bot.BotName);
return null;
}
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++) {
for (byte i = 0; (i < WebBrowser.MaxRetries) && (response == null); i++) {
try {
response = iEconService.GetTradeOffers(
get_received_offers: 1,
active_only: 1,
secure: true
get_descriptions: 1,
secure: !Program.GlobalConfig.ForceHttp
);
} catch (Exception e) {
Logging.LogGenericException(e, Bot.BotName);
@@ -170,212 +357,267 @@ namespace ArchiSteamFarm {
return null;
}
List<SteamTradeOffer> result = new List<SteamTradeOffer>();
Dictionary<ulong, Tuple<uint, Steam.Item.EType>> descriptions = new Dictionary<ulong, Tuple<uint, Steam.Item.EType>>();
foreach (KeyValue description in response["descriptions"].Children) {
ulong classID = description["classid"].AsUnsignedLong();
if (classID == 0) {
Logging.LogNullError(nameof(classID), Bot.BotName);
continue;
}
if (descriptions.ContainsKey(classID)) {
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[classID] = 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) {
SteamTradeOffer tradeOffer = new SteamTradeOffer {
tradeofferid = trade["tradeofferid"].AsString(),
accountid_other = trade["accountid_other"].AsInteger(),
trade_offer_state = trade["trade_offer_state"].AsEnum<SteamTradeOffer.ETradeOfferState>()
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) {
tradeOffer.items_to_give.Add(new SteamItem {
appid = item["appid"].AsString(),
contextid = item["contextid"].AsString(),
assetid = item["assetid"].AsString(),
classid = item["classid"].AsString(),
instanceid = item["instanceid"].AsString(),
amount = item["amount"].AsString(),
});
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<uint, Steam.Item.EType> description;
if (descriptions.TryGetValue(steamItem.ClassID, out description)) {
steamItem.RealAppID = description.Item1;
steamItem.Type = description.Item2;
}
tradeOffer.ItemsToGive.Add(steamItem);
}
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(),
classid = item["classid"].AsString(),
instanceid = item["instanceid"].AsString(),
amount = item["amount"].AsString(),
});
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<uint, Steam.Item.EType> description;
if (descriptions.TryGetValue(steamItem.ClassID, out description)) {
steamItem.RealAppID = description.Item1;
steamItem.Type = description.Item2;
}
tradeOffer.ItemsToReceive.Add(steamItem);
}
result.Add(tradeOffer);
}
return result;
}
internal async Task<bool> JoinClan(ulong clanID) {
if (clanID == 0) {
return false;
}
string sessionID;
if (!Cookie.TryGetValue("sessionid", out sessionID)) {
return false;
}
string request = "https://steamcommunity.com/gid/" + clanID;
Dictionary<string, string> data = new Dictionary<string, string>(2) {
{"sessionID", sessionID},
{"action", "join"}
};
HttpResponseMessage response = null;
for (byte i = 0; i < WebBrowser.MaxRetries && response == null; i++) {
response = await WebBrowser.UrlPost(request, data, Cookie).ConfigureAwait(false);
}
if (response == null) {
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
return false;
}
return true;
}
internal async Task<bool> AcceptTradeOffer(ulong tradeID) {
if (tradeID == 0) {
Logging.LogNullError(nameof(tradeID), Bot.BotName);
return false;
}
string sessionID;
if (!Cookie.TryGetValue("sessionid", out sessionID)) {
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
return false;
}
string referer = "https://steamcommunity.com/tradeoffer/" + tradeID;
string sessionID = WebBrowser.CookieContainer.GetCookieValue(SteamCommunityURL, "sessionid");
if (string.IsNullOrEmpty(sessionID)) {
Logging.LogNullError(nameof(sessionID), Bot.BotName);
return false;
}
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()}
{ "sessionid", sessionID },
{ "serverid", "1" },
{ "tradeofferid", tradeID.ToString() }
};
HttpResponseMessage response = null;
for (byte i = 0; i < WebBrowser.MaxRetries && response == null; i++) {
response = await WebBrowser.UrlPost(request, data, Cookie, referer).ConfigureAwait(false);
}
if (response == null) {
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
return false;
}
return true;
return await WebBrowser.UrlPostRetry(request, data, referer).ConfigureAwait(false);
}
internal bool DeclineTradeOffer(ulong tradeID) {
if (tradeID == 0 || ApiKey == null) {
return false;
internal async Task<HashSet<Steam.Item>> GetMyTradableInventory() {
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
return null;
}
KeyValue response = null;
using (dynamic iEconService = WebAPI.GetInterface("IEconService", ApiKey)) {
iEconService.Timeout = Timeout;
HashSet<Steam.Item> result = new HashSet<Steam.Item>();
for (byte i = 0; i < WebBrowser.MaxRetries && response == null; i++) {
try {
response = iEconService.DeclineTradeOffer(
tradeofferid: tradeID.ToString(),
method: WebRequestMethods.Http.Post,
secure: true
);
} catch (Exception e) {
Logging.LogGenericException(e, Bot.BotName);
ushort nextPage = 0;
while (true) {
string request = SteamCommunityURL + "/my/inventory/json/" + Steam.Item.SteamAppID + "/" + Steam.Item.SteamContextID + "?trading=1&start=" + nextPage;
JObject jObject = await WebBrowser.UrlGetToJObjectRetry(request).ConfigureAwait(false);
if (jObject == null) {
return null;
}
IEnumerable<JToken> descriptions = jObject.SelectTokens("$.rgDescriptions.*");
if (descriptions == null) {
return null; // OK, empty inventory
}
Dictionary<ulong, Tuple<uint, Steam.Item.EType>> descriptionMap = new Dictionary<ulong, Tuple<uint, Steam.Item.EType>>();
foreach (JToken description in descriptions) {
string classIDString = description["classid"].ToString();
if (string.IsNullOrEmpty(classIDString)) {
Logging.LogNullError(nameof(classIDString), Bot.BotName);
continue;
}
ulong classID;
if (!ulong.TryParse(classIDString, out classID) || (classID == 0)) {
Logging.LogNullError(nameof(classID), Bot.BotName);
continue;
}
if (descriptionMap.ContainsKey(classID)) {
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[classID] = new Tuple<uint, Steam.Item.EType>(appID, type);
}
}
if (response == null) {
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
return false;
}
return true;
}
internal async Task<List<SteamItem>> GetMyTradableInventory() {
JObject jObject = null;
for (byte i = 0; i < WebBrowser.MaxRetries && jObject == null; i++) {
jObject = await WebBrowser.UrlGetToJObject("https://steamcommunity.com/my/inventory/json/753/6?trading=1", Cookie).ConfigureAwait(false);
}
if (jObject == null) {
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
return null;
}
IEnumerable<JToken> jTokens = jObject.SelectTokens("$.rgInventory.*");
if (jTokens == null) {
Logging.LogNullError("jTokens", Bot.BotName);
return null;
}
List<SteamItem> result = new List<SteamItem>();
foreach (JToken jToken in jTokens) {
try {
result.Add(JsonConvert.DeserializeObject<SteamItem>(jToken.ToString()));
} catch (Exception e) {
Logging.LogGenericException(e, Bot.BotName);
IEnumerable<JToken> items = jObject.SelectTokens("$.rgInventory.*");
if (items == null) {
Logging.LogNullError(nameof(items), Bot.BotName);
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) {
Logging.LogNullError(nameof(steamItem), Bot.BotName);
continue;
}
Tuple<uint, Steam.Item.EType> description;
if (descriptionMap.TryGetValue(steamItem.ClassID, 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; // OK, last page
}
if (ushort.TryParse(jObject["more_start"].ToString(), out nextPage)) {
continue;
}
Logging.LogNullError(nameof(nextPage), Bot.BotName);
break;
}
return result;
}
internal async Task<bool> SendTradeOffer(List<SteamItem> inventory, ulong partnerID, string token = null) {
if (inventory == null || inventory.Count == 0 || partnerID == 0) {
internal async Task<bool> SendTradeOffer(HashSet<Steam.Item> inventory, ulong partnerID, string token = null) {
if ((inventory == null) || (inventory.Count == 0) || (partnerID == 0)) {
Logging.LogNullError(nameof(inventory) + " || " + nameof(inventory.Count) + " || " + nameof(partnerID), Bot.BotName);
return false;
}
string sessionID;
if (!Cookie.TryGetValue("sessionid", out sessionID)) {
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
return false;
}
List<SteamTradeOfferRequest> trades = new List<SteamTradeOfferRequest>(1 + inventory.Count / Trading.MaxItemsPerTrade);
string sessionID = WebBrowser.CookieContainer.GetCookieValue(SteamCommunityURL, "sessionid");
if (string.IsNullOrEmpty(sessionID)) {
Logging.LogNullError(nameof(sessionID), Bot.BotName);
return false;
}
SteamTradeOfferRequest singleTrade = null;
for (ushort i = 0; i < inventory.Count; i++) {
if (i % Trading.MaxItemsPerTrade == 0) {
Steam.TradeOfferRequest singleTrade = new Steam.TradeOfferRequest();
HashSet<Steam.TradeOfferRequest> trades = new HashSet<Steam.TradeOfferRequest> { singleTrade };
byte itemID = 0;
foreach (Steam.Item item in inventory) {
if (itemID >= Trading.MaxItemsPerTrade) {
if (trades.Count >= Trading.MaxTradesPerAccount) {
break;
}
singleTrade = new SteamTradeOfferRequest();
singleTrade = new Steam.TradeOfferRequest();
trades.Add(singleTrade);
itemID = 0;
}
SteamItem item = inventory[i];
singleTrade.me.assets.Add(new SteamItem() {
appid = "753",
contextid = "6",
amount = item.amount,
assetid = item.id
singleTrade.ItemsToGive.Assets.Add(new Steam.Item {
AppID = Steam.Item.SteamAppID,
ContextID = Steam.Item.SteamContextID,
Amount = item.Amount,
AssetID = item.AssetID
});
itemID++;
}
string referer = "https://steamcommunity.com/tradeoffer/new";
string referer = SteamCommunityURL + "/tradeoffer/new";
string request = referer + "/send";
foreach (SteamTradeOfferRequest 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) ? "" : string.Format("{{ \"trade_offer_access_token\":\"{0}\" }}", token)} // TODO: This should be rewrote
};
HttpResponseMessage response = null;
for (byte i = 0; i < WebBrowser.MaxRetries && response == null; i++) {
response = await WebBrowser.UrlPost(request, data, Cookie, referer).ConfigureAwait(false);
}
if (response == null) {
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
foreach (Dictionary<string, string> data in trades.Select(trade => 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}\"}}" }
})) {
if (!await WebBrowser.UrlPostRetry(request, data, referer).ConfigureAwait(false)) {
return false;
}
}
@@ -384,78 +626,110 @@ namespace ArchiSteamFarm {
}
internal async Task<HtmlDocument> GetBadgePage(byte page) {
if (page == 0 || SteamID == 0) {
if (page == 0) {
Logging.LogNullError(nameof(page), Bot.BotName);
return null;
}
HtmlDocument htmlDocument = null;
for (byte i = 0; i < WebBrowser.MaxRetries && htmlDocument == null; i++) {
htmlDocument = await WebBrowser.UrlGetToHtmlDocument("https://steamcommunity.com/profiles/" + SteamID + "/badges?l=english&p=" + page, Cookie).ConfigureAwait(false);
}
if (htmlDocument == null) {
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
return null;
}
return htmlDocument;
string request = SteamCommunityURL + "/my/badges?p=" + page;
return await WebBrowser.UrlGetToHtmlDocumentRetry(request).ConfigureAwait(false);
}
internal async Task<HtmlDocument> GetGameCardsPage(ulong appID) {
if (appID == 0 || SteamID == 0) {
if (appID == 0) {
Logging.LogNullError(nameof(appID), Bot.BotName);
return null;
}
HtmlDocument htmlDocument = null;
for (byte i = 0; i < WebBrowser.MaxRetries && htmlDocument == null; i++) {
htmlDocument = await WebBrowser.UrlGetToHtmlDocument("https://steamcommunity.com/profiles/" + SteamID + "/gamecards/" + appID + "?l=english", Cookie).ConfigureAwait(false);
}
if (htmlDocument == null) {
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
return null;
}
return htmlDocument;
string request = SteamCommunityURL + "/my/gamecards/" + appID;
return await WebBrowser.UrlGetToHtmlDocumentRetry(request).ConfigureAwait(false);
}
private async Task UnlockParentalAccount(string parentalPin) {
if (string.IsNullOrEmpty(parentalPin) || parentalPin.Equals("0")) {
return;
internal async Task<bool> MarkInventory() {
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
return false;
}
string request = SteamCommunityURL + "/my/inventory";
return await WebBrowser.UrlHeadRetry(request).ConfigureAwait(false);
}
private async Task<bool?> IsLoggedIn() {
// It would make sense to use /my/profile here, but it dismisses notifications related to profile comments
// So instead, we'll use some less intrusive link, such as /my/videos
string request = SteamCommunityURL + "/my/videos";
Uri uri = await WebBrowser.UrlHeadToUriRetry(request).ConfigureAwait(false);
if (uri == null) {
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)) {
Logging.LogNullError(nameof(parentalPin), Bot.BotName);
return false;
}
if (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 }
};
HttpResponseMessage response = null;
for (byte i = 0; i < WebBrowser.MaxRetries && response == null; i++) {
response = await WebBrowser.UrlPost("https://steamcommunity.com/parental/ajaxunlock", data, Cookie, "https://steamcommunity.com/").ConfigureAwait(false);
bool result = await WebBrowser.UrlPostRetry(request, data, SteamCommunityURL).ConfigureAwait(false);
if (!result) {
Logging.LogGenericInfo("Failed!", Bot.BotName);
return false;
}
if (response == null) {
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
return;
}
IEnumerable<string> setCookieValues;
if (!response.Headers.TryGetValues("Set-Cookie", out setCookieValues)) {
Logging.LogNullError("setCookieValues", Bot.BotName);
return;
}
foreach (string setCookieValue in setCookieValues) {
if (setCookieValue.Contains("steamparental=")) {
string setCookie = setCookieValue.Substring(setCookieValue.IndexOf("steamparental=") + 14);
setCookie = setCookie.Substring(0, setCookie.IndexOf(';'));
Cookie["steamparental"] = setCookie;
Logging.LogGenericInfo("Success!", Bot.BotName);
return;
}
}
Logging.LogGenericWarning("Failed to unlock parental account!", Bot.BotName);
Logging.LogGenericInfo("Success!", Bot.BotName);
return true;
}
}
}

File diff suppressed because it is too large Load Diff

132
ArchiSteamFarm/BotConfig.cs Normal file
View File

@@ -0,0 +1,132 @@
/*
_ _ _ ____ _ _____
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
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 {
// ReSharper disable once ClassCannotBeInstantiated
// ReSharper disable once ClassNeverInstantiated.Global
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; }
[JsonProperty]
internal string SteamPassword { get; set; }
[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)) {
Logging.LogNullError(nameof(filePath));
return null;
}
if (!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() { }
}
}

View File

@@ -0,0 +1,121 @@
/*
_ _ _ ____ _ _____
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
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.Diagnostics.CodeAnalysis;
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)) {
Logging.LogNullError(nameof(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) {
Logging.LogNullError(nameof(botDatabase));
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(nameof(filePath));
}
FilePath = filePath;
Save();
}
// This constructor is used only by deserializer
[SuppressMessage("ReSharper", "UnusedMember.Local")]
private BotDatabase() { }
internal void Save() {
lock (FilePath) {
try {
File.WriteAllText(FilePath, JsonConvert.SerializeObject(this));
} catch (Exception e) {
Logging.LogGenericException(e);
}
}
}
}
}

View File

@@ -27,68 +27,45 @@ 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 sealed class CardsFarmer {
private const byte StatusCheckSleep = 5; // In minutes, how long to wait before checking the appID again
private const ushort MaxFarmingTime = 600; // In minutes, how long ASF is allowed to farm one game in solo mode
internal readonly ConcurrentDictionary<uint, float> GamesToFarm = new ConcurrentDictionary<uint, float>();
internal readonly List<uint> CurrentGamesFarming = new List<uint>();
private readonly ManualResetEvent FarmResetEvent = new ManualResetEvent(false);
private readonly SemaphoreSlim Semaphore = new SemaphoreSlim(1);
internal readonly ConcurrentHashSet<uint> CurrentGamesFarming = new ConcurrentHashSet<uint>();
private readonly ManualResetEventSlim FarmResetEvent = new ManualResetEventSlim(false);
private readonly SemaphoreSlim FarmingSemaphore = new SemaphoreSlim(1);
private readonly Bot Bot;
private readonly Timer Timer;
private bool ManualMode = false;
private bool NowFarming = false;
internal bool ManualMode { get; private set; }
private bool NowFarming;
internal CardsFarmer(Bot bot) {
if (bot == null) {
throw new ArgumentNullException(nameof(bot));
}
Bot = bot;
Timer = new Timer(
async e => await CheckGamesForFarming().ConfigureAwait(false),
null,
TimeSpan.FromMinutes(15), // Delay
TimeSpan.FromMinutes(15) // Period
);
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 static List<uint> GetGamesToFarmSolo(ConcurrentDictionary<uint, float> gamesToFarm) {
if (gamesToFarm == null) {
return null;
}
List<uint> result = new List<uint>();
foreach (KeyValuePair<uint, float> keyValue in gamesToFarm) {
if (keyValue.Value >= 2) {
result.Add(keyValue.Key);
}
}
return result;
}
internal static uint GetAnyGameToFarm(ConcurrentDictionary<uint, float> gamesToFarm) {
if (gamesToFarm == null) {
return 0;
}
foreach (uint appID in gamesToFarm.Keys) {
return appID;
}
return 0;
}
internal async Task<bool> SwitchToManualMode(bool manualMode) {
internal async Task SwitchToManualMode(bool manualMode) {
if (ManualMode == manualMode) {
return false;
return;
}
ManualMode = manualMode;
@@ -98,59 +75,115 @@ namespace ArchiSteamFarm {
await StopFarming().ConfigureAwait(false);
} else {
Logging.LogGenericInfo("Now running in Automatic Farming mode", Bot.BotName);
var start = Task.Run(async () => await StartFarming().ConfigureAwait(false));
StartFarming().Forget();
}
return true;
}
internal bool FarmMultiple(ConcurrentDictionary<uint, float> appIDs) {
if (appIDs.Count == 0) {
return true;
internal async Task StartFarming() {
if (NowFarming || ManualMode || Bot.PlayingBlocked) {
return;
}
float maxHour = 0;
await FarmingSemaphore.WaitAsync().ConfigureAwait(false);
foreach (float hour in appIDs.Values) {
if (hour > maxHour) {
maxHour = hour;
if (NowFarming || ManualMode || Bot.PlayingBlocked) {
FarmingSemaphore.Release(); // We have nothing to do, don't forget to release semaphore
return;
}
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;
}
Logging.LogGenericInfo("We have a total of " + GamesToFarm.Count + " games to farm on this account...", Bot.BotName);
// This is the last moment for final check if we can farm
if (Bot.PlayingBlocked) {
Logging.LogGenericInfo("But account is currently occupied, so farming is stopped!");
FarmingSemaphore.Release(); // We have nothing to do, don't forget to release semaphore
return;
}
NowFarming = true;
FarmingSemaphore.Release(); // From this point we allow other calls to shut us down
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)) {
continue;
}
NowFarming = false;
return;
}
}
}
} while (await IsAnythingToFarm().ConfigureAwait(false));
CurrentGamesFarming.Clear();
CurrentGamesFarming.TrimExcess();
foreach (uint appID in appIDs.Keys) {
CurrentGamesFarming.Add(appID);
}
CurrentGamesFarming.ClearAndTrim();
NowFarming = false;
Logging.LogGenericInfo("Now farming: " + string.Join(", ", appIDs.Keys), Bot.BotName);
if (Farm(maxHour, appIDs.Keys)) {
CurrentGamesFarming.Clear();
return true;
} else {
CurrentGamesFarming.Clear();
return false;
}
Logging.LogGenericInfo("Farming finished!", Bot.BotName);
await Bot.OnFarmingFinished(true).ConfigureAwait(false);
}
internal async Task<bool> FarmSolo(uint appID) {
if (appID == 0) {
return true;
internal async Task StopFarming() {
if (!NowFarming) {
return;
}
CurrentGamesFarming.Clear();
CurrentGamesFarming.TrimExcess();
CurrentGamesFarming.Add(appID);
await FarmingSemaphore.WaitAsync().ConfigureAwait(false);
Logging.LogGenericInfo("Now farming: " + appID, Bot.BotName);
if (await Farm(appID).ConfigureAwait(false)) {
float hours;
GamesToFarm.TryRemove(appID, out hours);
return true;
} else {
CurrentGamesFarming.Clear();
return false;
if (!NowFarming) {
FarmingSemaphore.Release();
return;
}
Logging.LogGenericInfo("Sending signal to stop farming", Bot.BotName);
FarmResetEvent.Set();
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("Farming stopped!", Bot.BotName);
FarmingSemaphore.Release();
}
internal async Task RestartFarming() {
@@ -158,97 +191,21 @@ namespace ArchiSteamFarm {
await StartFarming().ConfigureAwait(false);
}
internal async Task StartFarming() {
await Semaphore.WaitAsync().ConfigureAwait(false);
if (ManualMode) {
Semaphore.Release(); // We have nothing to do, don't forget to release semaphore
return;
private static HashSet<uint> GetGamesToFarmSolo(ConcurrentDictionary<uint, float> gamesToFarm) {
if (gamesToFarm == null) {
Logging.LogNullError(nameof(gamesToFarm));
return null;
}
if (!await IsAnythingToFarm().ConfigureAwait(false)) {
Semaphore.Release(); // We have nothing to do, don't forget to release semaphore
return;
HashSet<uint> result = new HashSet<uint>();
foreach (KeyValuePair<uint, float> keyValue in gamesToFarm.Where(keyValue => keyValue.Value >= 2)) {
result.Add(keyValue.Key);
}
Logging.LogGenericInfo("We have a total of " + GamesToFarm.Count + " games to farm on this account...", Bot.BotName);
NowFarming = true;
Semaphore.Release(); // From this point we allow other calls to shut us down
// Now the algorithm used for farming depends on whether account is restricted or not
if (Bot.CardDropsRestricted) { // If we have restricted card drops, we use complex algorithm
Logging.LogGenericInfo("Chosen farming algorithm: Complex", Bot.BotName);
while (GamesToFarm.Count > 0) {
List<uint> gamesToFarmSolo = GetGamesToFarmSolo(GamesToFarm);
if (gamesToFarmSolo.Count > 0) {
while (gamesToFarmSolo.Count > 0) {
uint appID = gamesToFarmSolo[0];
if (await FarmSolo(appID).ConfigureAwait(false)) {
Logging.LogGenericInfo("Done farming: " + appID, Bot.BotName);
gamesToFarmSolo.Remove(appID);
gamesToFarmSolo.TrimExcess();
} else {
NowFarming = false;
return;
}
}
} else {
if (FarmMultiple(GamesToFarm)) {
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 = GetAnyGameToFarm(GamesToFarm);
if (await FarmSolo(appID).ConfigureAwait(false)) {
Logging.LogGenericInfo("Done farming: " + appID, Bot.BotName);
} else {
NowFarming = false;
return;
}
}
}
CurrentGamesFarming.Clear();
CurrentGamesFarming.TrimExcess();
NowFarming = false;
Logging.LogGenericInfo("Farming finished!", Bot.BotName);
await Bot.OnFarmingFinished().ConfigureAwait(false);
}
internal async Task StopFarming() {
await Semaphore.WaitAsync().ConfigureAwait(false);
if (!NowFarming) {
Semaphore.Release();
return;
}
Logging.LogGenericInfo("Sending signal to stop farming", Bot.BotName);
FarmResetEvent.Set();
for (byte i = 0; i < 5 && NowFarming; i++) {
Logging.LogGenericInfo("Waiting for reaction...", Bot.BotName);
await Utilities.SleepAsync(1000).ConfigureAwait(false);
}
FarmResetEvent.Reset();
Logging.LogGenericInfo("Farming stopped!", Bot.BotName);
Semaphore.Release();
return result;
}
private async Task<bool> IsAnythingToFarm() {
if (NowFarming) {
return false;
}
if (await Bot.ArchiWebHandler.ReconnectIfNeeded().ConfigureAwait(false)) {
return false;
}
Logging.LogGenericInfo("Checking badges...", Bot.BotName);
// Find the number of badge pages
@@ -261,78 +218,113 @@ namespace ArchiSteamFarm {
byte maxPages = 1;
HtmlNodeCollection htmlNodeCollection = htmlDocument.DocumentNode.SelectNodes("//a[@class='pagelink']");
if (htmlNodeCollection != null && htmlNodeCollection.Count > 0) {
if ((htmlNodeCollection != null) && (htmlNodeCollection.Count > 0)) {
HtmlNode htmlNode = htmlNodeCollection[htmlNodeCollection.Count - 1];
if (!byte.TryParse(htmlNode.InnerText, out maxPages)) {
maxPages = 1; // Should never happen
string lastPage = htmlNode.InnerText;
if (!string.IsNullOrEmpty(lastPage)) {
if (!byte.TryParse(lastPage, out maxPages)) {
maxPages = 1; // Should never happen
}
}
}
GamesToFarm.Clear();
// Find APPIDs we need to farm
CheckPage(htmlDocument);
if (maxPages <= 1) {
return GamesToFarm.Count > 0;
}
Logging.LogGenericInfo("Checking other pages...", Bot.BotName);
List<Task> tasks = new List<Task>(maxPages - 1);
for (byte page = 1; page <= maxPages; page++) {
if (page == 1) {
CheckPage(htmlDocument); // Because we fetched page number 1 already
} else {
byte currentPage = page; // We need a copy of variable being passed when in for loops
tasks.Add(Task.Run(async () => await CheckPage(currentPage).ConfigureAwait(false)));
}
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);
if (GamesToFarm.Count == 0) {
return true;
}
// If we have restricted card drops, actually do check hours of all games that are left to farm
if (Bot.CardDropsRestricted) {
tasks = new List<Task>(GamesToFarm.Keys.Count);
Logging.LogGenericInfo("Checking hours...", Bot.BotName);
foreach (uint appID in GamesToFarm.Keys) {
tasks.Add(Task.Run(async () => await CheckHours(appID).ConfigureAwait(false)));
}
await Task.WhenAll(tasks).ConfigureAwait(false);
}
return true;
return GamesToFarm.Count > 0;
}
private void CheckPage(HtmlDocument htmlDocument) {
if (htmlDocument == null) {
Logging.LogNullError(nameof(htmlDocument), Bot.BotName);
return;
}
HtmlNodeCollection htmlNodeCollection = htmlDocument.DocumentNode.SelectNodes("//a[@class='btn_green_white_innerfade btn_small_thin']");
if (htmlNodeCollection == null) {
HtmlNodeCollection htmlNodes = htmlDocument.DocumentNode.SelectNodes("//div[@class='badge_title_stats']");
if (htmlNodes == null) {
Logging.LogNullError(nameof(htmlNodes), Bot.BotName);
return;
}
foreach (HtmlNode htmlNode in htmlNodeCollection) {
string steamLink = htmlNode.GetAttributeValue("href", null);
if (steamLink == null) {
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(nameof(steamLink), Bot.BotName);
continue;
}
uint appID = (uint) Utilities.OnlyNumbers(steamLink);
if (appID == 0) {
int index = steamLink.LastIndexOf('/');
if (index < 0) {
Logging.LogNullError(nameof(index), Bot.BotName);
continue;
}
if (Bot.GlobalBlacklist.Contains(appID) || Bot.Blacklist.Contains(appID)) {
index++;
if (steamLink.Length <= index) {
Logging.LogNullError(nameof(steamLink.Length), Bot.BotName);
continue;
}
// We assume that every game has at least 2 hours played, until we actually check them
GamesToFarm[appID] = 2;
steamLink = steamLink.Substring(index);
uint appID;
if (!uint.TryParse(steamLink, out appID) || (appID == 0)) {
Logging.LogNullError(nameof(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(nameof(timeNode), Bot.BotName);
continue;
}
string hoursString = timeNode.InnerText;
if (string.IsNullOrEmpty(hoursString)) {
Logging.LogNullError(nameof(hoursString), Bot.BotName);
continue;
}
float hours = 0;
Match match = Regex.Match(hoursString, @"[0-9\.,]+");
if (match.Success) {
if (!float.TryParse(match.Value, NumberStyles.Number, CultureInfo.InvariantCulture, out hours)) {
Logging.LogNullError(nameof(hours), Bot.BotName);
continue;
}
}
GamesToFarm[appID] = hours;
}
}
private async Task CheckPage(byte page) {
if (page == 0) {
Logging.LogNullError(nameof(page), Bot.BotName);
return;
}
@@ -344,51 +336,17 @@ namespace ArchiSteamFarm {
CheckPage(htmlDocument);
}
private async Task CheckHours(uint appID) {
if (appID == 0) {
return;
}
HtmlDocument htmlDocument = await Bot.ArchiWebHandler.GetGameCardsPage(appID).ConfigureAwait(false);
if (htmlDocument == null) {
Logging.LogNullError("htmlDocument", Bot.BotName);
return;
}
HtmlNode htmlNode = htmlDocument.DocumentNode.SelectSingleNode("//div[@class='badge_title_stats_playtime']");
if (htmlNode == null) {
Logging.LogNullError("htmlNode", Bot.BotName);
return;
}
string hoursString = htmlNode.InnerText;
if (string.IsNullOrEmpty(hoursString)) {
Logging.LogNullError("hoursString", Bot.BotName);
return;
}
hoursString = Regex.Match(hoursString, @"[0-9\.,]+").Value;
float hours;
if (string.IsNullOrEmpty(hoursString)) {
hours = 0;
} else {
hours = float.Parse(hoursString, CultureInfo.InvariantCulture);
}
GamesToFarm[appID] = hours;
}
private async Task CheckGamesForFarming() {
if (NowFarming || GamesToFarm.Count > 0 || !Bot.SteamClient.IsConnected) {
if (NowFarming || ManualMode || !Bot.SteamClient.IsConnected) {
return;
}
await StartFarming().ConfigureAwait(false);
}
private async Task<bool?> ShouldFarm(ulong appID) {
private async Task<bool?> ShouldFarm(uint appID) {
if (appID == 0) {
Logging.LogNullError(nameof(appID), Bot.BotName);
return false;
}
@@ -398,35 +356,101 @@ namespace ArchiSteamFarm {
}
HtmlNode htmlNode = htmlDocument.DocumentNode.SelectSingleNode("//span[@class='progress_info_bold']");
if (htmlNode == null) {
await Bot.ArchiWebHandler.ReconnectIfNeeded().ConfigureAwait(false);
return null;
if (htmlNode != null) {
return !htmlNode.InnerText.Contains("No card drops");
}
return !htmlNode.InnerText.Contains("No card drops");
Logging.LogNullError(nameof(htmlNode), Bot.BotName);
return null;
}
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.ClearAndTrim();
return true;
}
Logging.LogGenericInfo("Now farming: " + string.Join(", ", CurrentGamesFarming), Bot.BotName);
bool result = FarmHours(maxHour, CurrentGamesFarming);
CurrentGamesFarming.ClearAndTrim();
return result;
}
private async Task<bool> FarmSolo(uint appID) {
if (appID == 0) {
Logging.LogNullError(nameof(appID), Bot.BotName);
return true;
}
CurrentGamesFarming.Add(appID);
Logging.LogGenericInfo("Now farming: " + appID, Bot.BotName);
bool result = await Farm(appID).ConfigureAwait(false);
CurrentGamesFarming.ClearAndTrim();
if (!result) {
return false;
}
float hours;
if (!GamesToFarm.TryRemove(appID, out hours)) {
return false;
}
TimeSpan timeSpan = TimeSpan.FromHours(hours);
Logging.LogGenericInfo("Done farming: " + appID + " after " + timeSpan.ToString(@"hh\:mm") + " hours of playtime!", Bot.BotName);
return true;
}
private async Task<bool> Farm(uint appID) {
if (appID == 0) {
Logging.LogNullError(nameof(appID), Bot.BotName);
return false;
}
Bot.ArchiHandler.PlayGames(appID);
bool success = true;
bool? keepFarming = await ShouldFarm(appID).ConfigureAwait(false);
for (ushort farmingTime = 0; farmingTime <= MaxFarmingTime && (!keepFarming.HasValue || keepFarming.Value); farmingTime += StatusCheckSleep) {
Logging.LogGenericInfo("Still farming: " + appID, Bot.BotName);
if (FarmResetEvent.WaitOne(1000 * 60 * StatusCheckSleep)) {
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.ArchiHandler.PlayGames(0);
Logging.LogGenericInfo("Stopped farming: " + appID, Bot.BotName);
return success;
}
private bool Farm(float maxHour, ICollection<uint> appIDs) {
private bool FarmHours(float maxHour, ConcurrentHashSet<uint> appIDs) {
if ((maxHour < 0) || (appIDs == null) || (appIDs.Count == 0)) {
Logging.LogNullError(nameof(maxHour) + " || " + nameof(appIDs) + " || " + nameof(appIDs.Count), Bot.BotName);
return false;
}
if (maxHour >= 2) {
return true;
}
@@ -435,26 +459,21 @@ namespace ArchiSteamFarm {
bool success = true;
while (maxHour < 2) {
Logging.LogGenericInfo("Still farming: " + string.Join(", ", appIDs), Bot.BotName);
if (FarmResetEvent.WaitOne(1000 * 60 * StatusCheckSleep)) {
if (FarmResetEvent.Wait(60 * 1000 * Program.GlobalConfig.FarmingDelay)) {
success = false;
break;
}
// Don't forget to update our GamesToFarm hours
float timePlayed = StatusCheckSleep / 60.0F;
foreach (KeyValuePair<uint, float> gameToFarm in GamesToFarm) {
if (!appIDs.Contains(gameToFarm.Key)) {
continue;
}
GamesToFarm[gameToFarm.Key] = gameToFarm.Value + timePlayed;
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.ArchiHandler.PlayGames(0);
Logging.LogGenericInfo("Stopped farming: " + string.Join(", ", appIDs), Bot.BotName);
return success;
}

View File

@@ -22,45 +22,37 @@
*/
using SteamKit2;
using SteamKit2.Internal;
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
namespace ArchiSteamFarm {
internal sealed class CMsgClientClanInviteAction : ISteamSerializableMessage, ISteamSerializable {
internal ulong GroupID { get; set; } = 0;
internal bool AcceptInvite { get; set; } = true;
internal sealed class ConcurrentEnumerator<T> : IEnumerator<T> {
public T Current => Enumerator.Current;
EMsg ISteamSerializableMessage.GetEMsg() {
return EMsg.ClientAcknowledgeClanInvite;
object IEnumerator.Current => Current;
private readonly IEnumerator<T> Enumerator;
private readonly ReaderWriterLockSlim Lock;
internal ConcurrentEnumerator(ICollection<T> collection, ReaderWriterLockSlim @lock) {
if ((collection == null) || (@lock == null)) {
throw new ArgumentNullException(nameof(collection) + " || " + nameof(@lock));
}
@lock.EnterReadLock();
Lock = @lock;
Enumerator = collection.GetEnumerator();
}
void ISteamSerializable.Serialize(Stream stream) {
if (stream == null) {
return;
}
public bool MoveNext() => Enumerator.MoveNext();
public void Reset() => Enumerator.Reset();
try {
BinaryWriter binaryWriter = new BinaryWriter(stream);
binaryWriter.Write(GroupID);
binaryWriter.Write(AcceptInvite);
} catch (Exception e) {
Logging.LogGenericException(e);
}
}
void ISteamSerializable.Deserialize(Stream stream) {
if (stream == null) {
return;
}
try {
BinaryReader binaryReader = new BinaryReader(stream);
GroupID = binaryReader.ReadUInt64();
AcceptInvite = binaryReader.ReadBoolean();
} catch (Exception e) {
Logging.LogGenericException(e);
public void Dispose() {
if (Lock != null) {
Lock.ExitReadLock();
}
}
}

View File

@@ -0,0 +1,123 @@
/*
_ _ _ ____ _ _____
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
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.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
namespace ArchiSteamFarm {
internal sealed class ConcurrentHashSet<T> : ICollection<T>, IDisposable {
private readonly HashSet<T> HashSet = new HashSet<T>();
private readonly ReaderWriterLockSlim Lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
public bool IsReadOnly => false;
public IEnumerator<T> GetEnumerator() => new ConcurrentEnumerator<T>(HashSet, Lock);
public int Count {
get {
Lock.EnterReadLock();
try {
return HashSet.Count;
} finally {
Lock.ExitReadLock();
}
}
}
[SuppressMessage("ReSharper", "UnusedMethodReturnValue.Global")]
public bool Add(T item) {
Lock.EnterWriteLock();
try {
return HashSet.Add(item);
} finally {
Lock.ExitWriteLock();
}
}
public void Clear() {
Lock.EnterWriteLock();
try {
HashSet.Clear();
} finally {
Lock.ExitWriteLock();
}
}
public void ClearAndTrim() {
Lock.EnterWriteLock();
try {
HashSet.Clear();
HashSet.TrimExcess();
} finally {
Lock.ExitWriteLock();
}
}
public bool Contains(T item) {
Lock.EnterReadLock();
try {
return HashSet.Contains(item);
} finally {
Lock.ExitReadLock();
}
}
public bool Remove(T item) {
Lock.EnterWriteLock();
try {
return HashSet.Remove(item);
} finally {
Lock.ExitWriteLock();
}
}
public void Dispose() {
if (Lock != null) {
Lock.Dispose();
}
}
public void CopyTo(T[] array, int arrayIndex) {
Lock.EnterReadLock();
try {
HashSet.CopyTo(array, arrayIndex);
} finally {
Lock.ExitReadLock();
}
}
void ICollection<T>.Add(T item) => Add(item);
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
}

View File

@@ -22,14 +22,43 @@
*/
using SteamKit2;
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
namespace ArchiSteamFarm {
internal static class Debugging {
#if DEBUG
[SuppressMessage("ReSharper", "ConvertToConstant.Global")]
internal static readonly bool IsDebugBuild = true;
#else
[SuppressMessage("ReSharper", "ConvertToConstant.Global")]
internal static readonly bool IsDebugBuild = false;
#endif
internal static bool IsReleaseBuild { get { return !IsDebugBuild; } }
internal static bool NetHookAlreadyInitialized { get; set; }
internal sealed class DebugListener : IDebugListener {
private readonly string FilePath;
internal DebugListener(string filePath) {
if (string.IsNullOrEmpty(filePath)) {
throw new ArgumentNullException(nameof(filePath));
}
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);
}
}
}
}
}
}

View File

@@ -0,0 +1,179 @@
/*
_ _ _ ____ _ _____
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
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.Diagnostics.CodeAnalysis;
using System.IO;
using System.Net.Sockets;
namespace ArchiSteamFarm {
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated"), SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
internal sealed class GlobalConfig {
[SuppressMessage("ReSharper", "UnusedMember.Global")]
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 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)) {
Logging.LogNullError(nameof(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) {
Logging.LogNullError(nameof(globalConfig));
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) {
return globalConfig;
}
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() { }
}
}

View File

@@ -0,0 +1,103 @@
/*
_ _ _ ____ _ _____
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
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.Diagnostics.CodeAnalysis;
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)) {
Logging.LogNullError(nameof(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) {
Logging.LogNullError(nameof(globalDatabase));
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(nameof(filePath));
}
FilePath = filePath;
Save();
}
// This constructor is used only by deserializer
[SuppressMessage("ReSharper", "UnusedMember.Local")]
private GlobalDatabase() { }
private void Save() {
lock (FilePath) {
try {
File.WriteAllText(FilePath, JsonConvert.SerializeObject(this));
} catch (Exception e) {
Logging.LogGenericException(e);
}
}
}
}
}

View File

@@ -0,0 +1,48 @@
/*
_ _ _ ____ _ _____
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
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.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Newtonsoft.Json;
namespace ArchiSteamFarm.JSON {
internal static class GitHub {
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global"), SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Local")]
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; }
}
}
}

View File

@@ -0,0 +1,325 @@
/*
_ _ _ ____ _ _____
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
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.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Newtonsoft.Json;
using SteamKit2;
namespace ArchiSteamFarm.JSON {
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), SuppressMessage("ReSharper", "UnusedMember.Local")]
private 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), SuppressMessage("ReSharper", "UnusedMember.Local")]
private 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)]
private 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), SuppressMessage("ReSharper", "UnusedMember.Local")]
private string ID {
get { return AssetIDString; }
set { AssetIDString = value; }
}
internal ulong ClassID { get; set; }
[JsonProperty(PropertyName = "classid", Required = Required.DisallowNull), SuppressMessage("ReSharper", "UnusedMember.Local")]
private 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), SuppressMessage("ReSharper", "UnusedMember.Local")]
private 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), SuppressMessage("ReSharper", "UnusedMember.Local")]
private 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
[SuppressMessage("ReSharper", "UnusedMember.Global")]
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), SuppressMessage("ReSharper", "UnusedMember.Local")]
private 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 { private 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 => OtherSteamID3 == 0 ? 0 : new SteamID(OtherSteamID3, EUniverse.Public, EAccountType.Individual);
internal bool IsSteamCardsOnlyTradeForUs() => ItemsToGive.All(item => (item.AppID == Item.SteamAppID) && (item.ContextID == Item.SteamContextID) && ((item.Type == Item.EType.FoilTradingCard) || (item.Type == Item.EType.TradingCard)));
internal bool IsPotentiallyDupesTradeForUs() {
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> { [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> { [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 of items to give is at least amount of items to receive (per game and per type)
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;
}
}
[SuppressMessage("ReSharper", "UnusedMember.Global")]
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();
}
}
}

View File

@@ -24,6 +24,7 @@
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Runtime.CompilerServices;
@@ -31,78 +32,101 @@ namespace ArchiSteamFarm {
internal static class Logging {
private static readonly object FileLock = new object();
internal static bool LogToFile { get; set; } = false;
private static bool LogToFile;
internal static void Init() {
LogToFile = Program.GlobalConfig.LogToFile;
if (!LogToFile) {
return;
}
lock (FileLock) {
if (!LogToFile) {
return;
}
try {
File.Delete(Program.LogFile);
} catch (Exception e) {
bool logToFile = LogToFile;
LogToFile = false;
LogGenericException(e);
LogToFile = logToFile;
}
}
}
internal static void LogGenericWTF(string message, string botName = "Main", [CallerMemberName] string previousMethodName = "") {
internal static void LogGenericWTF(string message, string botName = "Main", [CallerMemberName] string previousMethodName = null) {
if (string.IsNullOrEmpty(message)) {
LogNullError(nameof(message), botName);
return;
}
Log("[!!] WTF: " + previousMethodName + "() <" + botName + "> " + message + ", WTF?");
}
internal static void LogGenericError(string message, string botName = "Main", [CallerMemberName] string previousMethodName = "") {
internal static void LogGenericError(string message, string botName = "Main", [CallerMemberName] string previousMethodName = null) {
if (string.IsNullOrEmpty(message)) {
LogNullError(nameof(message), botName);
return;
}
Log("[!!] ERROR: " + previousMethodName + "() <" + botName + "> " + message);
}
internal static void LogGenericException(Exception exception, string botName = "Main", [CallerMemberName] string previousMethodName = "") {
if (exception == null) {
return;
}
internal static void LogGenericException(Exception exception, string botName = "Main", [CallerMemberName] string previousMethodName = null) {
while (true) {
if (exception == null) {
LogNullError(nameof(exception), botName);
return;
}
Log("[!] EXCEPTION: " + previousMethodName + "() <" + botName + "> " + exception.Message);
Log("[!] StackTrace: " + exception.StackTrace);
Log("[!] EXCEPTION: " + previousMethodName + "() <" + botName + "> " + exception.Message);
Log("[!] StackTrace:" + Environment.NewLine + exception.StackTrace);
Exception innerException = exception.InnerException;
if (innerException != null) {
LogGenericException(innerException, botName, previousMethodName);
if (exception.InnerException != null) {
exception = exception.InnerException;
continue;
}
break;
}
}
internal static void LogGenericWarning(string message, string botName = "Main", [CallerMemberName] string previousMethodName = "") {
internal static void LogGenericWarning(string message, string botName = "Main", [CallerMemberName] string previousMethodName = null) {
if (string.IsNullOrEmpty(message)) {
LogNullError(nameof(message), botName);
return;
}
Log("[!] WARNING: " + previousMethodName + "() <" + botName + "> " + message);
}
internal static void LogGenericInfo(string message, string botName = "Main", [CallerMemberName] string previousMethodName = "") {
internal static void LogGenericInfo(string message, string botName = "Main", [CallerMemberName] string previousMethodName = null) {
if (string.IsNullOrEmpty(message)) {
LogNullError(nameof(message), botName);
return;
}
Log("[*] INFO: " + previousMethodName + "() <" + botName + "> " + message);
}
internal static void LogNullError(string nullObjectName, string botName = "Main", [CallerMemberName] string previousMethodName = "") {
if (string.IsNullOrEmpty(nullObjectName)) {
return;
}
[SuppressMessage("ReSharper", "ExplicitCallerInfoArgument")]
internal static void LogNullError(string nullObjectName, string botName = "Main", [CallerMemberName] string previousMethodName = null) {
while (true) {
if (string.IsNullOrEmpty(nullObjectName)) {
nullObjectName = nameof(nullObjectName);
continue;
}
LogGenericError(nullObjectName + " is null!", botName, previousMethodName);
LogGenericError(nullObjectName + " is null!", botName, previousMethodName);
break;
}
}
[Conditional("DEBUG")]
internal static void LogGenericDebug(string message, string botName = "Main", [CallerMemberName] string previousMethodName = "") {
[Conditional("DEBUG"), SuppressMessage("ReSharper", "UnusedMember.Global")]
internal static void LogGenericDebug(string message, string botName = "Main", [CallerMemberName] string previousMethodName = null) {
if (string.IsNullOrEmpty(message)) {
LogNullError(nameof(message), botName);
return;
}
@@ -111,6 +135,7 @@ namespace ArchiSteamFarm {
private static void Log(string message) {
if (string.IsNullOrEmpty(message)) {
LogNullError(nameof(message));
return;
}
@@ -118,19 +143,27 @@ namespace ArchiSteamFarm {
// Write on console only when not awaiting response from user
if (!Program.ConsoleIsBusy) {
Console.Write(loggedMessage);
try {
Console.Write(loggedMessage);
} catch {
// Ignored
}
}
if (LogToFile) {
lock (FileLock) {
try {
File.AppendAllText(Program.LogFile, loggedMessage);
} catch (Exception e) {
bool logToFile = LogToFile;
LogToFile = false;
LogGenericException(e);
LogToFile = logToFile;
}
if (!LogToFile) {
return;
}
lock (FileLock) {
if (!LogToFile) {
return;
}
try {
File.AppendAllText(Program.LogFile, loggedMessage);
} catch (Exception e) {
LogToFile = false;
LogGenericException(e);
}
}
}

View File

@@ -22,16 +22,23 @@
*/
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using ArchiSteamFarm.JSON;
namespace ArchiSteamFarm {
internal static class Program {
internal enum EUserInputType : byte {
Unknown,
DeviceID,
Login,
Password,
PhoneNumber,
@@ -40,121 +47,295 @@ namespace ArchiSteamFarm {
SteamParentalPIN,
RevocationCode,
TwoFactorAuthentication,
WCFHostname
}
internal enum EMode : byte {
private enum EMode : byte {
[SuppressMessage("ReSharper", "UnusedMember.Local")]
Unknown,
Normal, // Standard most common usage
Client, // WCF client only
Server // Normal + WCF server
}
private const string LatestGithubReleaseURL = "https://api.github.com/repos/JustArchi/ArchiSteamFarm/releases/latest";
internal const string ConfigDirectory = "config";
internal const string DebugDirectory = "debug";
internal const string LogFile = "log.txt";
internal const string GithubRepo = "JustArchi/ArchiSteamFarm";
private const string ASF = "ASF";
private const string GithubReleaseURL = "https://api.github.com/repos/" + GithubRepo + "/releases"; // GitHub API is HTTPS only
private const string GlobalConfigFile = ASF + ".json";
private const string GlobalDatabaseFile = ASF + ".db";
internal static readonly Version Version = Assembly.GetEntryAssembly().GetName().Version;
private 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 ExecutableFile = Assembly.Location;
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 readonly string Version = Assembly.GetName().Version.ToString();
internal static GlobalConfig GlobalConfig { get; private set; }
internal static GlobalDatabase GlobalDatabase { get; private set; }
internal static bool ConsoleIsBusy { get; private set; }
private static EMode Mode;
private static Timer AutoUpdatesTimer;
private static EMode Mode = EMode.Normal;
private static WebBrowser WebBrowser;
internal static bool ConsoleIsBusy { get; private set; } = false;
internal static async Task CheckForUpdate(bool updateOverride = false) {
string oldExeFile = ExecutableFile + ".old";
private static async Task CheckForUpdate() {
JObject response = await WebBrowser.UrlGetToJObject(LatestGithubReleaseURL).ConfigureAwait(false);
if (response == null) {
// 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";
}
Logging.LogGenericInfo("Checking new version...");
string response = await WebBrowser.UrlGetToContentRetry(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.LogGenericInfo("Local version: " + localVersion);
Logging.LogGenericInfo("Remote version: " + remoteVersion);
if ((releases == null) || (releases.Count == 0)) {
Logging.LogGenericWarning("Could not check latest version!");
return;
}
int comparisonResult = localVersion.CompareTo(remoteVersion);
if (comparisonResult < 0) {
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) {
return;
}
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 (!updateOverride && !GlobalConfig.AutoUpdates) {
Logging.LogGenericInfo("New version is available!");
Logging.LogGenericInfo("Consider updating yourself!");
await Utilities.SleepAsync(5000).ConfigureAwait(false);
} else if (comparisonResult > 0) {
Logging.LogGenericInfo("You're currently using pre-release version!");
Logging.LogGenericInfo("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 = releaseResponse.Assets.FirstOrDefault(asset => !string.IsNullOrEmpty(asset.Name) && asset.Name.Equals(ExecutableName, StringComparison.OrdinalIgnoreCase));
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 = await WebBrowser.UrlGetToBytesRetry(binaryAsset.DownloadURL).ConfigureAwait(false);
if (result == null) {
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 {
// Ignored
}
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 {
// Ignored
}
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 void Exit(int exitCode = 0) {
WCF.StopServer();
Environment.Exit(exitCode);
}
internal static void Restart() {
System.Diagnostics.Process.Start(ExecutableFile, string.Join(" ", Environment.GetCommandLineArgs()));
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);
var releaseLater = Task.Run(async () => {
await Utilities.SleepAsync(7000).ConfigureAwait(false); // We must add some delay to not get caught by Steam rate limiter
SteamSemaphore.Release();
});
}
internal static string GetUserInput(EUserInputType userInputType, string botName = "Main", 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 extraInformation = null) {
string result;
lock (ConsoleLock) {
ConsoleIsBusy = true;
switch (userInputType) {
case EUserInputType.DeviceID:
Console.Write("<" + botName + "> Please enter your Device ID (including \"android:\"): ");
break;
case EUserInputType.Login:
Console.Write("<" + botLogin + "> Please enter your login: ");
Console.Write("<" + botName + "> Please enter your login: ");
break;
case EUserInputType.Password:
Console.Write("<" + botLogin + "> Please enter your password: ");
Console.Write("<" + botName + "> Please enter your password: ");
break;
case EUserInputType.PhoneNumber:
Console.Write("<" + botLogin + "> Please enter your full phone number (e.g. +1234567890): ");
Console.Write("<" + botName + "> Please enter your full phone number (e.g. +1234567890): ");
break;
case EUserInputType.SMS:
Console.Write("<" + botLogin + "> Please enter SMS code sent on your mobile: ");
Console.Write("<" + 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("<" + botName + "> Please enter the auth code sent to your email: ");
break;
case EUserInputType.SteamParentalPIN:
Console.Write("<" + botLogin + "> Please enter steam parental PIN: ");
Console.Write("<" + botName + "> Please enter steam parental PIN: ");
break;
case EUserInputType.RevocationCode:
Console.WriteLine("<" + botLogin + "> PLEASE WRITE DOWN YOUR REVOCATION CODE: " + extraInformation);
Console.WriteLine("<" + botLogin + "> THIS IS THE ONLY WAY TO NOT GET LOCKED OUT OF YOUR ACCOUNT!");
Console.Write("<" + botLogin + "> Hit enter once ready...");
Console.WriteLine("<" + botName + "> PLEASE WRITE DOWN YOUR REVOCATION CODE: " + extraInformation);
Console.Write("<" + botName + "> Hit enter once ready...");
break;
case EUserInputType.TwoFactorAuthentication:
Console.Write("<" + botLogin + "> Please enter your 2 factor auth code from your authenticator app: ");
Console.Write("<" + botName + "> Please enter your 2 factor auth code from your authenticator app: ");
break;
case EUserInputType.WCFHostname:
Console.Write("<" + botName + "> Please enter your WCF hostname: ");
break;
default:
Console.Write("<" + 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;
}
return result.Trim(); // Get rid of all whitespace characters
return !string.IsNullOrEmpty(result) ? result.Trim() : null;
}
internal static void OnBotShutdown() {
foreach (Bot bot in Bot.Bots.Values) {
if (bot.KeepRunning) {
return;
}
if (Bot.Bots.Values.Any(bot => bot.KeepRunning)) {
return;
}
if (WCF.IsServerRunning()) {
@@ -162,33 +343,51 @@ namespace ArchiSteamFarm {
}
Logging.LogGenericInfo("No bots are running, exiting");
Thread.Sleep(5000);
ShutdownResetEvent.Set();
}
private static void InitServices() {
Logging.Init();
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) {
private static void ParseArgs(IEnumerable<string> args) {
if (args == null) {
Logging.LogNullError(nameof(args));
return;
}
foreach (string arg in args) {
switch (arg) {
case "":
break;
case "--client":
Mode = EMode.Client;
Logging.LogToFile = false;
break;
case "--log":
Logging.LogToFile = true;
break;
case "--no-log":
Logging.LogToFile = false;
break;
case "--server":
Mode = EMode.Server;
WCF.StartServer();
break;
default:
if (arg.StartsWith("--")) {
if (arg.StartsWith("--", StringComparison.Ordinal)) {
Logging.LogGenericWarning("Unrecognized parameter: " + arg);
continue;
}
@@ -198,10 +397,10 @@ namespace ArchiSteamFarm {
continue;
}
Logging.LogGenericInfo("Command sent: \"" + arg + "\"");
Logging.LogGenericInfo("Command sent: " + arg);
// We intentionally execute this async block synchronously
Logging.LogGenericInfo("Response received: \"" + WCF.SendCommand(arg) + "\"");
Logging.LogGenericInfo("Response received: " + WCF.SendCommand(arg));
/*
Task.Run(async () => {
Logging.LogGenericNotice("WCF", "Response received: " + await WCF.SendCommand(arg).ConfigureAwait(false));
@@ -213,15 +412,26 @@ namespace ArchiSteamFarm {
}
private static void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs args) {
if (sender == null || args == null) {
if ((sender == null) || (args == null) || (args.ExceptionObject == null)) {
Logging.LogNullError(nameof(sender) + " || " + nameof(args) + " || " + nameof(args.ExceptionObject));
return;
}
Logging.LogGenericException((Exception) args.ExceptionObject);
}
private static void Main(string[] args) {
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(UnhandledExceptionHandler);
private static void UnobservedTaskExceptionHandler(object sender, UnobservedTaskExceptionEventArgs args) {
if ((sender == null) || (args == null) || (args.Exception == null)) {
Logging.LogNullError(nameof(sender) + " || " + nameof(args) + " || " + nameof(args.Exception));
return;
}
Logging.LogGenericException(args.Exception);
}
private static void Init(IEnumerable<string> args) {
AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionHandler;
TaskScheduler.UnobservedTaskException += UnobservedTaskExceptionHandler;
Logging.LogGenericInfo("Archi's Steam Farm, version " + Version);
Directory.SetCurrentDirectory(ExecutableDirectory);
@@ -231,7 +441,7 @@ namespace ArchiSteamFarm {
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++) {
for (byte i = 0; i < 4; i++) {
Directory.SetCurrentDirectory("..");
if (Directory.Exists(ConfigDirectory)) {
break;
@@ -244,19 +454,30 @@ namespace ArchiSteamFarm {
}
}
// By default we're operating on normal mode
Mode = EMode.Normal;
Logging.LogToFile = true;
// 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);
// But that can be overriden by arguments
ParseArgs(args);
SteamKit2.DebugLog.AddListener(new Debugging.DebugListener(Path.Combine(DebugDirectory, "debug.txt")));
SteamKit2.DebugLog.Enabled = true;
}
// Parse args
if (args != null) {
ParseArgs(args);
}
// If we ran ASF as a client, we're done by now
if (Mode == EMode.Client) {
return;
Exit();
}
Task.Run(async () => await CheckForUpdate().ConfigureAwait(false)).Wait();
// From now on it's server mode
Logging.Init();
if (!Directory.Exists(ConfigDirectory)) {
Logging.LogGenericError("Config directory doesn't exist!");
@@ -264,28 +485,45 @@ namespace ArchiSteamFarm {
Exit(1);
}
// Before attempting to connect, initialize our list of CMs
Bot.RefreshCMs().Wait();
CheckForUpdate().Wait();
// Before attempting to connect, initialize our list of CMs
Bot.RefreshCMs(GlobalDatabase.CellID).Wait();
bool isRunning = false;
foreach (string botName in Directory.EnumerateFiles(ConfigDirectory, "*.json").Select(Path.GetFileNameWithoutExtension)) {
switch (botName) {
case ASF:
case "example":
case "minimal":
continue;
}
foreach (var configFile in Directory.EnumerateFiles(ConfigDirectory, "*.xml")) {
string botName = Path.GetFileNameWithoutExtension(configFile);
Bot bot = new Bot(botName);
if (!bot.Enabled) {
Logging.LogGenericInfo("Not starting this instance because it's disabled in config file", botName);
if ((bot.BotConfig == null) || !bot.BotConfig.Enabled) {
continue;
}
if (bot.BotConfig.StartOnLaunch) {
isRunning = true;
}
}
// Check if we got any bots running
OnBotShutdown();
if (!isRunning) {
OnBotShutdown();
}
}
private static void Main(string[] args) {
Init(args);
// Wait for signal to shutdown
ShutdownResetEvent.WaitOne();
ShutdownResetEvent.Wait();
// We got a signal to shutdown, consider giving user some time to read the message
Thread.Sleep(5000);
// This is over, cleanup only now
WCF.StopServer();
// We got a signal to shutdown
Exit();
}
}
}

View File

@@ -1,8 +1,7 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// 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("ArchiSteamFarm")]
@@ -10,12 +9,12 @@ 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("")]
// 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
// 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)]
@@ -25,12 +24,12 @@ using System.Runtime.InteropServices;
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// 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.6.0.0")]
[assembly: AssemblyFileVersion("1.6.0.0")]
[assembly: AssemblyVersion("2.0.5.0")]
[assembly: AssemblyFileVersion("2.0.5.0")]

View File

@@ -1,74 +0,0 @@
/*
_ _ _ ____ _ _____
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
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 sealed class SteamTradeOffer {
// 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
}
[JsonProperty(Required = Required.Always)]
internal string tradeofferid { get; set; }
[JsonProperty(Required = Required.Always)]
internal int accountid_other { get; set; }
[JsonProperty(Required = Required.Always)]
internal ETradeOfferState trade_offer_state { get; set; }
[JsonProperty(Required = Required.Always)]
internal List<SteamItem> items_to_give { get; } = new List<SteamItem>();
[JsonProperty(Required = Required.Always)]
internal List<SteamItem> items_to_receive { get; } = new List<SteamItem>();
// 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;
}
}
}
}

View File

@@ -22,9 +22,13 @@
*/
using SteamAuth;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using ArchiSteamFarm.JSON;
namespace ArchiSteamFarm {
internal sealed class Trading {
@@ -34,66 +38,177 @@ namespace ArchiSteamFarm {
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);
var releaseLater = Task.Run(async () => {
await Utilities.SleepAsync(3000).ConfigureAwait(false); // We must add some delay to not get caught by Steam rate limiter
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(nameof(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)));
tradeOffers.RemoveWhere(tradeoffer => tradeoffer.State != Steam.TradeOffer.ETradeOfferState.Active);
tradeOffers.TrimExcess();
if (tradeOffers.Count == 0) {
return;
}
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) {
Logging.LogNullError(nameof(tradeOffer), Bot.BotName);
return;
}
if (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) {
Logging.LogNullError(nameof(tradeOffer), Bot.BotName);
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;
}
// Decline trade if we're giving more count-wise
if (tradeOffer.ItemsToGive.Count > tradeOffer.ItemsToReceive.Count) {
return false;
}
// Decline trade if we're losing anything but steam cards, or if it's non-dupes trade
if (!tradeOffer.IsSteamCardsOnlyTradeForUs() || !tradeOffer.IsPotentiallyDupesTradeForUs()) {
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>(tradeOffer.ItemsToGive.Select(item => 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<ulong, uint> amountMap = new Dictionary<ulong, uint>();
foreach (Steam.Item item in inventory) {
uint amount;
if (amountMap.TryGetValue(item.ClassID, out amount)) {
amountMap[item.ClassID] = amount + item.Amount;
} else {
amountMap[item.ClassID] = item.Amount;
}
}
await Task.WhenAll(tasks).ConfigureAwait(false);
await Bot.AcceptAllConfirmations().ConfigureAwait(false);
}
// Calculate our value of items to give
List<uint> amountsToGive = new List<uint>(tradeOffer.ItemsToGive.Count);
foreach (ulong key in tradeOffer.ItemsToGive.Select(item => item.ClassID)) {
uint amount;
if (!amountMap.TryGetValue(key, out amount)) {
amountsToGive.Add(0);
continue;
}
private async Task ParseTrade(SteamTradeOffer tradeOffer) {
if (tradeOffer == null) {
return;
amountsToGive.Add(amount);
}
ulong tradeID;
if (!ulong.TryParse(tradeOffer.tradeofferid, out tradeID)) {
return;
// Sort it ascending
amountsToGive.Sort();
// Calculate our value of items to receive
List<uint> amountsToReceive = new List<uint>(tradeOffer.ItemsToReceive.Count);
foreach (ulong key in tradeOffer.ItemsToReceive.Select(item => item.ClassID)) {
uint amount;
if (!amountMap.TryGetValue(key, out amount)) {
amountsToReceive.Add(0);
continue;
}
amountsToReceive.Add(amount);
}
if (tradeOffer.items_to_give.Count == 0 || tradeOffer.OtherSteamID64 == Bot.SteamMasterID) {
Logging.LogGenericInfo("Accepting trade: " + tradeID, Bot.BotName);
await Bot.ArchiWebHandler.AcceptTradeOffer(tradeID).ConfigureAwait(false);
} else {
Logging.LogGenericInfo("Ignoring trade: " + tradeID, Bot.BotName);
}
// Sort it ascending
amountsToReceive.Sort();
// Check actual difference
int difference = amountsToGive.Select((t, i) => (int) (t - amountsToReceive[i])).Sum();
// Trade is worth for us if the difference is greater than 0
return difference > 0;
}
}
}

View File

@@ -22,55 +22,53 @@
*/
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace ArchiSteamFarm {
internal static class Utilities {
internal static async Task SleepAsync(int miliseconds) {
await Task.Delay(miliseconds).ConfigureAwait(false);
[SuppressMessage("ReSharper", "UnusedParameter.Global")]
internal static void Forget(this Task task) { }
internal static Task ForEachAsync<T>(this IEnumerable<T> sequence, Func<T, Task> action) {
if (action != null) {
return Task.WhenAll(sequence.Select(action));
}
Logging.LogNullError(nameof(action));
return Task.FromResult(true);
}
internal static ulong OnlyNumbers(string inputString) {
if (string.IsNullOrEmpty(inputString)) {
return 0;
}
string resultString = OnlyNumbersString(inputString);
if (string.IsNullOrEmpty(resultString)) {
return 0;
}
ulong result;
if (!ulong.TryParse(resultString, out result)) {
return 0;
}
return result;
}
internal static string OnlyNumbersString(string text) {
if (string.IsNullOrEmpty(text)) {
internal static string GetCookieValue(this CookieContainer cookieContainer, string url, string name) {
if (string.IsNullOrEmpty(url) || string.IsNullOrEmpty(name)) {
Logging.LogNullError(nameof(url) + " || " + nameof(name));
return null;
}
return Regex.Replace(text, @"[^\d]", "");
Uri uri;
try {
uri = new Uri(url);
} catch (UriFormatException e) {
Logging.LogGenericException(e);
return null;
}
CookieCollection cookies = cookieContainer.GetCookies(uri);
return cookies.Count == 0 ? null : (from Cookie cookie in cookies where cookie.Name.Equals(name) select cookie.Value).FirstOrDefault();
}
internal static uint GetCharCountInString(string s, char c) {
if (string.IsNullOrEmpty(s)) {
return 0;
internal static Task SleepAsync(int miliseconds) {
if (miliseconds >= 0) {
return Task.Delay(miliseconds);
}
uint count = 0;
foreach (char singleChar in s) {
if (singleChar == c) {
count++;
}
}
return count;
Logging.LogNullError(nameof(miliseconds));
return Task.FromResult(true);
}
}
}

View File

@@ -23,6 +23,7 @@
*/
using System;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Channels;
@@ -33,17 +34,26 @@ namespace ArchiSteamFarm {
string HandleCommand(string input);
}
internal class WCF : IWCF {
internal sealed class WCF : IWCF {
private const string URL = "http://localhost:1242/ASF"; // 1242 = 1024 + A(65) + S(83) + F(70)
private static string URL = "http://localhost:1242/ASF";
private ServiceHost ServiceHost;
private Client Client;
internal bool IsServerRunning() {
return ServiceHost != null;
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() => ServiceHost != null;
internal void StartServer() {
if (ServiceHost != null) {
return;
@@ -77,6 +87,11 @@ namespace ArchiSteamFarm {
}
internal string SendCommand(string input) {
if (string.IsNullOrEmpty(input)) {
Logging.LogNullError(nameof(input));
return null;
}
if (Client == null) {
Client = new Client(new BasicHttpBinding(), new EndpointAddress(URL));
}
@@ -86,42 +101,36 @@ namespace ArchiSteamFarm {
public string HandleCommand(string input) {
if (string.IsNullOrEmpty(input)) {
Logging.LogNullError(nameof(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.GetAnyBotName();
Bot bot = Bot.Bots.Values.FirstOrDefault();
if (bot == null) {
return "ERROR: No bots are enabled!";
}
if (string.IsNullOrEmpty(botName)) {
return "ERROR: Invalid botName: " + botName;
if (Program.GlobalConfig.SteamOwnerID == 0) {
return "Refusing to handle request because SteamOwnerID is not set!";
}
Bot bot;
if (!Bot.Bots.TryGetValue(botName, out bot)) {
return "ERROR: Couldn't find any bot named: " + botName;
}
string command = "!" + input;
string output = bot.Response(Program.GlobalConfig.SteamOwnerID, command).Result; // TODO: This should be asynchronous
Logging.LogGenericInfo("Received command: \"" + input + "\"");
string command = '!' + input;
string output = bot.HandleMessage(command).Result; // TODO: This should be asynchronous
Logging.LogGenericInfo("Answered to command: \"" + input + "\" with: \"" + output + "\"");
Logging.LogGenericInfo("Answered to command: " + input + " with: " + output);
return output;
}
}
internal class Client : ClientBase<IWCF>, IWCF {
internal sealed class Client : ClientBase<IWCF>, IWCF {
internal Client(Binding binding, EndpointAddress address) : base(binding, address) { }
public string HandleCommand(string input) {
if (string.IsNullOrEmpty(input)) {
Logging.LogNullError(nameof(input));
return null;
}
try {
return Channel.HandleCommand(input);
} catch (Exception e) {

View File

@@ -23,33 +23,30 @@
*/
using HtmlAgilityPack;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
namespace ArchiSteamFarm {
internal static class WebBrowser {
internal const byte HttpTimeout = 180; // In seconds, how long we can wait for server's response
internal 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
internal const byte MaxIdleTime = 15; // In seconds, how long socket is allowed to stay in CLOSE_WAIT state after there are no connections to it
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;
private static readonly HttpClient HttpClient = new HttpClient(new HttpClientHandler {
UseCookies = false
}) {
Timeout = TimeSpan.FromSeconds(HttpTimeout)
};
internal readonly CookieContainer CookieContainer = new CookieContainer();
private readonly HttpClient HttpClient;
private readonly string Identifier;
internal static void Init() {
// Most web services expect that UserAgent is set, so we declare it globally
// Any request can override that on as-needed basis (see: RequestOptions.FakeUserAgent)
HttpClient.DefaultRequestHeaders.UserAgent.ParseAdd(DefaultUserAgent);
// Set max connection limit from default of 2 to desired value
ServicePointManager.DefaultConnectionLimit = MaxConnections;
@@ -59,68 +56,237 @@ namespace ArchiSteamFarm {
// Don't use Expect100Continue, we're sure about our POSTs, save some TCP packets
ServicePointManager.Expect100Continue = false;
// Reuse ports if possible
// TODO: Mono doesn't support that feature yet
#if !__MonoCS__
// Reuse ports if possible (since .NET 4.6+)
//ServicePointManager.ReusePort = true;
#endif
}
internal static async Task<HttpResponseMessage> UrlGet(string request, Dictionary<string, string> cookies = null, string referer = null) {
if (string.IsNullOrEmpty(request)) {
return null;
internal WebBrowser(string identifier) {
if (string.IsNullOrEmpty(identifier)) {
throw new ArgumentNullException(nameof(identifier));
}
return await UrlRequest(request, HttpMethod.Get, null, cookies, referer).ConfigureAwait(false);
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 static async Task<HttpResponseMessage> UrlPost(string request, Dictionary<string, string> data = null, Dictionary<string, string> cookies = null, string referer = null) {
internal async Task<bool> UrlHeadRetry(string request, string referer = null) {
if (string.IsNullOrEmpty(request)) {
return null;
Logging.LogNullError(nameof(request));
return false;
}
return await UrlRequest(request, HttpMethod.Post, data, cookies, referer).ConfigureAwait(false);
bool result = false;
for (byte i = 0; (i < MaxRetries) && !result; i++) {
result = await UrlHead(request, referer).ConfigureAwait(false);
}
if (result) {
return true;
}
Logging.LogGenericWTF("Request failed even after " + MaxRetries + " tries", Identifier);
return false;
}
internal static async Task<string> UrlGetToContent(string request, Dictionary<string, string> cookies, string referer = null) {
internal async Task<Uri> UrlHeadToUriRetry(string request, string referer = null) {
if (string.IsNullOrEmpty(request)) {
Logging.LogNullError(nameof(request));
return null;
}
HttpResponseMessage httpResponse = await UrlGet(request, cookies, referer).ConfigureAwait(false);
if (httpResponse == null) {
return null;
Uri result = null;
for (byte i = 0; (i < MaxRetries) && (result == null); i++) {
result = await UrlHeadToUri(request, referer).ConfigureAwait(false);
}
HttpContent httpContent = httpResponse.Content;
if (httpContent == null) {
return null;
if (result != null) {
return result;
}
return await httpContent.ReadAsStringAsync().ConfigureAwait(false);
Logging.LogGenericWTF("Request failed even after " + MaxRetries + " tries", Identifier);
return null;
}
internal static async Task<HtmlDocument> UrlGetToHtmlDocument(string request, Dictionary<string, string> cookies = null, string referer = null) {
internal async Task<byte[]> UrlGetToBytesRetry(string request, string referer = null) {
if (string.IsNullOrEmpty(request)) {
Logging.LogNullError(nameof(request));
return null;
}
string content = await UrlGetToContent(request, cookies, referer).ConfigureAwait(false);
byte[] result = null;
for (byte i = 0; (i < MaxRetries) && (result == null); i++) {
result = await UrlGetToBytes(request, referer).ConfigureAwait(false);
}
if (result != null) {
return result;
}
Logging.LogGenericWTF("Request failed even after " + MaxRetries + " tries", Identifier);
return null;
}
internal async Task<string> UrlGetToContentRetry(string request, string referer = null) {
if (string.IsNullOrEmpty(request)) {
Logging.LogNullError(nameof(request));
return null;
}
string result = null;
for (byte i = 0; (i < MaxRetries) && string.IsNullOrEmpty(result); i++) {
result = await UrlGetToContent(request, referer).ConfigureAwait(false);
}
if (!string.IsNullOrEmpty(result)) {
return result;
}
Logging.LogGenericWTF("Request failed even after " + MaxRetries + " tries", Identifier);
return null;
}
internal async Task<HtmlDocument> UrlGetToHtmlDocumentRetry(string request, string referer = null) {
if (string.IsNullOrEmpty(request)) {
Logging.LogNullError(nameof(request));
return null;
}
HtmlDocument result = null;
for (byte i = 0; (i < MaxRetries) && (result == null); i++) {
result = await UrlGetToHtmlDocument(request, referer).ConfigureAwait(false);
}
if (result != null) {
return result;
}
Logging.LogGenericWTF("Request failed even after " + MaxRetries + " tries", Identifier);
return null;
}
internal async Task<JObject> UrlGetToJObjectRetry(string request, string referer = null) {
if (string.IsNullOrEmpty(request)) {
Logging.LogNullError(nameof(request));
return null;
}
JObject result = null;
for (byte i = 0; (i < MaxRetries) && (result == null); i++) {
result = await UrlGetToJObject(request, referer).ConfigureAwait(false);
}
if (result != null) {
return result;
}
Logging.LogGenericWTF("Request failed even after " + MaxRetries + " tries", Identifier);
return null;
}
internal async Task<XmlDocument> UrlGetToXMLRetry(string request, string referer = null) {
if (string.IsNullOrEmpty(request)) {
Logging.LogNullError(nameof(request));
return null;
}
XmlDocument result = null;
for (byte i = 0; (i < MaxRetries) && (result == null); i++) {
result = await UrlGetToXML(request, referer).ConfigureAwait(false);
}
if (result != null) {
return result;
}
Logging.LogGenericWTF("Request failed even after " + MaxRetries + " tries", Identifier);
return null;
}
internal async Task<bool> UrlPostRetry(string request, Dictionary<string, string> data = null, string referer = null) {
if (string.IsNullOrEmpty(request)) {
Logging.LogNullError(nameof(request));
return false;
}
bool result = false;
for (byte i = 0; (i < MaxRetries) && !result; i++) {
result = await UrlPost(request, data, referer).ConfigureAwait(false);
}
if (result) {
return true;
}
Logging.LogGenericWTF("Request failed even after " + MaxRetries + " tries", Identifier);
return false;
}
private async Task<byte[]> UrlGetToBytes(string request, string referer = null) {
if (string.IsNullOrEmpty(request)) {
Logging.LogNullError(nameof(request));
return null;
}
using (HttpResponseMessage httpResponse = await UrlGetToResponse(request, referer).ConfigureAwait(false)) {
if (httpResponse == null) {
return null;
}
return await httpResponse.Content.ReadAsByteArrayAsync().ConfigureAwait(false);
}
}
private async Task<string> UrlGetToContent(string request, string referer = null) {
if (string.IsNullOrEmpty(request)) {
Logging.LogNullError(nameof(request));
return null;
}
using (HttpResponseMessage httpResponse = await UrlGetToResponse(request, referer).ConfigureAwait(false)) {
if (httpResponse == null) {
return null;
}
return await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
}
}
private async Task<HtmlDocument> UrlGetToHtmlDocument(string request, string referer = null) {
if (string.IsNullOrEmpty(request)) {
Logging.LogNullError(nameof(request));
return null;
}
string content = await UrlGetToContent(request, referer).ConfigureAwait(false);
if (string.IsNullOrEmpty(content)) {
return null;
}
content = WebUtility.HtmlDecode(content);
HtmlDocument htmlDocument = new HtmlDocument();
htmlDocument.LoadHtml(content);
htmlDocument.LoadHtml(WebUtility.HtmlDecode(content));
return htmlDocument;
}
internal static async Task<JObject> UrlGetToJObject(string request, Dictionary<string, string> cookies = null, string referer = null) {
private async Task<JObject> UrlGetToJObject(string request, string referer = null) {
if (string.IsNullOrEmpty(request)) {
Logging.LogNullError(nameof(request));
return null;
}
string content = await UrlGetToContent(request, cookies, referer).ConfigureAwait(false);
string content = await UrlGetToContent(request, referer).ConfigureAwait(false);
if (string.IsNullOrEmpty(content)) {
return null;
}
@@ -129,39 +295,119 @@ namespace ArchiSteamFarm {
try {
jObject = JObject.Parse(content);
} catch (Exception e) {
Logging.LogGenericException(e);
} catch (JsonException e) {
Logging.LogGenericException(e, Identifier);
return null;
}
return jObject;
}
private static async Task<HttpResponseMessage> UrlRequest(string request, HttpMethod httpMethod, Dictionary<string, string> data = null, Dictionary<string, string> cookies = null, string referer = null) {
if (string.IsNullOrEmpty(request) || httpMethod == null) {
private async Task<HttpResponseMessage> UrlGetToResponse(string request, string referer = null) {
if (!string.IsNullOrEmpty(request)) {
return await UrlRequest(request, HttpMethod.Get, null, referer).ConfigureAwait(false);
}
Logging.LogNullError(nameof(request));
return null;
}
private async Task<XmlDocument> UrlGetToXML(string request, string referer = null) {
if (string.IsNullOrEmpty(request)) {
Logging.LogNullError(nameof(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;
}
private async Task<bool> UrlHead(string request, string referer = null) {
if (string.IsNullOrEmpty(request)) {
Logging.LogNullError(nameof(request));
return false;
}
using (HttpResponseMessage response = await UrlHeadToResponse(request, referer).ConfigureAwait(false)) {
return response != null;
}
}
private async Task<HttpResponseMessage> UrlHeadToResponse(string request, string referer = null) {
if (!string.IsNullOrEmpty(request)) {
return await UrlRequest(request, HttpMethod.Head, null, referer).ConfigureAwait(false);
}
Logging.LogNullError(nameof(request));
return null;
}
private async Task<Uri> UrlHeadToUri(string request, string referer = null) {
if (string.IsNullOrEmpty(request)) {
Logging.LogNullError(nameof(request));
return null;
}
using (HttpResponseMessage response = await UrlHeadToResponse(request, referer).ConfigureAwait(false)) {
return response == null ? null : response.RequestMessage.RequestUri;
}
}
private async Task<bool> UrlPost(string request, Dictionary<string, string> data = null, string referer = null) {
if (string.IsNullOrEmpty(request)) {
Logging.LogNullError(nameof(request));
return false;
}
using (HttpResponseMessage response = await UrlPostToResponse(request, data, referer).ConfigureAwait(false)) {
return response != null;
}
}
private async Task<HttpResponseMessage> UrlPostToResponse(string request, Dictionary<string, string> data = null, string referer = null) {
if (!string.IsNullOrEmpty(request)) {
return await UrlRequest(request, HttpMethod.Post, data, referer).ConfigureAwait(false);
}
Logging.LogNullError(nameof(request));
return null;
}
private async Task<HttpResponseMessage> UrlRequest(string request, HttpMethod httpMethod, Dictionary<string, string> data = null, string referer = null) {
if (string.IsNullOrEmpty(request) || (httpMethod == null)) {
Logging.LogNullError(nameof(request) + " || " + nameof(httpMethod));
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) {
if ((data != null) && (data.Count > 0)) {
try {
requestMessage.Content = new FormUrlEncodedContent(data);
} catch (UriFormatException e) {
Logging.LogGenericException(e);
Logging.LogGenericException(e, Identifier);
return null;
}
}
if (cookies != null && cookies.Count > 0) {
StringBuilder cookieHeader = new StringBuilder();
foreach (KeyValuePair<string, string> cookie in cookies) {
cookieHeader.Append(cookie.Key + "=" + cookie.Value + ";");
}
requestMessage.Headers.Add("Cookie", cookieHeader.ToString());
}
if (referer != null) {
if (!string.IsNullOrEmpty(referer)) {
requestMessage.Headers.Referrer = new Uri(referer);
}
@@ -172,11 +418,22 @@ namespace ArchiSteamFarm {
}
}
if (responseMessage == null || !responseMessage.IsSuccessStatusCode) {
if (responseMessage == null) {
return null;
}
return responseMessage;
if (responseMessage.IsSuccessStatusCode) {
return responseMessage;
}
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;
}
}
}

View File

@@ -0,0 +1,28 @@
{
"Debug": false,
"Headless": false,
"AutoUpdates": true,
"AutoRestart": true,
"UpdateChannel": 1,
"SteamProtocol": 6,
"SteamOwnerID": 0,
"MaxFarmingTime": 10,
"IdleFarmingPeriod": 3,
"FarmingDelay": 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
]
}

View 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
]
}

View File

@@ -1,153 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<!-- This is full-fledged example config, you may be also interested in minimal.xml for bare minimum one -->
<!-- This config includes all user-switchable properties that you may want to change on per-bot basis -->
<!-- Default values used in config match default ASF values when given config property is not found -->
<!-- In other words, if given property is not defined, ASF will assume default pre-programmed value -->
<!-- Notice, if you use special characters reserved for XML, you should escape them -->
<!-- Escape table: [& - &amp;] | [" - &quot;] | [' - &apos;] | [< - &lt;] | [> - &gt;] -->
<!-- So e.g. if your SteamPassword is "foo&" you should write value="foo&amp;" -->
<!-- Type of the property is a tip for you that defines what values you can use -->
<!-- bool - Boolean value that can be only "true" or "false" -->
<!-- string - Any sequence of characters, unless stated otherwise (keep in mind escape table above), with special treatment of "null" value -->
<!-- byte - 8-bit unsigned integer, used mostly for small numbers (0-255) -->
<!-- uint - 32-bit unsigned integer, used mostly for steam appID -->
<!-- ulong - 64-bit unsigned (long) integer, used mostly for representing steamID64 -->
<!-- HashSet(uint) - Comma-separated list of unique 32-bit unsigned integers -->
<!-- Master switch to turn account on and off, set to "true" after you're done -->
<!-- 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 -->
<!-- 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 -->
<!-- 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 -->
<!-- You can use "null" if you wish to preserve your actual nickname, and this is what you want most likely -->
<SteamNickname type="string" value="null"/>
<!-- This is your bot's API key, get one at https://steamcommunity.com/dev/apikey while logged in as a bot, domain doesn't matter -->
<!-- Remember that each account should have unique API key generated through above link, wrong API key will make all API-based functionalities to fail -->
<!-- API key is useless for primary accounts, as they're not using trading feature, you can leave it at "null" if you're configuring a primary account -->
<!-- When at "null", it will disable all ASF API-based functionalities such as trading -->
<SteamApiKey type="string" value="null"/>
<!-- This is your parental PIN if you use steam parental functionality -->
<!-- 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, and this is probably what you want -->
<SteamParentalPIN type="string" value="0"/>
<!-- This is steamID64 of the bot-master - you, for example "76561198006963719" -->
<!-- You can get one e.g. by logging in to http://steamrep.com/ -->
<!-- If you're configuring primary account, you can safely leave it at "0", as you're master yourself -->
<!-- When at "0", bot won't accept any commands, including steam cd-keys or trades" -->
<SteamMasterID type="ulong" value="0"/>
<!-- This is steamID64 of the master clan (group). If defined, bot will join the group and the chat automatically after logging in -->
<!-- The easiest way to get one is to check groups on your profile: http://steamcommunity.com/my/profile" -->
<!-- Then copying the URL of "Leave group" link, it will look like this: javascript:leaveGroupPrompt('103582791440160998','Archi\'s SC Farm') -->
<!-- The steamID64 we're looking for is there, in above example: "103582791440160998" -->
<!-- Remember that joining a group and the chat requires having non-restricted access to steam community, which is not always the case with alts -->
<!-- If you don't have your own farming group, most likely you don't want to change it, 0 means there is no master group defined -->
<SteamMasterClanID type="ulong" value="0"/>
<!-- This switch defines if bot should automatically start when ASF process is launched -->
<!-- Some people might want to avoid that, as they may prefer to issue "!start" command themselves -->
<!-- Most likely you want to keep this switch enabled, unless you have a reason -->
<StartOnLaunch type="bool" value="true"/>
<!-- This switch defines if you want to use built-in ASF two-factor-authentication (ASF 2FA) for this account -->
<!-- The one and only purpose for this function is automating steam logins and steam trades, so you won't need to enter 2FA codes all the time -->
<!-- This however defeats the whole purpose of two-factor-auth, and should be used on alt accounts ONLY -->
<!-- You should read full documentation, along with explanation here: https://github.com/JustArchi/ArchiSteamFarm/wiki/Escrow -->
<!-- Personally I suggest switching this to "true" ONLY for alts -->
<!-- WARNING, this option can potentially LOCK OUT YOUR ACCOUNT! If you don't fully understand this feature, DON'T USE IT! -->
<UseAsfAsMobileAuthenticator type="bool" value="false"/>
<!-- This switch defines if the account has card drops restricted -->
<!-- Restricted card drops means that the account doesn't receive any steam cards until it plays the game for at least 2 hours -->
<!-- There is no magical way to detect it by ASF, I made this option config-based switch -->
<!-- Based on our observations, it looks like most accounts that never issued a refund have non-restricted card drops, and this is what default value assumes -->
<!-- However, if you noticed that your cards never drop until you hit 2 hours played mark, consider setting it as "true" -->
<!-- Guessing "wrong" won't have any real consequences, ASF will just work in non-optimal way, and everybody wants to drop cards fast, right? -->
<!-- Based on this parameter, ASF will try to choose the most optimal cards farming algorithm for this account -->
<CardDropsRestricted type="bool" value="false"/>
<!-- This switch defines if the account should stay as "Offline" after logging in to Steam. This has several advantages and disadvantages -->
<!-- Personally I find it extremely useful for primary accounts, as your status will remain "Online" and not "In-Game" when ASF is farming -->
<!-- Thanks to that, your friends will never ask you again if you're playing or farming the game. Your status will now nicely reflect that -->
<!-- However, it's less useful for alt accounts, as you won't be able to interact with bots and check what they're doing - they'll appear as "Offline" -->
<!-- Personally I suggest switching this to "true" for primary accounts, and leaving it at "false" for alt accounts -->
<!-- Bot won't be able to receive and answer to your commands, unless you set "HandleOfflineMessages" below to "true" as well -->
<FarmOffline type="bool" value="false"/>
<!-- This switch defines if bot should handle offline messages when it sees them -->
<!-- Basically it should be used only when "FarmOffline" property above is true, so bot can handle offline messages -->
<!-- Reading offline messages will also mark them as received, therefore it should not be used if you want to keep them for later -->
<!-- Personally I suggest keeping this on "false" for primary accounts, and considering switching to "true" for alts, if "FarmOffline" above is set to true as well -->
<HandleOfflineMessages type="bool" value="false"/>
<!-- This switch defines if bot should try to forward key to the other bot in case of failing to redeem on his own account -->
<!-- This can be useful if you want to send key to your farm and you don't care which account redeems it -->
<!-- When "true", bot will try redeeming "AlreadyOwned", "BaseGameRequired", "OnCooldown" and "RegionLocked" keys on other available accounts -->
<!-- By default this option is disabled, as not everyone might want to redeem keys on other configured accounts -->
<!-- WARNING: Remember that Steam issues temporary ban on too many failed key activations (OnCooldown status) -->
<!-- Therefore, be careful when it comes to multiple keys activations, this option is supposed to help you, not make you lazy -->
<ForwardKeysToOtherBots type="bool" value="false"/>
<!-- This switch defines bot's behaviour on getting multiple keys -->
<!-- When "false", bot will try to redeem all keys on it's own account, when "true", keys will be distributed on "one bot, one key" basis -->
<!-- Keep in mind that "ForwardKeysToOtherBots" property defined above affects this switch as well -->
<!-- When "ForwardKeysToOtherBots" is "true", bot will try redeeming "AlreadyOwned", "BaseGameRequired", "OnCooldown" and "RegionLocked" keys on other available accounts -->
<!-- If "ForwardKeysToOtherBots" is set to "false", bot will keep unused keys for itself -->
<!-- By default this option is disabled, as not everyone might want to alter the multiple keys redeeming behaviour -->
<!-- Switch this to "true" only if you prefer to use "one bot, one key" behaviour, instead of default one, which is to redeem all keys only on one account -->
<DistributeKeys type="bool" value="false"/>
<!-- This switch defines if bot should disconnect once farming is finished -->
<!-- Keep in mind that when no bots are active, ASF will shutdown as well -->
<!-- You may want to disconnect the bot after he's done, if that's the case, set below to "true" -->
<!-- However, you may instead want to keep it online for the whole time, in this case, leave it at "false" -->
<!-- Even when bot is not farming anything, he'll keep checking badges from time to time, if there is anything new to farm -->
<!-- Personally I suggest leaving it at "false", unless you have a reason to close the process after all bots finished farming -->
<ShutdownOnFarmingFinished type="bool" value="false"/>
<!-- This switch defines if bot should send you all the items it farmed after farming is finished -->
<!-- Remember that in order to use this feature, SteamMasterID must be defined above -->
<!-- If SteamMasterID is not a friend of this bot, SteamTradeToken will also be needed to set below -->
<SendOnFarmingFinished type="bool" value="false"/>
<!-- This is a SteamTradeToken of SteamMasterID, which is required if bot doesn't have SteamMasterID on friend list -->
<!-- You can get the token here: http://steamcommunity.com/id/me/tradeoffers/privacy while being logged in as SteamMasterID -->
<!-- The token has 8 characters and is written in the last part of trade URL link, starting after "&token=" -->
<SteamTradeToken type="string" value="null"/>
<!-- This switch defines if bot should send you trade offer with all farmed cards on regular basis -->
<!-- This can become useful if you have lots of games to farm and you don't want to wait for all of them to be farmed -->
<!-- However, if you have many bots running, it may become a bit spammy/intrusive, so it's not enabled by default -->
<!-- Remember that there is also "SendOnFarmingFinished" switch above, which is far less intrusive and can be used instead -->
<!-- This property defines how often bot should send you a trade offer, in hours -->
<!-- For example, setting this to "24" will result in a trade offer being sent once per day (24 hours) -->
<!-- Default value of "0" disables that feature -->
<SendTradePeriod type="byte" value="0"/>
<!-- This is comma-separated list of IDs that should not be considered for farming -->
<!-- Default value includes appIDs that are wrongly appearing on the profile, e.g. Summer Sale, Winter Sale or Monster Summer Game -->
<!-- In addition to blacklist defined here, ASF also has global blacklist, which is being updated on as-needed basis, so you don't need to update this entry -->
<!-- You probably don't want to change anything here -->
<Blacklist type="HashSet(uint)" value="303700,335590,368020,425280"/>
<!-- This switch enables statistics for me - bot will join Archi's SC Farm group and chat on steam -->
<!-- Consider leaving it at "true", this way I can check how many running bots are active -->
<!-- Such information directly affects my willings to work on the project, as I can see how many users are actually using it -->
<!-- So if you want to see new versions coming up, bugs being fixed, and new features getting implemented, consider leaving it at "true" -->
<!-- You can find the group, along with all the statistics here: http://steamcommunity.com/groups/ascfarm -->
<Statistics type="bool" value="true"/>
</configuration>

View File

@@ -0,0 +1,5 @@
{
"Enabled": false,
"SteamLogin": null,
"SteamPassword": null
}

View File

@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<!-- This is minimalistic config "just to make ASF work". For full-fledged config, please take a look at example.xml -->
<!-- Full-fledged config includes some neat features such as offline farming or cards farming algorithm selection -->
<!-- ASF will use default values for missing properties, check example.xml to see all of them -->
<Enabled type="bool" value="false"/>
<SteamLogin type="string" value="null"/>
<SteamPassword type="string" value="null"/>
</configuration>

View File

@@ -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.2" targetFramework="net452" />
<package id="Newtonsoft.Json" version="9.0.1-beta1" targetFramework="net451" />
<package id="protobuf-net" version="2.0.0.668" targetFramework="net45" />
<package id="SteamKit2" version="1.7.0" targetFramework="net452" />
</packages>

View 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 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(nameof(filePath));
}
FilePath = filePath;
}
internal void Save() {
lock (FilePath) {
try {
File.WriteAllText(FilePath, JsonConvert.SerializeObject(this, Formatting.Indented));
} catch (Exception e) {
Logging.LogGenericException(e);
}
}
}
internal 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 void Rename(string botName) {
if (string.IsNullOrEmpty(botName)) {
Logging.LogNullError(nameof(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");
}
}
}
}

View 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>

View File

@@ -0,0 +1,147 @@
/*
_ _ _ ____ _ _____
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
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.Diagnostics.CodeAnalysis;
using System.IO;
namespace ConfigGenerator {
[SuppressMessage("ReSharper", "AutoPropertyCanBeMadeGetOnly.Global"), SuppressMessage("ReSharper", "CollectionNeverQueried.Global"), SuppressMessage("ReSharper", "MemberCanBePrivate.Global"), SuppressMessage("ReSharper", "UnusedMember.Global")]
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)) {
Logging.LogNullError(nameof(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;
}
[SuppressMessage("ReSharper", "UnusedMember.Local")]
private BotConfig() { }
private BotConfig(string filePath) : base(filePath) {
if (string.IsNullOrEmpty(filePath)) {
throw new ArgumentNullException(nameof(filePath));
}
GamesPlayedWhileIdle.Add(0);
Save();
}
}
}

View File

@@ -0,0 +1,123 @@
<?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=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.9.0.1-beta1\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>
<DesignTime>True</DesignTime>
</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 --llvm --server -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>

View File

@@ -22,34 +22,27 @@
*/
using Newtonsoft.Json;
using System;
using System.IO;
using System.Windows.Forms;
namespace ArchiSteamFarm {
internal sealed class SteamItem {
// REF: https://developer.valvesoftware.com/wiki/Steam_Web_API/IEconService#CEcon_Asset
namespace ConfigGenerator {
internal sealed class ConfigPage : TabPage {
internal readonly ASFConfig ASFConfig;
[JsonProperty(Required = Required.Always)]
internal string appid { get; set; }
internal ConfigPage(ASFConfig config) {
if (config == null) {
throw new ArgumentNullException(nameof(config));
}
[JsonProperty(Required = Required.Always)]
internal string contextid { get; set; }
ASFConfig = config;
[JsonProperty(Required = Required.Always)]
internal string assetid { get; set; }
RefreshText();
[JsonProperty]
internal string id {
get { return assetid; }
set { assetid = value; }
EnhancedPropertyGrid enhancedPropertyGrid = new EnhancedPropertyGrid(config);
Controls.Add(enhancedPropertyGrid);
}
[JsonProperty(Required = Required.Always)]
internal string classid { get; set; }
[JsonProperty(Required = Required.Always)]
internal string instanceid { get; set; }
[JsonProperty(Required = Required.Always)]
internal string amount { get; set; }
internal void RefreshText() => Text = Path.GetFileNameWithoutExtension(ASFConfig.FilePath);
}
}

View 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>

View File

@@ -22,20 +22,16 @@
*/
using Newtonsoft.Json;
using System.Diagnostics.CodeAnalysis;
namespace ArchiSteamFarm {
internal sealed class SteamTradeOfferRequest {
[JsonProperty(Required = Required.Always)]
internal bool newversion { get; } = true;
[JsonProperty(Required = Required.Always)]
internal int version { get; } = 2;
[JsonProperty(Required = Required.Always)]
internal SteamItemList me { get; } = new SteamItemList();
[JsonProperty(Required = Required.Always)]
internal SteamItemList them { get; } = new SteamItemList();
namespace ConfigGenerator {
internal static class Debugging {
#if DEBUG
[SuppressMessage("ReSharper", "ConvertToConstant.Global")]
internal static readonly bool IsDebugBuild = true;
#else
[SuppressMessage("ReSharper", "ConvertToConstant.Global")]
internal static readonly bool IsDebugBuild = false;
#endif
}
}

View 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 System;
using System.Drawing;
using System.Windows.Forms;
using ConfigGenerator.Properties;
namespace ConfigGenerator {
internal static class DialogBox {
internal static DialogResult InputBox(string title, string promptText, out string value) {
if (string.IsNullOrEmpty(title) || string.IsNullOrEmpty(promptText)) {
Logging.LogNullError(nameof(title) + " || " + nameof(promptText));
value = null;
return DialogResult.Abort;
}
TextBox textBox = new TextBox {
Anchor = AnchorStyles.Right,
Bounds = new Rectangle(12, 36, 372, 20),
Width = 1000
};
Button buttonOk = new Button {
Anchor = AnchorStyles.Bottom | AnchorStyles.Right,
Bounds = new Rectangle(228, 72, 75, 23),
DialogResult = DialogResult.OK,
Text = Resources.OK
};
Button buttonCancel = new Button {
Anchor = AnchorStyles.Bottom | AnchorStyles.Right,
Bounds = new Rectangle(309, 72, 75, 23),
DialogResult = DialogResult.Cancel,
Text = Resources.Cancel
};
Label label = new Label {
AutoSize = true,
Bounds = new Rectangle(9, 20, 372, 13),
Text = promptText
};
Form form = new Form {
AcceptButton = buttonOk,
CancelButton = buttonCancel,
ClientSize = new Size(Math.Max(300, label.Right + 10), 107),
Controls = { label, textBox, buttonOk, buttonCancel },
FormBorderStyle = FormBorderStyle.FixedDialog,
MinimizeBox = false,
MaximizeBox = false,
StartPosition = FormStartPosition.CenterScreen,
Text = title
};
DialogResult dialogResult = form.ShowDialog();
value = textBox.Text;
return dialogResult;
}
internal static DialogResult YesNoBox(string title, string promptText) {
if (string.IsNullOrEmpty(title) || string.IsNullOrEmpty(promptText)) {
Logging.LogNullError(nameof(title) + " || " + nameof(promptText));
return DialogResult.Abort;
}
Button buttonYes = new Button {
Anchor = AnchorStyles.Bottom | AnchorStyles.Right,
Bounds = new Rectangle(228, 72, 75, 23),
DialogResult = DialogResult.Yes,
Text = Resources.Yes
};
Button buttonNo = new Button {
Anchor = AnchorStyles.Bottom | AnchorStyles.Right,
Bounds = new Rectangle(309, 72, 75, 23),
DialogResult = DialogResult.No,
Text = Resources.No
};
Label label = new Label {
AutoSize = true,
Bounds = new Rectangle(9, 20, 372, 13),
Text = promptText
};
Form form = new Form {
AcceptButton = buttonYes,
CancelButton = buttonNo,
ClientSize = new Size(Math.Max(300, label.Right + 10), 107),
Controls = { label, buttonYes, buttonNo },
FormBorderStyle = FormBorderStyle.FixedDialog,
MinimizeBox = false,
MaximizeBox = false,
StartPosition = FormStartPosition.CenterScreen,
Text = title
};
DialogResult dialogResult = form.ShowDialog();
return dialogResult;
}
}
}

View File

@@ -0,0 +1,88 @@
/*
_ _ _ ____ _ _____
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
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(nameof(config));
}
ASFConfig = config;
SelectedObject = config;
Anchor = AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top;
Dock = DockStyle.Fill;
HelpVisible = false;
ToolbarVisible = false;
}
protected override void OnPropertyValueChanged(PropertyValueChangedEventArgs args) {
if (args == null) {
Logging.LogNullError(nameof(args));
return;
}
base.OnPropertyValueChanged(args);
ASFConfig.Save();
BotConfig botConfig = ASFConfig as BotConfig;
if (botConfig != null) {
if (!botConfig.Enabled) {
return;
}
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) {
return;
}
if (globalConfig.SteamOwnerID != 0) {
Tutorial.OnAction(Tutorial.EPhase.GlobalConfigReady);
}
}
protected override void OnGotFocus(EventArgs args) {
if (args == null) {
Logging.LogNullError(nameof(args));
return;
}
base.OnGotFocus(args);
ASFConfig.Save();
}
}
}

View File

@@ -0,0 +1,187 @@
/*
_ _ _ ____ _ _____
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
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.Diagnostics.CodeAnalysis;
using System.IO;
using System.Net.Sockets;
namespace ConfigGenerator {
[SuppressMessage("ReSharper", "AutoPropertyCanBeMadeGetOnly.Global"), SuppressMessage("ReSharper", "CollectionNeverQueried.Global"), SuppressMessage("ReSharper", "MemberCanBePrivate.Global"), SuppressMessage("ReSharper", "UnusedMember.Global")]
internal sealed class GlobalConfig : ASFConfig {
[SuppressMessage("ReSharper", "UnusedMember.Global")]
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
private 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 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;
[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)) {
Logging.LogNullError(nameof(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) {
return globalConfig;
}
Logging.LogGenericWarning("Configured WCFPort is invalid: " + globalConfig.WCFPort + ". Value of " + DefaultWCFPort + " will be used instead");
globalConfig.WCFPort = DefaultWCFPort;
return globalConfig;
}
[SuppressMessage("ReSharper", "UnusedMember.Local")]
private GlobalConfig() { }
private GlobalConfig(string filePath) : base(filePath) {
if (string.IsNullOrEmpty(filePath)) {
throw new ArgumentNullException(nameof(filePath));
}
Blacklist.AddRange(GlobalBlacklist);
Save();
}
}
}

100
ConfigGenerator/Logging.cs Normal file
View File

@@ -0,0 +1,100 @@
/*
_ _ _ ____ _ _____
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
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.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Windows.Forms;
using ConfigGenerator.Properties;
namespace ConfigGenerator {
internal static class Logging {
internal static void LogGenericInfoWithoutStacktrace(string message) {
if (string.IsNullOrEmpty(message)) {
LogNullError(nameof(message));
return;
}
MessageBox.Show(message, Resources.Information, MessageBoxButtons.OK, MessageBoxIcon.Information);
}
internal static void LogGenericErrorWithoutStacktrace(string message) {
if (string.IsNullOrEmpty(message)) {
LogNullError(nameof(message));
return;
}
MessageBox.Show(message, Resources.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
internal static void LogGenericException(Exception exception, [CallerMemberName] string previousMethodName = null) {
while (true) {
if (exception == null) {
LogNullError(nameof(exception));
return;
}
MessageBox.Show(previousMethodName + @"() " + exception.Message + Environment.NewLine + exception.StackTrace, Resources.Exception, MessageBoxButtons.OK, MessageBoxIcon.Error);
if (exception.InnerException != null) {
exception = exception.InnerException;
continue;
}
break;
}
}
internal static void LogGenericWarning(string message, [CallerMemberName] string previousMethodName = null) {
if (string.IsNullOrEmpty(message)) {
LogNullError(nameof(message));
return;
}
MessageBox.Show(previousMethodName + @"() " + message, Resources.Warning, MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
[SuppressMessage("ReSharper", "ExplicitCallerInfoArgument")]
internal static void LogNullError(string nullObjectName, [CallerMemberName] string previousMethodName = null) {
while (true) {
if (string.IsNullOrEmpty(nullObjectName)) {
nullObjectName = nameof(nullObjectName);
continue;
}
LogGenericError(nullObjectName + " is null!", previousMethodName);
break;
}
}
private static void LogGenericError(string message, [CallerMemberName] string previousMethodName = null) {
if (string.IsNullOrEmpty(message)) {
LogNullError(nameof(message));
return;
}
LogGenericErrorWithoutStacktrace(previousMethodName + @"() " + message);
}
}
}

78
ConfigGenerator/MainForm.Designer.cs generated Normal file
View 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;
}
}

203
ConfigGenerator/MainForm.cs Normal file
View File

@@ -0,0 +1,203 @@
/*
_ _ _ ____ _ _____
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
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.Linq;
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 args) {
if ((sender == null) || (args == null)) {
Logging.LogNullError(nameof(sender) + " || " + nameof(args));
return;
}
ASFTab = new ConfigPage(GlobalConfig.Load(Path.Combine(Program.ConfigDirectory, Program.GlobalConfigFile)));
MainTab.TabPages.Add(ASFTab);
foreach (string 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 args) {
if ((sender == null) || (args == null)) {
Logging.LogNullError(nameof(sender) + " || " + nameof(args));
return;
}
if (args.TabPage == RemoveTab) {
ConfigPage configPage = OldTab as ConfigPage;
if (configPage == null) {
MainTab.SelectedIndex = -1;
return;
}
if (configPage == ASFTab) {
MainTab.SelectedTab = ASFTab;
Logging.LogGenericErrorWithoutStacktrace("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 (args.TabPage == RenameTab) {
ConfigPage configPage = OldTab as ConfigPage;
if (configPage == null) {
MainTab.SelectedIndex = -1;
return;
}
if (configPage == ASFTab) {
MainTab.SelectedTab = ASFTab;
Logging.LogGenericErrorWithoutStacktrace("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.LogGenericErrorWithoutStacktrace("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 (args.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.LogGenericErrorWithoutStacktrace("Your bot name is empty!");
return;
}
// Get rid of any potential whitespaces in bot name
input = Regex.Replace(input, @"\s+", "");
if (ASFConfig.ASFConfigs.Select(config => Path.GetFileNameWithoutExtension(config.FilePath)).Any(fileNameWithoutExtension => (fileNameWithoutExtension == null) || fileNameWithoutExtension.Equals(input))) {
Logging.LogGenericErrorWithoutStacktrace("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 (args.TabPage == ASFTab) {
Tutorial.OnAction(Tutorial.EPhase.GlobalConfigOpened);
}
}
private void MainTab_Deselecting(object sender, TabControlCancelEventArgs args) {
if ((sender == null) || (args == null)) {
Logging.LogNullError(nameof(sender) + " || " + nameof(args));
return;
}
OldTab = args.TabPage;
}
private void MainForm_Shown(object sender, EventArgs args) {
if ((sender == null) || (args == null)) {
Logging.LogNullError(nameof(sender) + " || " + nameof(args));
return;
}
Tutorial.OnAction(Tutorial.EPhase.Shown);
}
private void MainForm_HelpButtonClicked(object sender, CancelEventArgs args) {
if ((sender == null) || (args == null)) {
Logging.LogNullError(nameof(sender) + " || " + nameof(args));
return;
}
args.Cancel = true;
Tutorial.OnAction(Tutorial.EPhase.Help);
Process.Start("https://github.com/JustArchi/ArchiSteamFarm/wiki/Configuration");
Tutorial.OnAction(Tutorial.EPhase.HelpFinished);
}
}
}

File diff suppressed because it is too large Load Diff

104
ConfigGenerator/Program.cs Normal file
View File

@@ -0,0 +1,104 @@
/*
_ _ _ ____ _ _____
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
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)) {
continue;
}
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)) {
return;
}
Logging.LogGenericErrorWithoutStacktrace("Config directory could not be found!");
Environment.Exit(1);
}
private static void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs args) {
if ((sender == null) || (args == null) || (args.ExceptionObject == null)) {
Logging.LogNullError(nameof(sender) + " || " + nameof(args) + " || " + nameof(args.ExceptionObject));
return;
}
Logging.LogGenericException((Exception) args.ExceptionObject);
}
private static void UnobservedTaskExceptionHandler(object sender, UnobservedTaskExceptionEventArgs args) {
if ((sender == null) || (args == null) || (args.Exception == null)) {
Logging.LogNullError(nameof(sender) + " || " + nameof(args) + " || " + nameof(args.Exception));
return;
}
Logging.LogGenericException(args.Exception);
}
}
}

View File

@@ -0,0 +1,35 @@
using System.Reflection;
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")]

View File

@@ -0,0 +1,135 @@
//------------------------------------------------------------------------------
// <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 {
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("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;
}
}
/// <summary>
/// Looks up a localized string similar to Cancel.
/// </summary>
internal static string Cancel {
get {
return ResourceManager.GetString("Cancel", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Error.
/// </summary>
internal static string Error {
get {
return ResourceManager.GetString("Error", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Exception.
/// </summary>
internal static string Exception {
get {
return ResourceManager.GetString("Exception", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Information.
/// </summary>
internal static string Information {
get {
return ResourceManager.GetString("Information", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to No.
/// </summary>
internal static string No {
get {
return ResourceManager.GetString("No", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to OK.
/// </summary>
internal static string OK {
get {
return ResourceManager.GetString("OK", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Warning.
/// </summary>
internal static string Warning {
get {
return ResourceManager.GetString("Warning", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Yes.
/// </summary>
internal static string Yes {
get {
return ResourceManager.GetString("Yes", resourceCulture);
}
}
}
}

View File

@@ -0,0 +1,141 @@
<?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>
<data name="Information" xml:space="preserve">
<value>Information</value>
</data>
<data name="Error" xml:space="preserve">
<value>Error</value>
</data>
<data name="Exception" xml:space="preserve">
<value>Exception</value>
</data>
<data name="Warning" xml:space="preserve">
<value>Warning</value>
</data>
<data name="OK" xml:space="preserve">
<value>OK</value>
</data>
<data name="Cancel" xml:space="preserve">
<value>Cancel</value>
</data>
<data name="Yes" xml:space="preserve">
<value>Yes</value>
</data>
<data name="No" xml:space="preserve">
<value>No</value>
</data>
</root>

View 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;
}
}
}
}

View 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>

View 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 { private 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.LogGenericInfoWithoutStacktrace("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.LogGenericInfoWithoutStacktrace("You can now notice the main ASF Config Generator screen, it's really easy to use!");
Logging.LogGenericInfoWithoutStacktrace("At the top of the window you can notice currently loaded configs, and 3 extra buttons for removing, renaming and adding new ones.");
Logging.LogGenericInfoWithoutStacktrace("In the middle of the window you will be able to configure all config properties that are available for you.");
Logging.LogGenericInfoWithoutStacktrace("In the top right corner you can find help button [?] which will redirect you to ASF wiki where you can find more information.");
Logging.LogGenericInfoWithoutStacktrace("Please click the help button to continue.");
break;
case EPhase.Help:
Logging.LogGenericInfoWithoutStacktrace("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.LogGenericInfoWithoutStacktrace("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.LogGenericInfoWithoutStacktrace("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.LogGenericInfoWithoutStacktrace("As you can see your bot config is now ready to configure!");
Logging.LogGenericInfoWithoutStacktrace("First thing that you want to do is switching \"Enabled\" property from False to True, try it!");
break;
case EPhase.BotEnabled:
Logging.LogGenericInfoWithoutStacktrace("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.LogGenericInfoWithoutStacktrace("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.LogGenericInfoWithoutStacktrace("While bot config affects only given bot instance you're configuring, global config affects whole ASF process, including all configured bots.");
Logging.LogGenericInfoWithoutStacktrace("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.LogGenericInfoWithoutStacktrace("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.LogGenericInfoWithoutStacktrace("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.LogGenericInfoWithoutStacktrace("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

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 KiB

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="9.0.1-beta1" targetFramework="net451" />
</packages>

6
GUI/App.config Normal file
View 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>

View File

@@ -22,12 +22,14 @@
*/
using Newtonsoft.Json;
using System.Collections.Generic;
namespace GUI {
internal static class Debugging {
#if DEBUG
internal static readonly bool IsDebugBuild = true;
#else
internal static readonly bool IsDebugBuild = false;
#endif
namespace ArchiSteamFarm {
internal sealed class SteamItemList {
[JsonProperty(Required = Required.Always)]
internal List<SteamItem> assets { get; } = new List<SteamItem>();
internal static bool IsReleaseBuild => !IsDebugBuild;
}
}

513
GUI/Form1.Designer.cs generated Normal file
View 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
View 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

File diff suppressed because it is too large Load Diff

84
GUI/Form2.Designer.cs generated Normal file
View 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
View 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

File diff suppressed because it is too large Load Diff

146
GUI/GUI.csproj Normal file
View File

@@ -0,0 +1,146 @@
<?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>
</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
View 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
View 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());
}
}
}

View 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
View 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;
}
}
}
}

View 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
View 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;
}
}
}
}

View 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
View 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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 KiB

View File

@@ -1,44 +1,50 @@
ArchiSteamFarm
===================
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).
[![Build Status (Windows)](https://img.shields.io/appveyor/ci/JustArchi/ArchiSteamFarm.svg?label=Windows)](https://ci.appveyor.com/project/JustArchi/ArchiSteamFarm)
[![Build Status (Mono)](https://img.shields.io/travis/JustArchi/ArchiSteamFarm.svg?label=Mono)](https://travis-ci.org/JustArchi/ArchiSteamFarm)
[![GitHub Release](https://img.shields.io/github/release/JustArchi/ArchiSteamFarm.svg?label=Latest)](https://github.com/JustArchi/ArchiSteamFarm/releases/latest)
[![Github All Releases](https://img.shields.io/github/downloads/JustArchi/ArchiSteamFarm/total.svg?label=Downloads)](https://github.com/JustArchi/ArchiSteamFarm/releases)
[![Paypal Donate](https://img.shields.io/badge/Paypal-donate-yellow.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=HD2P2P3WGS5Y4)
[![Steam Donate](https://img.shields.io/badge/Steam-donate-yellow.svg)](https://steamcommunity.com/tradeoffer/new/?partner=46697991&token=0ix2Ruv_)
---
ASF is a C# application that allows you to farm steam cards using multiple steam accounts simultaneously. Unlike Idle Master which works only for 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:**
**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
- Automatic farming of available games with card drops using any number of active accounts
- No requirement of running or even having official Steam client installed
- Guarantee of being VAC-free
- Complex error-reporting mechanism, allowing ASF to be smart and resume farming even in case of Steam or networking problems
- Customizable cards farming algorithm which will push performance of cards farming to the maximum
- Offline farming, allowing you to skip in-game status and not confuse your friends anymore
- Advanced support for alt accounts, including ability to redeem keys, redeem gifts, accept trades and more through a simple Steam chat
- Support for latest Steam security features, including SteamGuard, SteamParental and Two-Factor authentication
- Unique ASF 2FA mechanism allowing ASF to act as a mobile authenticator (if needed)
- StreamTradeMatcher integration allowing ASF to help you in completing your steam badges by accepting dupe trades
- Full Mono support, cross-OS compatibility, official support for Windows, Linux and OS X
- ...and many more!
**Setting up:**
**Setting up / Help**
Detailed setting up instructions are available on **[our wiki](https://github.com/JustArchi/ArchiSteamFarm/wiki/Setting-up)**.
Detailed guide regarding setting up and using ASF is available on **[our wiki](https://github.com/JustArchi/ArchiSteamFarm/wiki)**.
**Current Commands:**
**Supported / Tested operating systems:**
Detailed documentation of all available commands is available on **[our wiki](https://github.com/JustArchi/ArchiSteamFarm/wiki/Commands)**.
ASF officially supports Windows, Linux and OS X operating systems, including following tested variants:
> 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 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)
- Windows 10 (Native)
- Windows 8.1 (Native)
- Windows 7 (Native)
- Windows Vista (Native)
- Debian 9 Stretch (Mono)
- Debian 8 Jessie (Mono)
- Ubuntu 16.04 (Mono)
- OS X 10.11 (Mono)
- OS X 10.7 (Mono)
However, any operating system [listed here](http://www.mono-project.com/docs/about-mono/supported-platforms/) should run ASF flawlessly.
**Need help or more info?**
Head over to our [wiki](https://github.com/JustArchi/ArchiSteamFarm/wiki) then.
However, any **[currently supported Windows](http://windows.microsoft.com/en-us/windows/lifecycle)** should run ASF flawlessly (with latest .NET framework), as well as any **[Mono-powered OS](http://www.mono-project.com/docs/about-mono/supported-platforms/)** (with latest Mono).

View File

@@ -8,8 +8,29 @@ namespace SteamAuth
{
public class Confirmation
{
public string ConfirmationID;
public string ConfirmationKey;
public string ConfirmationDescription;
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
}
}
}

View File

@@ -32,8 +32,8 @@
<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.2\lib\net45\Newtonsoft.Json.dll</HintPath>
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.9.0.1-beta1\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />

View File

@@ -71,7 +71,7 @@ namespace SteamAuth
if (removeResponse == null || removeResponse.Response == null || !removeResponse.Response.Success) return false;
return true;
}
catch (Exception e)
catch (Exception)
{
return false;
}
@@ -115,7 +115,7 @@ namespace SteamAuth
codePoint /= steamGuardCodeTranslations.Length;
}
}
catch (Exception e)
catch (Exception)
{
return null; //Change later, catch-alls are bad!
}
@@ -162,9 +162,9 @@ namespace SteamAuth
string confDesc = confDescs[i].Groups[1].Value;
Confirmation conf = new Confirmation()
{
ConfirmationDescription = confDesc,
ConfirmationID = confID,
ConfirmationKey = confKey
Description = confDesc,
ID = confID,
Key = confKey
};
ret.Add(conf);
}
@@ -212,9 +212,9 @@ namespace SteamAuth
string confDesc = confDescs[i].Groups[1].Value;
Confirmation conf = new Confirmation()
{
ConfirmationDescription = confDesc,
ConfirmationID = confID,
ConfirmationKey = confKey
Description = confDesc,
ID = confID,
Key = confKey
};
ret.Add(conf);
}
@@ -268,7 +268,7 @@ namespace SteamAuth
this.Session.SteamLoginSecure = tokenSecure;
return true;
}
catch (Exception e)
catch (Exception)
{
return false;
}
@@ -300,7 +300,7 @@ namespace SteamAuth
this.Session.SteamLoginSecure = tokenSecure;
return true;
}
catch (Exception e)
catch (Exception)
{
return false;
}
@@ -308,7 +308,7 @@ namespace SteamAuth
private ConfirmationDetailsResponse _getConfirmationDetails(Confirmation conf)
{
string url = APIEndpoints.COMMUNITY_BASE + "/mobileconf/details/" + conf.ConfirmationID + "?";
string url = APIEndpoints.COMMUNITY_BASE + "/mobileconf/details/" + conf.ID + "?";
string queryString = GenerateConfirmationQueryParams("details");
url += queryString;
@@ -329,7 +329,7 @@ namespace SteamAuth
string url = APIEndpoints.COMMUNITY_BASE + "/mobileconf/ajaxop";
string queryString = "?op=" + op + "&";
queryString += GenerateConfirmationQueryParams(op);
queryString += "&cid=" + conf.ConfirmationID + "&ck=" + conf.ConfirmationKey;
queryString += "&cid=" + conf.ID + "&ck=" + conf.Key;
url += queryString;
CookieContainer cookies = new CookieContainer();
@@ -401,7 +401,7 @@ namespace SteamAuth
string hash = WebUtility.UrlEncode(encodedData);
return hash;
}
catch (Exception e)
catch (Exception)
{
return null; //Fix soon: catch-all is BAD!
}

View File

@@ -161,8 +161,6 @@ namespace SteamAuth
this.LoggedIn = true;
return LoginResult.LoginOkay;
}
return LoginResult.GeneralFailure;
}
private class LoginResponse

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="8.0.2" targetFramework="net452" />
<package id="Newtonsoft.Json" version="9.0.1-beta1" targetFramework="net451" />
</packages>

9
appveyor.yml Normal file
View 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
View 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! :)"

View File

@@ -81,7 +81,7 @@
Reads the next JSON token from the stream.
</summary>
<returns>
true if the next token was read successfully; false if there are no more tokens to read.
<c>true</c> if the next token was read successfully; <c>false</c> if there are no more tokens to read.
</returns>
</member>
<member name="M:Newtonsoft.Json.Bson.BsonReader.Close">
@@ -320,12 +320,12 @@
</member>
<member name="F:Newtonsoft.Json.ConstructorHandling.Default">
<summary>
First attempt to use the public default constructor, then fall back to single paramatized constructor, then the non-public default constructor.
First attempt to use the public default constructor, then fall back to single parameterized constructor, then the non-public default constructor.
</summary>
</member>
<member name="F:Newtonsoft.Json.ConstructorHandling.AllowNonPublicDefaultConstructor">
<summary>
Json.NET will use a non-public default constructor before falling back to a paramatized constructor.
Json.NET will use a non-public default constructor before falling back to a parameterized constructor.
</summary>
</member>
<member name="T:Newtonsoft.Json.Converters.BinaryConverter">
@@ -604,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.
@@ -775,7 +781,7 @@
</summary>
<param name="attributeName">Attribute name to test.</param>
<param name="prefix">The attribute name prefix if it has one, otherwise an empty string.</param>
<returns>True if attribute name is for a namespace attribute, otherwise false.</returns>
<returns><c>true</c> if attribute name is for a namespace attribute, otherwise <c>false</c>.</returns>
</member>
<member name="M:Newtonsoft.Json.Converters.XmlNodeConverter.CanConvert(System.Type)">
<summary>
@@ -1058,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.
@@ -1075,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"/>.
@@ -1091,10 +1118,9 @@
When overridden in a derived class, returns whether resetting an object changes its value.
</summary>
<returns>
true if resetting the component changes its value; otherwise, false.
<c>true</c> if resetting the component changes its value; otherwise, <c>false</c>.
</returns>
<param name="component">The component to test for reset capability.
</param>
<param name="component">The component to test for reset capability.</param>
</member>
<member name="M:Newtonsoft.Json.Linq.JPropertyDescriptor.GetValue(System.Object)">
<summary>
@@ -1126,10 +1152,9 @@
When overridden in a derived class, determines a value indicating whether the value of this property needs to be persisted.
</summary>
<returns>
true if the property should be persisted; otherwise, false.
<c>true</c> if the property should be persisted; otherwise, <c>false</c>.
</returns>
<param name="component">The component with the property to be examined for persistence.
</param>
<param name="component">The component with the property to be examined for persistence.</param>
</member>
<member name="P:Newtonsoft.Json.Linq.JPropertyDescriptor.ComponentType">
<summary>
@@ -1144,7 +1169,7 @@
When overridden in a derived class, gets a value indicating whether this property is read-only.
</summary>
<returns>
true if the property is read-only; otherwise, false.
<c>true</c> if the property is read-only; otherwise, <c>false</c>.
</returns>
</member>
<member name="P:Newtonsoft.Json.Linq.JPropertyDescriptor.PropertyType">
@@ -1212,7 +1237,7 @@
<param name="x">The first object of type <see cref="T:Newtonsoft.Json.Linq.JToken"/> to compare.</param>
<param name="y">The second object of type <see cref="T:Newtonsoft.Json.Linq.JToken"/> to compare.</param>
<returns>
true if the specified objects are equal; otherwise, false.
<c>true</c> if the specified objects are equal; otherwise, <c>false</c>.
</returns>
</member>
<member name="M:Newtonsoft.Json.Linq.JTokenEqualityComparer.GetHashCode(Newtonsoft.Json.Linq.JToken)">
@@ -1795,7 +1820,7 @@
<param name="propertyName">Name of the property.</param>
<param name="value">The value.</param>
<param name="comparison">One of the enumeration values that specifies how the strings will be compared.</param>
<returns>true if a value was successfully retrieved; otherwise, false.</returns>
<returns><c>true</c> if a value was successfully retrieved; otherwise, <c>false</c>.</returns>
</member>
<member name="M:Newtonsoft.Json.Linq.JObject.Add(System.String,Newtonsoft.Json.Linq.JToken)">
<summary>
@@ -1809,7 +1834,7 @@
Removes the property with the specified name.
</summary>
<param name="propertyName">Name of the property.</param>
<returns>true if item was successfully removed; otherwise, false.</returns>
<returns><c>true</c> if item was successfully removed; otherwise, <c>false</c>.</returns>
</member>
<member name="M:Newtonsoft.Json.Linq.JObject.TryGetValue(System.String,Newtonsoft.Json.Linq.JToken@)">
<summary>
@@ -1817,7 +1842,7 @@
</summary>
<param name="propertyName">Name of the property.</param>
<param name="value">The value.</param>
<returns>true if a value was successfully retrieved; otherwise, false.</returns>
<returns><c>true</c> if a value was successfully retrieved; otherwise, <c>false</c>.</returns>
</member>
<member name="M:Newtonsoft.Json.Linq.JObject.GetEnumerator">
<summary>
@@ -2150,7 +2175,7 @@
Reads the next JSON token from the stream.
</summary>
<returns>
true if the next token was read successfully; false if there are no more tokens to read.
<c>true</c> if the next token was read successfully; <c>false</c> if there are no more tokens to read.
</returns>
</member>
<member name="P:Newtonsoft.Json.Linq.JTokenReader.Path">
@@ -2409,7 +2434,7 @@
</summary>
<param name="t1">The first <see cref="T:Newtonsoft.Json.Linq.JToken"/> to compare.</param>
<param name="t2">The second <see cref="T:Newtonsoft.Json.Linq.JToken"/> to compare.</param>
<returns>true if the tokens are equal; otherwise false.</returns>
<returns><c>true</c> if the tokens are equal; otherwise <c>false</c>.</returns>
</member>
<member name="P:Newtonsoft.Json.Linq.JToken.Next">
<summary>
@@ -3511,9 +3536,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>
@@ -3539,7 +3564,7 @@
Indicates whether the current object is equal to another object of the same type.
</summary>
<returns>
true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
<c>true</c> if the current object is equal to the <paramref name="other"/> parameter; otherwise, <c>false</c>.
</returns>
<param name="other">An object to compare with this object.</param>
</member>
@@ -3549,7 +3574,7 @@
</summary>
<param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
<returns>
true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
<c>true</c> if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, <c>false</c>.
</returns>
<exception cref="T:System.NullReferenceException">
The <paramref name="obj"/> parameter is null.
@@ -3639,6 +3664,47 @@
Do not try to read metadata properties.
</summary>
</member>
<member name="T:Newtonsoft.Json.Serialization.CamelCaseNamingStrategy">
<summary>
A camel case naming strategy.
</summary>
</member>
<member name="M:Newtonsoft.Json.Serialization.CamelCaseNamingStrategy.#ctor(System.Boolean,System.Boolean)">
<summary>
Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.CamelCaseNamingStrategy"/> class.
</summary>
<param name="processDictionaryKeys">
A flag indicating whether dictionary keys should be processed.
</param>
<param name="overrideSpecifiedNames">
A flag indicating whether explicitly specified property names should be processed,
e.g. a property name customized with a <see cref="T:Newtonsoft.Json.JsonPropertyAttribute"/>.
</param>
</member>
<member name="M:Newtonsoft.Json.Serialization.CamelCaseNamingStrategy.#ctor">
<summary>
Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.CamelCaseNamingStrategy"/> class.
</summary>
</member>
<member name="M:Newtonsoft.Json.Serialization.CamelCaseNamingStrategy.ResolvePropertyName(System.String)">
<summary>
Resolves the specified property name.
</summary>
<param name="name">The property name to resolve.</param>
<returns>The resolved property name.</returns>
</member>
<member name="T:Newtonsoft.Json.Serialization.DefaultNamingStrategy">
<summary>
The default naming strategy. Property names and dictionary keys are unchanged.
</summary>
</member>
<member name="M:Newtonsoft.Json.Serialization.DefaultNamingStrategy.ResolvePropertyName(System.String)">
<summary>
Resolves the specified property name.
</summary>
<param name="name">The property name to resolve.</param>
<returns>The resolved property name.</returns>
</member>
<member name="T:Newtonsoft.Json.Serialization.DiagnosticsTraceWriter">
<summary>
Represents a trace writer that writes to the application's <see cref="T:System.Diagnostics.TraceListener"/> instances.
@@ -3781,6 +3847,46 @@
A <see cref="T:System.String"/> of the most recent trace messages.
</returns>
</member>
<member name="T:Newtonsoft.Json.Serialization.NamingStrategy">
<summary>
A base class for resolving how property names and dictionary keys are serialized.
</summary>
</member>
<member name="P:Newtonsoft.Json.Serialization.NamingStrategy.ProcessDictionaryKeys">
<summary>
A flag indicating whether dictionary keys should be processed.
Defaults to <c>false</c>.
</summary>
</member>
<member name="P:Newtonsoft.Json.Serialization.NamingStrategy.OverrideSpecifiedNames">
<summary>
A flag indicating whether explicitly specified property names,
e.g. a property name customized with a <see cref="T:Newtonsoft.Json.JsonPropertyAttribute"/>, should be processed.
Defaults to <c>false</c>.
</summary>
</member>
<member name="M:Newtonsoft.Json.Serialization.NamingStrategy.GetPropertyName(System.String,System.Boolean)">
<summary>
Gets the serialized name for a given property name.
</summary>
<param name="name">The initial property name.</param>
<param name="hasSpecifiedName">A flag indicating whether the property has had a name explicitly specfied.</param>
<returns>The serialized property name.</returns>
</member>
<member name="M:Newtonsoft.Json.Serialization.NamingStrategy.GetDictionaryKey(System.String)">
<summary>
Gets the serialized key for a given dictionary key.
</summary>
<param name="key">The initial dictionary key.</param>
<returns>The serialized dictionary key.</returns>
</member>
<member name="M:Newtonsoft.Json.Serialization.NamingStrategy.ResolvePropertyName(System.String)">
<summary>
Resolves the specified property name.
</summary>
<param name="name">The property name to resolve.</param>
<returns>The resolved property name.</returns>
</member>
<member name="T:Newtonsoft.Json.Serialization.ReflectionAttributeProvider">
<summary>
Provides methods to get attributes from a <see cref="T:System.Type"/>, <see cref="T:System.Reflection.MemberInfo"/>, <see cref="T:System.Reflection.ParameterInfo"/> or <see cref="T:System.Reflection.Assembly"/>.
@@ -3807,6 +3913,35 @@
<param name="inherit">When true, look up the hierarchy chain for the inherited custom attribute.</param>
<returns>A collection of <see cref="T:System.Attribute"/>s, or an empty collection.</returns>
</member>
<member name="T:Newtonsoft.Json.Serialization.SnakeCaseNamingStrategy">
<summary>
A snake case naming strategy.
</summary>
</member>
<member name="M:Newtonsoft.Json.Serialization.SnakeCaseNamingStrategy.#ctor(System.Boolean,System.Boolean)">
<summary>
Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.SnakeCaseNamingStrategy"/> class.
</summary>
<param name="processDictionaryKeys">
A flag indicating whether dictionary keys should be processed.
</param>
<param name="overrideSpecifiedNames">
A flag indicating whether explicitly specified property names should be processed,
e.g. a property name customized with a <see cref="T:Newtonsoft.Json.JsonPropertyAttribute"/>.
</param>
</member>
<member name="M:Newtonsoft.Json.Serialization.SnakeCaseNamingStrategy.#ctor">
<summary>
Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.SnakeCaseNamingStrategy"/> class.
</summary>
</member>
<member name="M:Newtonsoft.Json.Serialization.SnakeCaseNamingStrategy.ResolvePropertyName(System.String)">
<summary>
Resolves the specified property name.
</summary>
<param name="name">The property name to resolve.</param>
<returns>The resolved property name.</returns>
</member>
<member name="T:Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver">
<summary>
Resolves member mappings for a type, camel casing property names.
@@ -3817,13 +3952,6 @@
Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver"/> class.
</summary>
</member>
<member name="M:Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver.ResolvePropertyName(System.String)">
<summary>
Resolves the name of the property.
</summary>
<param name="propertyName">Name of the property.</param>
<returns>The property name camel cased.</returns>
</member>
<member name="T:Newtonsoft.Json.Serialization.DefaultContractResolver">
<summary>
Used by <see cref="T:Newtonsoft.Json.JsonSerializer"/> to resolves a <see cref="T:Newtonsoft.Json.Serialization.JsonContract"/> for a given <see cref="T:System.Type"/>.
@@ -3868,6 +3996,12 @@
<c>true</c> if the <see cref="T:System.SerializableAttribute"/> attribute will be ignored when serializing and deserializing types; otherwise, <c>false</c>.
</value>
</member>
<member name="P:Newtonsoft.Json.Serialization.DefaultContractResolver.NamingStrategy">
<summary>
Gets or sets the naming strategy used to resolve how property names and dictionary keys are serialized.
</summary>
<value>The naming strategy used to resolve how property names and dictionary keys are serialized.</value>
</member>
<member name="M:Newtonsoft.Json.Serialization.DefaultContractResolver.#ctor">
<summary>
Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.DefaultContractResolver"/> class.
@@ -4772,13 +4906,6 @@
<param name="converterArgs">Optional arguments to pass to an initializing constructor of the JsonConverter.
If null, the default constructor is used.</param>
</member>
<member name="M:Newtonsoft.Json.Serialization.JsonTypeReflector.GetJsonConverterCreator(System.Type)">
<summary>
Create a factory function that can be used to create instances of a JsonConverter described by the
argument type. The returned function can then be used to either invoke the converter's default ctor, or any
parameterized constructors by way of an object array.
</summary>
</member>
<member name="T:Newtonsoft.Json.Serialization.ObjectConstructor`1">
<summary>
Represents a method that constructs an object.
@@ -5940,16 +6067,6 @@
<param name="initial">The list to add to.</param>
<param name="collection">The collection of elements to add.</param>
</member>
<member name="M:Newtonsoft.Json.Utilities.CollectionUtils.IndexOf``1(System.Collections.Generic.IEnumerable{``0},``0,System.Collections.Generic.IEqualityComparer{``0})">
<summary>
Returns the index of the first occurrence in a sequence by using a specified IEqualityComparer{TSource}.
</summary>
<typeparam name="TSource">The type of the elements of source.</typeparam>
<param name="list">A sequence in which to locate a value.</param>
<param name="value">The object to locate in the sequence</param>
<param name="comparer">An equality comparer to compare values.</param>
<returns>The zero-based index of the first occurrence of value within the entire sequence, if found; otherwise, 1.</returns>
</member>
<member name="M:Newtonsoft.Json.Utilities.ReflectionUtils.GetCollectionItemType(System.Type)">
<summary>
Gets the type of the typed collection's items.
@@ -6028,13 +6145,6 @@
<c>true</c> if the string is all white space; otherwise, <c>false</c>.
</returns>
</member>
<member name="M:Newtonsoft.Json.Utilities.StringUtils.NullEmptyString(System.String)">
<summary>
Nulls an empty string.
</summary>
<param name="s">The string.</param>
<returns>Null if the string was null, otherwise the string unchanged.</returns>
</member>
<member name="T:Newtonsoft.Json.Required">
<summary>
Indicating whether a property is required.
@@ -6167,21 +6277,38 @@
</member>
<member name="P:Newtonsoft.Json.JsonContainerAttribute.ItemConverterType">
<summary>
Gets the collection's items converter.
Gets or sets the collection's items converter.
</summary>
<value>The collection's items converter.</value>
</member>
<member name="P:Newtonsoft.Json.JsonContainerAttribute.ItemConverterParameters">
<summary>
The parameter list to use when constructing the JsonConverter described by ItemConverterType.
The parameter list to use when constructing the <see cref="T:Newtonsoft.Json.JsonConverter"/> described by ItemConverterType.
If null, the default constructor is used.
When non-null, there must be a constructor defined in the JsonConverter that exactly matches the number,
When non-null, there must be a constructor defined in the <see cref="T:Newtonsoft.Json.JsonConverter"/> that exactly matches the number,
order, and type of these parameters.
</summary>
<example>
[JsonContainer(ItemConverterType = typeof(MyContainerConverter), ItemConverterParameters = new object[] { 123, "Four" })]
</example>
</member>
<member name="P:Newtonsoft.Json.JsonContainerAttribute.NamingStrategyType">
<summary>
Gets or sets the <see cref="T:System.Type"/> of the <see cref="T:Newtonsoft.Json.Serialization.NamingStrategy"/>.
</summary>
<value>The <see cref="T:System.Type"/> of the <see cref="T:Newtonsoft.Json.Serialization.NamingStrategy"/>.</value>
</member>
<member name="P:Newtonsoft.Json.JsonContainerAttribute.NamingStrategyParameters">
<summary>
The parameter list to use when constructing the <see cref="T:Newtonsoft.Json.Serialization.NamingStrategy"/> described by NamingStrategyType.
If null, the default constructor is used.
When non-null, there must be a constructor defined in the <see cref="T:Newtonsoft.Json.Serialization.NamingStrategy"/> that exactly matches the number,
order, and type of these parameters.
</summary>
<example>
[JsonContainer(NamingStrategyType = typeof(MyNamingStrategy), NamingStrategyParameters = new object[] { 123, "Four" })]
</example>
</member>
<member name="P:Newtonsoft.Json.JsonContainerAttribute.IsReference">
<summary>
Gets or sets a value that indicates whether to preserve object references.
@@ -6263,13 +6390,13 @@
</member>
<member name="P:Newtonsoft.Json.JsonConverterAttribute.ConverterType">
<summary>
Gets the <see cref="T:System.Type"/> of the converter.
Gets the <see cref="T:System.Type"/> of the <see cref="T:Newtonsoft.Json.JsonConverter"/>.
</summary>
<value>The <see cref="T:System.Type"/> of the converter.</value>
<value>The <see cref="T:System.Type"/> of the <see cref="T:Newtonsoft.Json.JsonConverter"/>.</value>
</member>
<member name="P:Newtonsoft.Json.JsonConverterAttribute.ConverterParameters">
<summary>
The parameter list to use when constructing the JsonConverter described by ConverterType.
The parameter list to use when constructing the <see cref="T:Newtonsoft.Json.JsonConverter"/> described by ConverterType.
If null, the default constructor is used.
</summary>
</member>
@@ -6277,14 +6404,14 @@
<summary>
Initializes a new instance of the <see cref="T:Newtonsoft.Json.JsonConverterAttribute"/> class.
</summary>
<param name="converterType">Type of the converter.</param>
<param name="converterType">Type of the <see cref="T:Newtonsoft.Json.JsonConverter"/>.</param>
</member>
<member name="M:Newtonsoft.Json.JsonConverterAttribute.#ctor(System.Type,System.Object[])">
<summary>
Initializes a new instance of the <see cref="T:Newtonsoft.Json.JsonConverterAttribute"/> class.
</summary>
<param name="converterType">Type of the converter.</param>
<param name="converterParameters">Parameter list to use when constructing the JsonConverter. Can be null.</param>
<param name="converterType">Type of the <see cref="T:Newtonsoft.Json.JsonConverter"/>.</param>
<param name="converterParameters">Parameter list to use when constructing the <see cref="T:Newtonsoft.Json.JsonConverter"/>. Can be null.</param>
</member>
<member name="T:Newtonsoft.Json.JsonObjectAttribute">
<summary>
@@ -6630,7 +6757,7 @@
Reads the next JSON token from the stream.
</summary>
<returns>
true if the next token was read successfully; false if there are no more tokens to read.
<c>true</c> if the next token was read successfully; <c>false</c> if there are no more tokens to read.
</returns>
</member>
<member name="T:Newtonsoft.Json.MemberSerialization">
@@ -6646,7 +6773,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>
@@ -6698,7 +6825,7 @@
Reads the next JSON token from the stream.
</summary>
<returns>
true if the next token was read successfully; false if there are no more tokens to read.
<c>true</c> if the next token was read successfully; <c>false</c> if there are no more tokens to read.
</returns>
</member>
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsInt32">
@@ -6779,21 +6906,38 @@
</member>
<member name="P:Newtonsoft.Json.JsonPropertyAttribute.ItemConverterType">
<summary>
Gets or sets the converter used when serializing the property's collection items.
Gets or sets the <see cref="T:Newtonsoft.Json.JsonConverter"/> used when serializing the property's collection items.
</summary>
<value>The collection's items converter.</value>
<value>The collection's items <see cref="T:Newtonsoft.Json.JsonConverter"/>.</value>
</member>
<member name="P:Newtonsoft.Json.JsonPropertyAttribute.ItemConverterParameters">
<summary>
The parameter list to use when constructing the JsonConverter described by ItemConverterType.
The parameter list to use when constructing the <see cref="T:Newtonsoft.Json.JsonConverter"/> described by ItemConverterType.
If null, the default constructor is used.
When non-null, there must be a constructor defined in the JsonConverter that exactly matches the number,
When non-null, there must be a constructor defined in the <see cref="T:Newtonsoft.Json.JsonConverter"/> that exactly matches the number,
order, and type of these parameters.
</summary>
<example>
[JsonProperty(ItemConverterType = typeof(MyContainerConverter), ItemConverterParameters = new object[] { 123, "Four" })]
</example>
</member>
<member name="P:Newtonsoft.Json.JsonPropertyAttribute.NamingStrategyType">
<summary>
Gets or sets the <see cref="T:System.Type"/> of the <see cref="T:Newtonsoft.Json.Serialization.NamingStrategy"/>.
</summary>
<value>The <see cref="T:System.Type"/> of the <see cref="T:Newtonsoft.Json.Serialization.NamingStrategy"/>.</value>
</member>
<member name="P:Newtonsoft.Json.JsonPropertyAttribute.NamingStrategyParameters">
<summary>
The parameter list to use when constructing the <see cref="T:Newtonsoft.Json.Serialization.NamingStrategy"/> described by NamingStrategyType.
If null, the default constructor is used.
When non-null, there must be a constructor defined in the <see cref="T:Newtonsoft.Json.Serialization.NamingStrategy"/> that exactly matches the number,
order, and type of these parameters.
</summary>
<example>
[JsonProperty(NamingStrategyType = typeof(MyNamingStrategy), NamingStrategyParameters = new object[] { 123, "Four" })]
</example>
</member>
<member name="P:Newtonsoft.Json.JsonPropertyAttribute.NullValueHandling">
<summary>
Gets or sets the null value handling used when serializing this property.
@@ -7463,7 +7607,7 @@
<summary>
Reads the next JSON token from the stream.
</summary>
<returns>true if the next token was read successfully; false if there are no more tokens to read.</returns>
<returns><c>true</c> if the next token was read successfully; <c>false</c> if there are no more tokens to read.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonReader.ReadAsInt32">
<summary>

View File

@@ -64,7 +64,7 @@
Reads the next JSON token from the stream.
</summary>
<returns>
true if the next token was read successfully; false if there are no more tokens to read.
<c>true</c> if the next token was read successfully; <c>false</c> if there are no more tokens to read.
</returns>
</member>
<member name="M:Newtonsoft.Json.Bson.BsonReader.Close">
@@ -627,6 +627,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.
@@ -798,7 +804,7 @@
</summary>
<param name="attributeName">Attribute name to test.</param>
<param name="prefix">The attribute name prefix if it has one, otherwise an empty string.</param>
<returns>True if attribute name is for a namespace attribute, otherwise false.</returns>
<returns><c>true</c> if attribute name is for a namespace attribute, otherwise <c>false</c>.</returns>
</member>
<member name="M:Newtonsoft.Json.Converters.XmlNodeConverter.CanConvert(System.Type)">
<summary>
@@ -816,12 +822,12 @@
</member>
<member name="F:Newtonsoft.Json.ConstructorHandling.Default">
<summary>
First attempt to use the public default constructor, then fall back to single paramatized constructor, then the non-public default constructor.
First attempt to use the public default constructor, then fall back to single parameterized constructor, then the non-public default constructor.
</summary>
</member>
<member name="F:Newtonsoft.Json.ConstructorHandling.AllowNonPublicDefaultConstructor">
<summary>
Json.NET will use a non-public default constructor before falling back to a paramatized constructor.
Json.NET will use a non-public default constructor before falling back to a parameterized constructor.
</summary>
</member>
<member name="T:Newtonsoft.Json.DateFormatHandling">
@@ -1089,10 +1095,9 @@
When overridden in a derived class, returns whether resetting an object changes its value.
</summary>
<returns>
true if resetting the component changes its value; otherwise, false.
<c>true</c> if resetting the component changes its value; otherwise, <c>false</c>.
</returns>
<param name="component">The component to test for reset capability.
</param>
<param name="component">The component to test for reset capability.</param>
</member>
<member name="M:Newtonsoft.Json.Linq.JPropertyDescriptor.GetValue(System.Object)">
<summary>
@@ -1124,10 +1129,9 @@
When overridden in a derived class, determines a value indicating whether the value of this property needs to be persisted.
</summary>
<returns>
true if the property should be persisted; otherwise, false.
<c>true</c> if the property should be persisted; otherwise, <c>false</c>.
</returns>
<param name="component">The component with the property to be examined for persistence.
</param>
<param name="component">The component with the property to be examined for persistence.</param>
</member>
<member name="P:Newtonsoft.Json.Linq.JPropertyDescriptor.ComponentType">
<summary>
@@ -1142,7 +1146,7 @@
When overridden in a derived class, gets a value indicating whether this property is read-only.
</summary>
<returns>
true if the property is read-only; otherwise, false.
<c>true</c> if the property is read-only; otherwise, <c>false</c>.
</returns>
</member>
<member name="P:Newtonsoft.Json.Linq.JPropertyDescriptor.PropertyType">
@@ -1190,6 +1194,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.
@@ -1207,6 +1217,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.JRaw">
<summary>
Represents a raw JSON string.
@@ -1255,7 +1280,7 @@
<param name="x">The first object of type <see cref="T:Newtonsoft.Json.Linq.JToken"/> to compare.</param>
<param name="y">The second object of type <see cref="T:Newtonsoft.Json.Linq.JToken"/> to compare.</param>
<returns>
true if the specified objects are equal; otherwise, false.
<c>true</c> if the specified objects are equal; otherwise, <c>false</c>.
</returns>
</member>
<member name="M:Newtonsoft.Json.Linq.JTokenEqualityComparer.GetHashCode(Newtonsoft.Json.Linq.JToken)">
@@ -1843,7 +1868,7 @@
<param name="propertyName">Name of the property.</param>
<param name="value">The value.</param>
<param name="comparison">One of the enumeration values that specifies how the strings will be compared.</param>
<returns>true if a value was successfully retrieved; otherwise, false.</returns>
<returns><c>true</c> if a value was successfully retrieved; otherwise, <c>false</c>.</returns>
</member>
<member name="M:Newtonsoft.Json.Linq.JObject.Add(System.String,Newtonsoft.Json.Linq.JToken)">
<summary>
@@ -1857,7 +1882,7 @@
Removes the property with the specified name.
</summary>
<param name="propertyName">Name of the property.</param>
<returns>true if item was successfully removed; otherwise, false.</returns>
<returns><c>true</c> if item was successfully removed; otherwise, <c>false</c>.</returns>
</member>
<member name="M:Newtonsoft.Json.Linq.JObject.TryGetValue(System.String,Newtonsoft.Json.Linq.JToken@)">
<summary>
@@ -1865,7 +1890,7 @@
</summary>
<param name="propertyName">Name of the property.</param>
<param name="value">The value.</param>
<returns>true if a value was successfully retrieved; otherwise, false.</returns>
<returns><c>true</c> if a value was successfully retrieved; otherwise, <c>false</c>.</returns>
</member>
<member name="M:Newtonsoft.Json.Linq.JObject.GetEnumerator">
<summary>
@@ -2204,7 +2229,7 @@
Reads the next JSON token from the stream.
</summary>
<returns>
true if the next token was read successfully; false if there are no more tokens to read.
<c>true</c> if the next token was read successfully; <c>false</c> if there are no more tokens to read.
</returns>
</member>
<member name="P:Newtonsoft.Json.Linq.JTokenReader.Path">
@@ -2469,7 +2494,7 @@
</summary>
<param name="t1">The first <see cref="T:Newtonsoft.Json.Linq.JToken"/> to compare.</param>
<param name="t2">The second <see cref="T:Newtonsoft.Json.Linq.JToken"/> to compare.</param>
<returns>true if the tokens are equal; otherwise false.</returns>
<returns><c>true</c> if the tokens are equal; otherwise <c>false</c>.</returns>
</member>
<member name="P:Newtonsoft.Json.Linq.JToken.Next">
<summary>
@@ -3605,9 +3630,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>
@@ -3633,7 +3658,7 @@
Indicates whether the current object is equal to another object of the same type.
</summary>
<returns>
true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
<c>true</c> if the current object is equal to the <paramref name="other"/> parameter; otherwise, <c>false</c>.
</returns>
<param name="other">An object to compare with this object.</param>
</member>
@@ -3643,7 +3668,7 @@
</summary>
<param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
<returns>
true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
<c>true</c> if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, <c>false</c>.
</returns>
<exception cref="T:System.NullReferenceException">
The <paramref name="obj"/> parameter is null.
@@ -3733,6 +3758,47 @@
Do not try to read metadata properties.
</summary>
</member>
<member name="T:Newtonsoft.Json.Serialization.CamelCaseNamingStrategy">
<summary>
A camel case naming strategy.
</summary>
</member>
<member name="M:Newtonsoft.Json.Serialization.CamelCaseNamingStrategy.#ctor(System.Boolean,System.Boolean)">
<summary>
Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.CamelCaseNamingStrategy"/> class.
</summary>
<param name="processDictionaryKeys">
A flag indicating whether dictionary keys should be processed.
</param>
<param name="overrideSpecifiedNames">
A flag indicating whether explicitly specified property names should be processed,
e.g. a property name customized with a <see cref="T:Newtonsoft.Json.JsonPropertyAttribute"/>.
</param>
</member>
<member name="M:Newtonsoft.Json.Serialization.CamelCaseNamingStrategy.#ctor">
<summary>
Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.CamelCaseNamingStrategy"/> class.
</summary>
</member>
<member name="M:Newtonsoft.Json.Serialization.CamelCaseNamingStrategy.ResolvePropertyName(System.String)">
<summary>
Resolves the specified property name.
</summary>
<param name="name">The property name to resolve.</param>
<returns>The resolved property name.</returns>
</member>
<member name="T:Newtonsoft.Json.Serialization.DefaultNamingStrategy">
<summary>
The default naming strategy. Property names and dictionary keys are unchanged.
</summary>
</member>
<member name="M:Newtonsoft.Json.Serialization.DefaultNamingStrategy.ResolvePropertyName(System.String)">
<summary>
Resolves the specified property name.
</summary>
<param name="name">The property name to resolve.</param>
<returns>The resolved property name.</returns>
</member>
<member name="T:Newtonsoft.Json.Serialization.DiagnosticsTraceWriter">
<summary>
Represents a trace writer that writes to the application's <see cref="T:System.Diagnostics.TraceListener"/> instances.
@@ -3875,6 +3941,46 @@
A <see cref="T:System.String"/> of the most recent trace messages.
</returns>
</member>
<member name="T:Newtonsoft.Json.Serialization.NamingStrategy">
<summary>
A base class for resolving how property names and dictionary keys are serialized.
</summary>
</member>
<member name="P:Newtonsoft.Json.Serialization.NamingStrategy.ProcessDictionaryKeys">
<summary>
A flag indicating whether dictionary keys should be processed.
Defaults to <c>false</c>.
</summary>
</member>
<member name="P:Newtonsoft.Json.Serialization.NamingStrategy.OverrideSpecifiedNames">
<summary>
A flag indicating whether explicitly specified property names,
e.g. a property name customized with a <see cref="T:Newtonsoft.Json.JsonPropertyAttribute"/>, should be processed.
Defaults to <c>false</c>.
</summary>
</member>
<member name="M:Newtonsoft.Json.Serialization.NamingStrategy.GetPropertyName(System.String,System.Boolean)">
<summary>
Gets the serialized name for a given property name.
</summary>
<param name="name">The initial property name.</param>
<param name="hasSpecifiedName">A flag indicating whether the property has had a name explicitly specfied.</param>
<returns>The serialized property name.</returns>
</member>
<member name="M:Newtonsoft.Json.Serialization.NamingStrategy.GetDictionaryKey(System.String)">
<summary>
Gets the serialized key for a given dictionary key.
</summary>
<param name="key">The initial dictionary key.</param>
<returns>The serialized dictionary key.</returns>
</member>
<member name="M:Newtonsoft.Json.Serialization.NamingStrategy.ResolvePropertyName(System.String)">
<summary>
Resolves the specified property name.
</summary>
<param name="name">The property name to resolve.</param>
<returns>The resolved property name.</returns>
</member>
<member name="T:Newtonsoft.Json.Serialization.ReflectionAttributeProvider">
<summary>
Provides methods to get attributes from a <see cref="T:System.Type"/>, <see cref="T:System.Reflection.MemberInfo"/>, <see cref="T:System.Reflection.ParameterInfo"/> or <see cref="T:System.Reflection.Assembly"/>.
@@ -3901,6 +4007,35 @@
<param name="inherit">When true, look up the hierarchy chain for the inherited custom attribute.</param>
<returns>A collection of <see cref="T:System.Attribute"/>s, or an empty collection.</returns>
</member>
<member name="T:Newtonsoft.Json.Serialization.SnakeCaseNamingStrategy">
<summary>
A snake case naming strategy.
</summary>
</member>
<member name="M:Newtonsoft.Json.Serialization.SnakeCaseNamingStrategy.#ctor(System.Boolean,System.Boolean)">
<summary>
Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.SnakeCaseNamingStrategy"/> class.
</summary>
<param name="processDictionaryKeys">
A flag indicating whether dictionary keys should be processed.
</param>
<param name="overrideSpecifiedNames">
A flag indicating whether explicitly specified property names should be processed,
e.g. a property name customized with a <see cref="T:Newtonsoft.Json.JsonPropertyAttribute"/>.
</param>
</member>
<member name="M:Newtonsoft.Json.Serialization.SnakeCaseNamingStrategy.#ctor">
<summary>
Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.SnakeCaseNamingStrategy"/> class.
</summary>
</member>
<member name="M:Newtonsoft.Json.Serialization.SnakeCaseNamingStrategy.ResolvePropertyName(System.String)">
<summary>
Resolves the specified property name.
</summary>
<param name="name">The property name to resolve.</param>
<returns>The resolved property name.</returns>
</member>
<member name="T:Newtonsoft.Json.Serialization.JsonISerializableContract">
<summary>
Contract details for a <see cref="T:System.Type"/> used by the <see cref="T:Newtonsoft.Json.JsonSerializer"/>.
@@ -3999,13 +4134,6 @@
Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver"/> class.
</summary>
</member>
<member name="M:Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver.ResolvePropertyName(System.String)">
<summary>
Resolves the name of the property.
</summary>
<param name="propertyName">Name of the property.</param>
<returns>The property name camel cased.</returns>
</member>
<member name="T:Newtonsoft.Json.Serialization.DefaultContractResolver">
<summary>
Used by <see cref="T:Newtonsoft.Json.JsonSerializer"/> to resolves a <see cref="T:Newtonsoft.Json.Serialization.JsonContract"/> for a given <see cref="T:System.Type"/>.
@@ -4050,6 +4178,12 @@
<c>true</c> if the <see cref="T:System.SerializableAttribute"/> attribute will be ignored when serializing and deserializing types; otherwise, <c>false</c>.
</value>
</member>
<member name="P:Newtonsoft.Json.Serialization.DefaultContractResolver.NamingStrategy">
<summary>
Gets or sets the naming strategy used to resolve how property names and dictionary keys are serialized.
</summary>
<value>The naming strategy used to resolve how property names and dictionary keys are serialized.</value>
</member>
<member name="M:Newtonsoft.Json.Serialization.DefaultContractResolver.#ctor">
<summary>
Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.DefaultContractResolver"/> class.
@@ -4866,13 +5000,6 @@
<param name="converterArgs">Optional arguments to pass to an initializing constructor of the JsonConverter.
If null, the default constructor is used.</param>
</member>
<member name="M:Newtonsoft.Json.Serialization.JsonTypeReflector.GetJsonConverterCreator(System.Type)">
<summary>
Create a factory function that can be used to create instances of a JsonConverter described by the
argument type. The returned function can then be used to either invoke the converter's default ctor, or any
parameterized constructors by way of an object array.
</summary>
</member>
<member name="T:Newtonsoft.Json.Serialization.ReflectionValueProvider">
<summary>
Get and set values for a <see cref="T:System.Reflection.MemberInfo"/> using reflection.
@@ -4976,16 +5103,6 @@
<param name="initial">The list to add to.</param>
<param name="collection">The collection of elements to add.</param>
</member>
<member name="M:Newtonsoft.Json.Utilities.CollectionUtils.IndexOf``1(System.Collections.Generic.IEnumerable{``0},``0,System.Collections.Generic.IEqualityComparer{``0})">
<summary>
Returns the index of the first occurrence in a sequence by using a specified IEqualityComparer{TSource}.
</summary>
<typeparam name="TSource">The type of the elements of source.</typeparam>
<param name="list">A sequence in which to locate a value.</param>
<param name="value">The object to locate in the sequence</param>
<param name="comparer">An equality comparer to compare values.</param>
<returns>The zero-based index of the first occurrence of value within the entire sequence, if found; otherwise, 1.</returns>
</member>
<member name="M:Newtonsoft.Json.Utilities.ReflectionUtils.GetCollectionItemType(System.Type)">
<summary>
Gets the type of the typed collection's items.
@@ -5064,13 +5181,6 @@
<c>true</c> if the string is all white space; otherwise, <c>false</c>.
</returns>
</member>
<member name="M:Newtonsoft.Json.Utilities.StringUtils.NullEmptyString(System.String)">
<summary>
Nulls an empty string.
</summary>
<param name="s">The string.</param>
<returns>Null if the string was null, otherwise the string unchanged.</returns>
</member>
<member name="T:Newtonsoft.Json.Required">
<summary>
Indicating whether a property is required.
@@ -5203,21 +5313,38 @@
</member>
<member name="P:Newtonsoft.Json.JsonContainerAttribute.ItemConverterType">
<summary>
Gets the collection's items converter.
Gets or sets the collection's items converter.
</summary>
<value>The collection's items converter.</value>
</member>
<member name="P:Newtonsoft.Json.JsonContainerAttribute.ItemConverterParameters">
<summary>
The parameter list to use when constructing the JsonConverter described by ItemConverterType.
The parameter list to use when constructing the <see cref="T:Newtonsoft.Json.JsonConverter"/> described by ItemConverterType.
If null, the default constructor is used.
When non-null, there must be a constructor defined in the JsonConverter that exactly matches the number,
When non-null, there must be a constructor defined in the <see cref="T:Newtonsoft.Json.JsonConverter"/> that exactly matches the number,
order, and type of these parameters.
</summary>
<example>
[JsonContainer(ItemConverterType = typeof(MyContainerConverter), ItemConverterParameters = new object[] { 123, "Four" })]
</example>
</member>
<member name="P:Newtonsoft.Json.JsonContainerAttribute.NamingStrategyType">
<summary>
Gets or sets the <see cref="T:System.Type"/> of the <see cref="T:Newtonsoft.Json.Serialization.NamingStrategy"/>.
</summary>
<value>The <see cref="T:System.Type"/> of the <see cref="T:Newtonsoft.Json.Serialization.NamingStrategy"/>.</value>
</member>
<member name="P:Newtonsoft.Json.JsonContainerAttribute.NamingStrategyParameters">
<summary>
The parameter list to use when constructing the <see cref="T:Newtonsoft.Json.Serialization.NamingStrategy"/> described by NamingStrategyType.
If null, the default constructor is used.
When non-null, there must be a constructor defined in the <see cref="T:Newtonsoft.Json.Serialization.NamingStrategy"/> that exactly matches the number,
order, and type of these parameters.
</summary>
<example>
[JsonContainer(NamingStrategyType = typeof(MyNamingStrategy), NamingStrategyParameters = new object[] { 123, "Four" })]
</example>
</member>
<member name="P:Newtonsoft.Json.JsonContainerAttribute.IsReference">
<summary>
Gets or sets a value that indicates whether to preserve object references.
@@ -5299,13 +5426,13 @@
</member>
<member name="P:Newtonsoft.Json.JsonConverterAttribute.ConverterType">
<summary>
Gets the <see cref="T:System.Type"/> of the converter.
Gets the <see cref="T:System.Type"/> of the <see cref="T:Newtonsoft.Json.JsonConverter"/>.
</summary>
<value>The <see cref="T:System.Type"/> of the converter.</value>
<value>The <see cref="T:System.Type"/> of the <see cref="T:Newtonsoft.Json.JsonConverter"/>.</value>
</member>
<member name="P:Newtonsoft.Json.JsonConverterAttribute.ConverterParameters">
<summary>
The parameter list to use when constructing the JsonConverter described by ConverterType.
The parameter list to use when constructing the <see cref="T:Newtonsoft.Json.JsonConverter"/> described by ConverterType.
If null, the default constructor is used.
</summary>
</member>
@@ -5313,14 +5440,14 @@
<summary>
Initializes a new instance of the <see cref="T:Newtonsoft.Json.JsonConverterAttribute"/> class.
</summary>
<param name="converterType">Type of the converter.</param>
<param name="converterType">Type of the <see cref="T:Newtonsoft.Json.JsonConverter"/>.</param>
</member>
<member name="M:Newtonsoft.Json.JsonConverterAttribute.#ctor(System.Type,System.Object[])">
<summary>
Initializes a new instance of the <see cref="T:Newtonsoft.Json.JsonConverterAttribute"/> class.
</summary>
<param name="converterType">Type of the converter.</param>
<param name="converterParameters">Parameter list to use when constructing the JsonConverter. Can be null.</param>
<param name="converterType">Type of the <see cref="T:Newtonsoft.Json.JsonConverter"/>.</param>
<param name="converterParameters">Parameter list to use when constructing the <see cref="T:Newtonsoft.Json.JsonConverter"/>. Can be null.</param>
</member>
<member name="T:Newtonsoft.Json.JsonObjectAttribute">
<summary>
@@ -5672,7 +5799,7 @@
Reads the next JSON token from the stream.
</summary>
<returns>
true if the next token was read successfully; false if there are no more tokens to read.
<c>true</c> if the next token was read successfully; <c>false</c> if there are no more tokens to read.
</returns>
</member>
<member name="T:Newtonsoft.Json.MemberSerialization">
@@ -5688,7 +5815,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="T:System.Runtime.Serialization.DataMemberAttribute"/> are serialized.
Only members marked with <see cref="T:Newtonsoft.Json.JsonPropertyAttribute"/> or <see cref="T:System.Runtime.Serialization.DataMemberAttribute"/> are serialized.
This member serialization mode can also be set by marking the class with <see cref="T:System.Runtime.Serialization.DataContractAttribute"/>.
</summary>
</member>
@@ -5740,7 +5867,7 @@
Reads the next JSON token from the stream.
</summary>
<returns>
true if the next token was read successfully; false if there are no more tokens to read.
<c>true</c> if the next token was read successfully; <c>false</c> if there are no more tokens to read.
</returns>
</member>
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsInt32">
@@ -5827,21 +5954,38 @@
</member>
<member name="P:Newtonsoft.Json.JsonPropertyAttribute.ItemConverterType">
<summary>
Gets or sets the converter used when serializing the property's collection items.
Gets or sets the <see cref="T:Newtonsoft.Json.JsonConverter"/> used when serializing the property's collection items.
</summary>
<value>The collection's items converter.</value>
<value>The collection's items <see cref="T:Newtonsoft.Json.JsonConverter"/>.</value>
</member>
<member name="P:Newtonsoft.Json.JsonPropertyAttribute.ItemConverterParameters">
<summary>
The parameter list to use when constructing the JsonConverter described by ItemConverterType.
The parameter list to use when constructing the <see cref="T:Newtonsoft.Json.JsonConverter"/> described by ItemConverterType.
If null, the default constructor is used.
When non-null, there must be a constructor defined in the JsonConverter that exactly matches the number,
When non-null, there must be a constructor defined in the <see cref="T:Newtonsoft.Json.JsonConverter"/> that exactly matches the number,
order, and type of these parameters.
</summary>
<example>
[JsonProperty(ItemConverterType = typeof(MyContainerConverter), ItemConverterParameters = new object[] { 123, "Four" })]
</example>
</member>
<member name="P:Newtonsoft.Json.JsonPropertyAttribute.NamingStrategyType">
<summary>
Gets or sets the <see cref="T:System.Type"/> of the <see cref="T:Newtonsoft.Json.Serialization.NamingStrategy"/>.
</summary>
<value>The <see cref="T:System.Type"/> of the <see cref="T:Newtonsoft.Json.Serialization.NamingStrategy"/>.</value>
</member>
<member name="P:Newtonsoft.Json.JsonPropertyAttribute.NamingStrategyParameters">
<summary>
The parameter list to use when constructing the <see cref="T:Newtonsoft.Json.Serialization.NamingStrategy"/> described by NamingStrategyType.
If null, the default constructor is used.
When non-null, there must be a constructor defined in the <see cref="T:Newtonsoft.Json.Serialization.NamingStrategy"/> that exactly matches the number,
order, and type of these parameters.
</summary>
<example>
[JsonProperty(NamingStrategyType = typeof(MyNamingStrategy), NamingStrategyParameters = new object[] { 123, "Four" })]
</example>
</member>
<member name="P:Newtonsoft.Json.JsonPropertyAttribute.NullValueHandling">
<summary>
Gets or sets the null value handling used when serializing this property.
@@ -6517,7 +6661,7 @@
<summary>
Reads the next JSON token from the stream.
</summary>
<returns>true if the next token was read successfully; false if there are no more tokens to read.</returns>
<returns><c>true</c> if the next token was read successfully; <c>false</c> if there are no more tokens to read.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonReader.ReadAsInt32">
<summary>

View File

@@ -64,7 +64,7 @@
Reads the next JSON token from the stream.
</summary>
<returns>
true if the next token was read successfully; false if there are no more tokens to read.
<c>true</c> if the next token was read successfully; <c>false</c> if there are no more tokens to read.
</returns>
</member>
<member name="M:Newtonsoft.Json.Bson.BsonReader.Close">
@@ -699,6 +699,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.
@@ -870,7 +876,7 @@
</summary>
<param name="attributeName">Attribute name to test.</param>
<param name="prefix">The attribute name prefix if it has one, otherwise an empty string.</param>
<returns>True if attribute name is for a namespace attribute, otherwise false.</returns>
<returns><c>true</c> if attribute name is for a namespace attribute, otherwise <c>false</c>.</returns>
</member>
<member name="M:Newtonsoft.Json.Converters.XmlNodeConverter.CanConvert(System.Type)">
<summary>
@@ -888,12 +894,12 @@
</member>
<member name="F:Newtonsoft.Json.ConstructorHandling.Default">
<summary>
First attempt to use the public default constructor, then fall back to single paramatized constructor, then the non-public default constructor.
First attempt to use the public default constructor, then fall back to single parameterized constructor, then the non-public default constructor.
</summary>
</member>
<member name="F:Newtonsoft.Json.ConstructorHandling.AllowNonPublicDefaultConstructor">
<summary>
Json.NET will use a non-public default constructor before falling back to a paramatized constructor.
Json.NET will use a non-public default constructor before falling back to a parameterized constructor.
</summary>
</member>
<member name="T:Newtonsoft.Json.FloatFormatHandling">
@@ -1173,6 +1179,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.
@@ -1190,6 +1202,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.JRaw">
<summary>
Represents a raw JSON string.
@@ -1230,10 +1257,9 @@
When overridden in a derived class, returns whether resetting an object changes its value.
</summary>
<returns>
true if resetting the component changes its value; otherwise, false.
<c>true</c> if resetting the component changes its value; otherwise, <c>false</c>.
</returns>
<param name="component">The component to test for reset capability.
</param>
<param name="component">The component to test for reset capability.</param>
</member>
<member name="M:Newtonsoft.Json.Linq.JPropertyDescriptor.GetValue(System.Object)">
<summary>
@@ -1265,10 +1291,9 @@
When overridden in a derived class, determines a value indicating whether the value of this property needs to be persisted.
</summary>
<returns>
true if the property should be persisted; otherwise, false.
<c>true</c> if the property should be persisted; otherwise, <c>false</c>.
</returns>
<param name="component">The component with the property to be examined for persistence.
</param>
<param name="component">The component with the property to be examined for persistence.</param>
</member>
<member name="P:Newtonsoft.Json.Linq.JPropertyDescriptor.ComponentType">
<summary>
@@ -1283,7 +1308,7 @@
When overridden in a derived class, gets a value indicating whether this property is read-only.
</summary>
<returns>
true if the property is read-only; otherwise, false.
<c>true</c> if the property is read-only; otherwise, <c>false</c>.
</returns>
</member>
<member name="P:Newtonsoft.Json.Linq.JPropertyDescriptor.PropertyType">
@@ -1327,7 +1352,7 @@
<param name="x">The first object of type <see cref="T:Newtonsoft.Json.Linq.JToken"/> to compare.</param>
<param name="y">The second object of type <see cref="T:Newtonsoft.Json.Linq.JToken"/> to compare.</param>
<returns>
true if the specified objects are equal; otherwise, false.
<c>true</c> if the specified objects are equal; otherwise, <c>false</c>.
</returns>
</member>
<member name="M:Newtonsoft.Json.Linq.JTokenEqualityComparer.GetHashCode(Newtonsoft.Json.Linq.JToken)">
@@ -1926,7 +1951,7 @@
<param name="propertyName">Name of the property.</param>
<param name="value">The value.</param>
<param name="comparison">One of the enumeration values that specifies how the strings will be compared.</param>
<returns>true if a value was successfully retrieved; otherwise, false.</returns>
<returns><c>true</c> if a value was successfully retrieved; otherwise, <c>false</c>.</returns>
</member>
<member name="M:Newtonsoft.Json.Linq.JObject.Add(System.String,Newtonsoft.Json.Linq.JToken)">
<summary>
@@ -1940,7 +1965,7 @@
Removes the property with the specified name.
</summary>
<param name="propertyName">Name of the property.</param>
<returns>true if item was successfully removed; otherwise, false.</returns>
<returns><c>true</c> if item was successfully removed; otherwise, <c>false</c>.</returns>
</member>
<member name="M:Newtonsoft.Json.Linq.JObject.TryGetValue(System.String,Newtonsoft.Json.Linq.JToken@)">
<summary>
@@ -1948,7 +1973,7 @@
</summary>
<param name="propertyName">Name of the property.</param>
<param name="value">The value.</param>
<returns>true if a value was successfully retrieved; otherwise, false.</returns>
<returns><c>true</c> if a value was successfully retrieved; otherwise, <c>false</c>.</returns>
</member>
<member name="M:Newtonsoft.Json.Linq.JObject.GetEnumerator">
<summary>
@@ -2296,7 +2321,7 @@
Reads the next JSON token from the stream.
</summary>
<returns>
true if the next token was read successfully; false if there are no more tokens to read.
<c>true</c> if the next token was read successfully; <c>false</c> if there are no more tokens to read.
</returns>
</member>
<member name="P:Newtonsoft.Json.Linq.JTokenReader.Path">
@@ -2561,7 +2586,7 @@
</summary>
<param name="t1">The first <see cref="T:Newtonsoft.Json.Linq.JToken"/> to compare.</param>
<param name="t2">The second <see cref="T:Newtonsoft.Json.Linq.JToken"/> to compare.</param>
<returns>true if the tokens are equal; otherwise false.</returns>
<returns><c>true</c> if the tokens are equal; otherwise <c>false</c>.</returns>
</member>
<member name="P:Newtonsoft.Json.Linq.JToken.Next">
<summary>
@@ -3715,9 +3740,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>
@@ -3743,7 +3768,7 @@
Indicates whether the current object is equal to another object of the same type.
</summary>
<returns>
true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
<c>true</c> if the current object is equal to the <paramref name="other"/> parameter; otherwise, <c>false</c>.
</returns>
<param name="other">An object to compare with this object.</param>
</member>
@@ -3753,7 +3778,7 @@
</summary>
<param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
<returns>
true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
<c>true</c> if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, <c>false</c>.
</returns>
<exception cref="T:System.NullReferenceException">
The <paramref name="obj"/> parameter is null.
@@ -3852,6 +3877,47 @@
Do not try to read metadata properties.
</summary>
</member>
<member name="T:Newtonsoft.Json.Serialization.CamelCaseNamingStrategy">
<summary>
A camel case naming strategy.
</summary>
</member>
<member name="M:Newtonsoft.Json.Serialization.CamelCaseNamingStrategy.#ctor(System.Boolean,System.Boolean)">
<summary>
Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.CamelCaseNamingStrategy"/> class.
</summary>
<param name="processDictionaryKeys">
A flag indicating whether dictionary keys should be processed.
</param>
<param name="overrideSpecifiedNames">
A flag indicating whether explicitly specified property names should be processed,
e.g. a property name customized with a <see cref="T:Newtonsoft.Json.JsonPropertyAttribute"/>.
</param>
</member>
<member name="M:Newtonsoft.Json.Serialization.CamelCaseNamingStrategy.#ctor">
<summary>
Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.CamelCaseNamingStrategy"/> class.
</summary>
</member>
<member name="M:Newtonsoft.Json.Serialization.CamelCaseNamingStrategy.ResolvePropertyName(System.String)">
<summary>
Resolves the specified property name.
</summary>
<param name="name">The property name to resolve.</param>
<returns>The resolved property name.</returns>
</member>
<member name="T:Newtonsoft.Json.Serialization.DefaultNamingStrategy">
<summary>
The default naming strategy. Property names and dictionary keys are unchanged.
</summary>
</member>
<member name="M:Newtonsoft.Json.Serialization.DefaultNamingStrategy.ResolvePropertyName(System.String)">
<summary>
Resolves the specified property name.
</summary>
<param name="name">The property name to resolve.</param>
<returns>The resolved property name.</returns>
</member>
<member name="T:Newtonsoft.Json.Serialization.DiagnosticsTraceWriter">
<summary>
Represents a trace writer that writes to the application's <see cref="T:System.Diagnostics.TraceListener"/> instances.
@@ -4019,6 +4085,46 @@
A <see cref="T:System.String"/> of the most recent trace messages.
</returns>
</member>
<member name="T:Newtonsoft.Json.Serialization.NamingStrategy">
<summary>
A base class for resolving how property names and dictionary keys are serialized.
</summary>
</member>
<member name="P:Newtonsoft.Json.Serialization.NamingStrategy.ProcessDictionaryKeys">
<summary>
A flag indicating whether dictionary keys should be processed.
Defaults to <c>false</c>.
</summary>
</member>
<member name="P:Newtonsoft.Json.Serialization.NamingStrategy.OverrideSpecifiedNames">
<summary>
A flag indicating whether explicitly specified property names,
e.g. a property name customized with a <see cref="T:Newtonsoft.Json.JsonPropertyAttribute"/>, should be processed.
Defaults to <c>false</c>.
</summary>
</member>
<member name="M:Newtonsoft.Json.Serialization.NamingStrategy.GetPropertyName(System.String,System.Boolean)">
<summary>
Gets the serialized name for a given property name.
</summary>
<param name="name">The initial property name.</param>
<param name="hasSpecifiedName">A flag indicating whether the property has had a name explicitly specfied.</param>
<returns>The serialized property name.</returns>
</member>
<member name="M:Newtonsoft.Json.Serialization.NamingStrategy.GetDictionaryKey(System.String)">
<summary>
Gets the serialized key for a given dictionary key.
</summary>
<param name="key">The initial dictionary key.</param>
<returns>The serialized dictionary key.</returns>
</member>
<member name="M:Newtonsoft.Json.Serialization.NamingStrategy.ResolvePropertyName(System.String)">
<summary>
Resolves the specified property name.
</summary>
<param name="name">The property name to resolve.</param>
<returns>The resolved property name.</returns>
</member>
<member name="T:Newtonsoft.Json.Serialization.ReflectionAttributeProvider">
<summary>
Provides methods to get attributes from a <see cref="T:System.Type"/>, <see cref="T:System.Reflection.MemberInfo"/>, <see cref="T:System.Reflection.ParameterInfo"/> or <see cref="T:System.Reflection.Assembly"/>.
@@ -4045,6 +4151,35 @@
<param name="inherit">When true, look up the hierarchy chain for the inherited custom attribute.</param>
<returns>A collection of <see cref="T:System.Attribute"/>s, or an empty collection.</returns>
</member>
<member name="T:Newtonsoft.Json.Serialization.SnakeCaseNamingStrategy">
<summary>
A snake case naming strategy.
</summary>
</member>
<member name="M:Newtonsoft.Json.Serialization.SnakeCaseNamingStrategy.#ctor(System.Boolean,System.Boolean)">
<summary>
Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.SnakeCaseNamingStrategy"/> class.
</summary>
<param name="processDictionaryKeys">
A flag indicating whether dictionary keys should be processed.
</param>
<param name="overrideSpecifiedNames">
A flag indicating whether explicitly specified property names should be processed,
e.g. a property name customized with a <see cref="T:Newtonsoft.Json.JsonPropertyAttribute"/>.
</param>
</member>
<member name="M:Newtonsoft.Json.Serialization.SnakeCaseNamingStrategy.#ctor">
<summary>
Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.SnakeCaseNamingStrategy"/> class.
</summary>
</member>
<member name="M:Newtonsoft.Json.Serialization.SnakeCaseNamingStrategy.ResolvePropertyName(System.String)">
<summary>
Resolves the specified property name.
</summary>
<param name="name">The property name to resolve.</param>
<returns>The resolved property name.</returns>
</member>
<member name="T:Newtonsoft.Json.Serialization.JsonDynamicContract">
<summary>
Contract details for a <see cref="T:System.Type"/> used by the <see cref="T:Newtonsoft.Json.JsonSerializer"/>.
@@ -4166,13 +4301,6 @@
Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver"/> class.
</summary>
</member>
<member name="M:Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver.ResolvePropertyName(System.String)">
<summary>
Resolves the name of the property.
</summary>
<param name="propertyName">Name of the property.</param>
<returns>The property name camel cased.</returns>
</member>
<member name="T:Newtonsoft.Json.Serialization.DefaultContractResolver">
<summary>
Used by <see cref="T:Newtonsoft.Json.JsonSerializer"/> to resolves a <see cref="T:Newtonsoft.Json.Serialization.JsonContract"/> for a given <see cref="T:System.Type"/>.
@@ -4217,6 +4345,12 @@
<c>true</c> if the <see cref="T:System.SerializableAttribute"/> attribute will be ignored when serializing and deserializing types; otherwise, <c>false</c>.
</value>
</member>
<member name="P:Newtonsoft.Json.Serialization.DefaultContractResolver.NamingStrategy">
<summary>
Gets or sets the naming strategy used to resolve how property names and dictionary keys are serialized.
</summary>
<value>The naming strategy used to resolve how property names and dictionary keys are serialized.</value>
</member>
<member name="M:Newtonsoft.Json.Serialization.DefaultContractResolver.#ctor">
<summary>
Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.DefaultContractResolver"/> class.
@@ -5048,13 +5182,6 @@
<param name="converterArgs">Optional arguments to pass to an initializing constructor of the JsonConverter.
If null, the default constructor is used.</param>
</member>
<member name="M:Newtonsoft.Json.Serialization.JsonTypeReflector.GetJsonConverterCreator(System.Type)">
<summary>
Create a factory function that can be used to create instances of a JsonConverter described by the
argument type. The returned function can then be used to either invoke the converter's default ctor, or any
parameterized constructors by way of an object array.
</summary>
</member>
<member name="T:Newtonsoft.Json.Serialization.ReflectionValueProvider">
<summary>
Get and set values for a <see cref="T:System.Reflection.MemberInfo"/> using reflection.
@@ -5184,16 +5311,6 @@
<param name="initial">The list to add to.</param>
<param name="collection">The collection of elements to add.</param>
</member>
<member name="M:Newtonsoft.Json.Utilities.CollectionUtils.IndexOf``1(System.Collections.Generic.IEnumerable{``0},``0,System.Collections.Generic.IEqualityComparer{``0})">
<summary>
Returns the index of the first occurrence in a sequence by using a specified IEqualityComparer{TSource}.
</summary>
<typeparam name="TSource">The type of the elements of source.</typeparam>
<param name="list">A sequence in which to locate a value.</param>
<param name="value">The object to locate in the sequence</param>
<param name="comparer">An equality comparer to compare values.</param>
<returns>The zero-based index of the first occurrence of value within the entire sequence, if found; otherwise, 1.</returns>
</member>
<member name="M:Newtonsoft.Json.Utilities.ReflectionUtils.GetCollectionItemType(System.Type)">
<summary>
Gets the type of the typed collection's items.
@@ -5272,13 +5389,6 @@
<c>true</c> if the string is all white space; otherwise, <c>false</c>.
</returns>
</member>
<member name="M:Newtonsoft.Json.Utilities.StringUtils.NullEmptyString(System.String)">
<summary>
Nulls an empty string.
</summary>
<param name="s">The string.</param>
<returns>Null if the string was null, otherwise the string unchanged.</returns>
</member>
<member name="T:Newtonsoft.Json.Required">
<summary>
Indicating whether a property is required.
@@ -5411,21 +5521,38 @@
</member>
<member name="P:Newtonsoft.Json.JsonContainerAttribute.ItemConverterType">
<summary>
Gets the collection's items converter.
Gets or sets the collection's items converter.
</summary>
<value>The collection's items converter.</value>
</member>
<member name="P:Newtonsoft.Json.JsonContainerAttribute.ItemConverterParameters">
<summary>
The parameter list to use when constructing the JsonConverter described by ItemConverterType.
The parameter list to use when constructing the <see cref="T:Newtonsoft.Json.JsonConverter"/> described by ItemConverterType.
If null, the default constructor is used.
When non-null, there must be a constructor defined in the JsonConverter that exactly matches the number,
When non-null, there must be a constructor defined in the <see cref="T:Newtonsoft.Json.JsonConverter"/> that exactly matches the number,
order, and type of these parameters.
</summary>
<example>
[JsonContainer(ItemConverterType = typeof(MyContainerConverter), ItemConverterParameters = new object[] { 123, "Four" })]
</example>
</member>
<member name="P:Newtonsoft.Json.JsonContainerAttribute.NamingStrategyType">
<summary>
Gets or sets the <see cref="T:System.Type"/> of the <see cref="T:Newtonsoft.Json.Serialization.NamingStrategy"/>.
</summary>
<value>The <see cref="T:System.Type"/> of the <see cref="T:Newtonsoft.Json.Serialization.NamingStrategy"/>.</value>
</member>
<member name="P:Newtonsoft.Json.JsonContainerAttribute.NamingStrategyParameters">
<summary>
The parameter list to use when constructing the <see cref="T:Newtonsoft.Json.Serialization.NamingStrategy"/> described by NamingStrategyType.
If null, the default constructor is used.
When non-null, there must be a constructor defined in the <see cref="T:Newtonsoft.Json.Serialization.NamingStrategy"/> that exactly matches the number,
order, and type of these parameters.
</summary>
<example>
[JsonContainer(NamingStrategyType = typeof(MyNamingStrategy), NamingStrategyParameters = new object[] { 123, "Four" })]
</example>
</member>
<member name="P:Newtonsoft.Json.JsonContainerAttribute.IsReference">
<summary>
Gets or sets a value that indicates whether to preserve object references.
@@ -5507,13 +5634,13 @@
</member>
<member name="P:Newtonsoft.Json.JsonConverterAttribute.ConverterType">
<summary>
Gets the <see cref="T:System.Type"/> of the converter.
Gets the <see cref="T:System.Type"/> of the <see cref="T:Newtonsoft.Json.JsonConverter"/>.
</summary>
<value>The <see cref="T:System.Type"/> of the converter.</value>
<value>The <see cref="T:System.Type"/> of the <see cref="T:Newtonsoft.Json.JsonConverter"/>.</value>
</member>
<member name="P:Newtonsoft.Json.JsonConverterAttribute.ConverterParameters">
<summary>
The parameter list to use when constructing the JsonConverter described by ConverterType.
The parameter list to use when constructing the <see cref="T:Newtonsoft.Json.JsonConverter"/> described by ConverterType.
If null, the default constructor is used.
</summary>
</member>
@@ -5521,14 +5648,14 @@
<summary>
Initializes a new instance of the <see cref="T:Newtonsoft.Json.JsonConverterAttribute"/> class.
</summary>
<param name="converterType">Type of the converter.</param>
<param name="converterType">Type of the <see cref="T:Newtonsoft.Json.JsonConverter"/>.</param>
</member>
<member name="M:Newtonsoft.Json.JsonConverterAttribute.#ctor(System.Type,System.Object[])">
<summary>
Initializes a new instance of the <see cref="T:Newtonsoft.Json.JsonConverterAttribute"/> class.
</summary>
<param name="converterType">Type of the converter.</param>
<param name="converterParameters">Parameter list to use when constructing the JsonConverter. Can be null.</param>
<param name="converterType">Type of the <see cref="T:Newtonsoft.Json.JsonConverter"/>.</param>
<param name="converterParameters">Parameter list to use when constructing the <see cref="T:Newtonsoft.Json.JsonConverter"/>. Can be null.</param>
</member>
<member name="T:Newtonsoft.Json.JsonObjectAttribute">
<summary>
@@ -5880,7 +6007,7 @@
Reads the next JSON token from the stream.
</summary>
<returns>
true if the next token was read successfully; false if there are no more tokens to read.
<c>true</c> if the next token was read successfully; <c>false</c> if there are no more tokens to read.
</returns>
</member>
<member name="T:Newtonsoft.Json.MemberSerialization">
@@ -5896,7 +6023,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="T:System.Runtime.Serialization.DataMemberAttribute"/> are serialized.
Only members marked with <see cref="T:Newtonsoft.Json.JsonPropertyAttribute"/> or <see cref="T:System.Runtime.Serialization.DataMemberAttribute"/> are serialized.
This member serialization mode can also be set by marking the class with <see cref="T:System.Runtime.Serialization.DataContractAttribute"/>.
</summary>
</member>
@@ -5948,7 +6075,7 @@
Reads the next JSON token from the stream.
</summary>
<returns>
true if the next token was read successfully; false if there are no more tokens to read.
<c>true</c> if the next token was read successfully; <c>false</c> if there are no more tokens to read.
</returns>
</member>
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsInt32">
@@ -6035,21 +6162,38 @@
</member>
<member name="P:Newtonsoft.Json.JsonPropertyAttribute.ItemConverterType">
<summary>
Gets or sets the converter used when serializing the property's collection items.
Gets or sets the <see cref="T:Newtonsoft.Json.JsonConverter"/> used when serializing the property's collection items.
</summary>
<value>The collection's items converter.</value>
<value>The collection's items <see cref="T:Newtonsoft.Json.JsonConverter"/>.</value>
</member>
<member name="P:Newtonsoft.Json.JsonPropertyAttribute.ItemConverterParameters">
<summary>
The parameter list to use when constructing the JsonConverter described by ItemConverterType.
The parameter list to use when constructing the <see cref="T:Newtonsoft.Json.JsonConverter"/> described by ItemConverterType.
If null, the default constructor is used.
When non-null, there must be a constructor defined in the JsonConverter that exactly matches the number,
When non-null, there must be a constructor defined in the <see cref="T:Newtonsoft.Json.JsonConverter"/> that exactly matches the number,
order, and type of these parameters.
</summary>
<example>
[JsonProperty(ItemConverterType = typeof(MyContainerConverter), ItemConverterParameters = new object[] { 123, "Four" })]
</example>
</member>
<member name="P:Newtonsoft.Json.JsonPropertyAttribute.NamingStrategyType">
<summary>
Gets or sets the <see cref="T:System.Type"/> of the <see cref="T:Newtonsoft.Json.Serialization.NamingStrategy"/>.
</summary>
<value>The <see cref="T:System.Type"/> of the <see cref="T:Newtonsoft.Json.Serialization.NamingStrategy"/>.</value>
</member>
<member name="P:Newtonsoft.Json.JsonPropertyAttribute.NamingStrategyParameters">
<summary>
The parameter list to use when constructing the <see cref="T:Newtonsoft.Json.Serialization.NamingStrategy"/> described by NamingStrategyType.
If null, the default constructor is used.
When non-null, there must be a constructor defined in the <see cref="T:Newtonsoft.Json.Serialization.NamingStrategy"/> that exactly matches the number,
order, and type of these parameters.
</summary>
<example>
[JsonProperty(NamingStrategyType = typeof(MyNamingStrategy), NamingStrategyParameters = new object[] { 123, "Four" })]
</example>
</member>
<member name="P:Newtonsoft.Json.JsonPropertyAttribute.NullValueHandling">
<summary>
Gets or sets the null value handling used when serializing this property.
@@ -6725,7 +6869,7 @@
<summary>
Reads the next JSON token from the stream.
</summary>
<returns>true if the next token was read successfully; false if there are no more tokens to read.</returns>
<returns><c>true</c> if the next token was read successfully; <c>false</c> if there are no more tokens to read.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonReader.ReadAsInt32">
<summary>

View File

@@ -81,7 +81,7 @@
Reads the next JSON token from the stream.
</summary>
<returns>
true if the next token was read successfully; false if there are no more tokens to read.
<c>true</c> if the next token was read successfully; <c>false</c> if there are no more tokens to read.
</returns>
</member>
<member name="M:Newtonsoft.Json.Bson.BsonReader.Close">
@@ -326,12 +326,12 @@
</member>
<member name="F:Newtonsoft.Json.ConstructorHandling.Default">
<summary>
First attempt to use the public default constructor, then fall back to single paramatized constructor, then the non-public default constructor.
First attempt to use the public default constructor, then fall back to single parameterized constructor, then the non-public default constructor.
</summary>
</member>
<member name="F:Newtonsoft.Json.ConstructorHandling.AllowNonPublicDefaultConstructor">
<summary>
Json.NET will use a non-public default constructor before falling back to a paramatized constructor.
Json.NET will use a non-public default constructor before falling back to a parameterized constructor.
</summary>
</member>
<member name="T:Newtonsoft.Json.Converters.BinaryConverter">
@@ -778,6 +778,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.
@@ -885,7 +891,7 @@
</summary>
<param name="attributeName">Attribute name to test.</param>
<param name="prefix">The attribute name prefix if it has one, otherwise an empty string.</param>
<returns>True if attribute name is for a namespace attribute, otherwise false.</returns>
<returns><c>true</c> if attribute name is for a namespace attribute, otherwise <c>false</c>.</returns>
</member>
<member name="M:Newtonsoft.Json.Converters.XmlNodeConverter.CanConvert(System.Type)">
<summary>
@@ -1146,21 +1152,38 @@
</member>
<member name="P:Newtonsoft.Json.JsonContainerAttribute.ItemConverterType">
<summary>
Gets the collection's items converter.
Gets or sets the collection's items converter.
</summary>
<value>The collection's items converter.</value>
</member>
<member name="P:Newtonsoft.Json.JsonContainerAttribute.ItemConverterParameters">
<summary>
The parameter list to use when constructing the JsonConverter described by ItemConverterType.
The parameter list to use when constructing the <see cref="T:Newtonsoft.Json.JsonConverter"/> described by ItemConverterType.
If null, the default constructor is used.
When non-null, there must be a constructor defined in the JsonConverter that exactly matches the number,
When non-null, there must be a constructor defined in the <see cref="T:Newtonsoft.Json.JsonConverter"/> that exactly matches the number,
order, and type of these parameters.
</summary>
<example>
[JsonContainer(ItemConverterType = typeof(MyContainerConverter), ItemConverterParameters = new object[] { 123, "Four" })]
</example>
</member>
<member name="P:Newtonsoft.Json.JsonContainerAttribute.NamingStrategyType">
<summary>
Gets or sets the <see cref="T:System.Type"/> of the <see cref="T:Newtonsoft.Json.Serialization.NamingStrategy"/>.
</summary>
<value>The <see cref="T:System.Type"/> of the <see cref="T:Newtonsoft.Json.Serialization.NamingStrategy"/>.</value>
</member>
<member name="P:Newtonsoft.Json.JsonContainerAttribute.NamingStrategyParameters">
<summary>
The parameter list to use when constructing the <see cref="T:Newtonsoft.Json.Serialization.NamingStrategy"/> described by NamingStrategyType.
If null, the default constructor is used.
When non-null, there must be a constructor defined in the <see cref="T:Newtonsoft.Json.Serialization.NamingStrategy"/> that exactly matches the number,
order, and type of these parameters.
</summary>
<example>
[JsonContainer(NamingStrategyType = typeof(MyNamingStrategy), NamingStrategyParameters = new object[] { 123, "Four" })]
</example>
</member>
<member name="P:Newtonsoft.Json.JsonContainerAttribute.IsReference">
<summary>
Gets or sets a value that indicates whether to preserve object references.
@@ -1912,13 +1935,13 @@
</member>
<member name="P:Newtonsoft.Json.JsonConverterAttribute.ConverterType">
<summary>
Gets the <see cref="T:System.Type"/> of the converter.
Gets the <see cref="T:System.Type"/> of the <see cref="T:Newtonsoft.Json.JsonConverter"/>.
</summary>
<value>The <see cref="T:System.Type"/> of the converter.</value>
<value>The <see cref="T:System.Type"/> of the <see cref="T:Newtonsoft.Json.JsonConverter"/>.</value>
</member>
<member name="P:Newtonsoft.Json.JsonConverterAttribute.ConverterParameters">
<summary>
The parameter list to use when constructing the JsonConverter described by ConverterType.
The parameter list to use when constructing the <see cref="T:Newtonsoft.Json.JsonConverter"/> described by ConverterType.
If null, the default constructor is used.
</summary>
</member>
@@ -1926,14 +1949,14 @@
<summary>
Initializes a new instance of the <see cref="T:Newtonsoft.Json.JsonConverterAttribute"/> class.
</summary>
<param name="converterType">Type of the converter.</param>
<param name="converterType">Type of the <see cref="T:Newtonsoft.Json.JsonConverter"/>.</param>
</member>
<member name="M:Newtonsoft.Json.JsonConverterAttribute.#ctor(System.Type,System.Object[])">
<summary>
Initializes a new instance of the <see cref="T:Newtonsoft.Json.JsonConverterAttribute"/> class.
</summary>
<param name="converterType">Type of the converter.</param>
<param name="converterParameters">Parameter list to use when constructing the JsonConverter. Can be null.</param>
<param name="converterType">Type of the <see cref="T:Newtonsoft.Json.JsonConverter"/>.</param>
<param name="converterParameters">Parameter list to use when constructing the <see cref="T:Newtonsoft.Json.JsonConverter"/>. Can be null.</param>
</member>
<member name="T:Newtonsoft.Json.JsonConverterCollection">
<summary>
@@ -2065,21 +2088,38 @@
</member>
<member name="P:Newtonsoft.Json.JsonPropertyAttribute.ItemConverterType">
<summary>
Gets or sets the converter used when serializing the property's collection items.
Gets or sets the <see cref="T:Newtonsoft.Json.JsonConverter"/> used when serializing the property's collection items.
</summary>
<value>The collection's items converter.</value>
<value>The collection's items <see cref="T:Newtonsoft.Json.JsonConverter"/>.</value>
</member>
<member name="P:Newtonsoft.Json.JsonPropertyAttribute.ItemConverterParameters">
<summary>
The parameter list to use when constructing the JsonConverter described by ItemConverterType.
The parameter list to use when constructing the <see cref="T:Newtonsoft.Json.JsonConverter"/> described by ItemConverterType.
If null, the default constructor is used.
When non-null, there must be a constructor defined in the JsonConverter that exactly matches the number,
When non-null, there must be a constructor defined in the <see cref="T:Newtonsoft.Json.JsonConverter"/> that exactly matches the number,
order, and type of these parameters.
</summary>
<example>
[JsonProperty(ItemConverterType = typeof(MyContainerConverter), ItemConverterParameters = new object[] { 123, "Four" })]
</example>
</member>
<member name="P:Newtonsoft.Json.JsonPropertyAttribute.NamingStrategyType">
<summary>
Gets or sets the <see cref="T:System.Type"/> of the <see cref="T:Newtonsoft.Json.Serialization.NamingStrategy"/>.
</summary>
<value>The <see cref="T:System.Type"/> of the <see cref="T:Newtonsoft.Json.Serialization.NamingStrategy"/>.</value>
</member>
<member name="P:Newtonsoft.Json.JsonPropertyAttribute.NamingStrategyParameters">
<summary>
The parameter list to use when constructing the <see cref="T:Newtonsoft.Json.Serialization.NamingStrategy"/> described by NamingStrategyType.
If null, the default constructor is used.
When non-null, there must be a constructor defined in the <see cref="T:Newtonsoft.Json.Serialization.NamingStrategy"/> that exactly matches the number,
order, and type of these parameters.
</summary>
<example>
[JsonProperty(NamingStrategyType = typeof(MyNamingStrategy), NamingStrategyParameters = new object[] { 123, "Four" })]
</example>
</member>
<member name="P:Newtonsoft.Json.JsonPropertyAttribute.NullValueHandling">
<summary>
Gets or sets the null value handling used when serializing this property.
@@ -2335,7 +2375,7 @@
<summary>
Reads the next JSON token from the stream.
</summary>
<returns>true if the next token was read successfully; false if there are no more tokens to read.</returns>
<returns><c>true</c> if the next token was read successfully; <c>false</c> if there are no more tokens to read.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonReader.ReadAsInt32">
<summary>
@@ -3041,7 +3081,7 @@
Reads the next JSON token from the stream.
</summary>
<returns>
true if the next token was read successfully; false if there are no more tokens to read.
<c>true</c> if the next token was read successfully; <c>false</c> if there are no more tokens to read.
</returns>
</member>
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsInt32">
@@ -3603,7 +3643,7 @@
Reads the next JSON token from the stream.
</summary>
<returns>
true if the next token was read successfully; false if there are no more tokens to read.
<c>true</c> if the next token was read successfully; <c>false</c> if there are no more tokens to read.
</returns>
</member>
<member name="T:Newtonsoft.Json.JsonWriter">
@@ -4930,7 +4970,7 @@
<param name="propertyName">Name of the property.</param>
<param name="value">The value.</param>
<param name="comparison">One of the enumeration values that specifies how the strings will be compared.</param>
<returns>true if a value was successfully retrieved; otherwise, false.</returns>
<returns><c>true</c> if a value was successfully retrieved; otherwise, <c>false</c>.</returns>
</member>
<member name="M:Newtonsoft.Json.Linq.JObject.Add(System.String,Newtonsoft.Json.Linq.JToken)">
<summary>
@@ -4944,7 +4984,7 @@
Removes the property with the specified name.
</summary>
<param name="propertyName">Name of the property.</param>
<returns>true if item was successfully removed; otherwise, false.</returns>
<returns><c>true</c> if item was successfully removed; otherwise, <c>false</c>.</returns>
</member>
<member name="M:Newtonsoft.Json.Linq.JObject.TryGetValue(System.String,Newtonsoft.Json.Linq.JToken@)">
<summary>
@@ -4952,7 +4992,7 @@
</summary>
<param name="propertyName">Name of the property.</param>
<param name="value">The value.</param>
<returns>true if a value was successfully retrieved; otherwise, false.</returns>
<returns><c>true</c> if a value was successfully retrieved; otherwise, <c>false</c>.</returns>
</member>
<member name="M:Newtonsoft.Json.Linq.JObject.GetEnumerator">
<summary>
@@ -5094,6 +5134,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.JProperty">
<summary>
Represents a JSON property.
@@ -5182,10 +5228,9 @@
When overridden in a derived class, returns whether resetting an object changes its value.
</summary>
<returns>
true if resetting the component changes its value; otherwise, false.
<c>true</c> if resetting the component changes its value; otherwise, <c>false</c>.
</returns>
<param name="component">The component to test for reset capability.
</param>
<param name="component">The component to test for reset capability.</param>
</member>
<member name="M:Newtonsoft.Json.Linq.JPropertyDescriptor.GetValue(System.Object)">
<summary>
@@ -5217,10 +5262,9 @@
When overridden in a derived class, determines a value indicating whether the value of this property needs to be persisted.
</summary>
<returns>
true if the property should be persisted; otherwise, false.
<c>true</c> if the property should be persisted; otherwise, <c>false</c>.
</returns>
<param name="component">The component with the property to be examined for persistence.
</param>
<param name="component">The component with the property to be examined for persistence.</param>
</member>
<member name="P:Newtonsoft.Json.Linq.JPropertyDescriptor.ComponentType">
<summary>
@@ -5235,7 +5279,7 @@
When overridden in a derived class, gets a value indicating whether this property is read-only.
</summary>
<returns>
true if the property is read-only; otherwise, false.
<c>true</c> if the property is read-only; otherwise, <c>false</c>.
</returns>
</member>
<member name="P:Newtonsoft.Json.Linq.JPropertyDescriptor.PropertyType">
@@ -5322,7 +5366,7 @@
</summary>
<param name="t1">The first <see cref="T:Newtonsoft.Json.Linq.JToken"/> to compare.</param>
<param name="t2">The second <see cref="T:Newtonsoft.Json.Linq.JToken"/> to compare.</param>
<returns>true if the tokens are equal; otherwise false.</returns>
<returns><c>true</c> if the tokens are equal; otherwise <c>false</c>.</returns>
</member>
<member name="P:Newtonsoft.Json.Linq.JToken.Next">
<summary>
@@ -6196,7 +6240,7 @@
<param name="x">The first object of type <see cref="T:Newtonsoft.Json.Linq.JToken"/> to compare.</param>
<param name="y">The second object of type <see cref="T:Newtonsoft.Json.Linq.JToken"/> to compare.</param>
<returns>
true if the specified objects are equal; otherwise, false.
<c>true</c> if the specified objects are equal; otherwise, <c>false</c>.
</returns>
</member>
<member name="M:Newtonsoft.Json.Linq.JTokenEqualityComparer.GetHashCode(Newtonsoft.Json.Linq.JToken)">
@@ -6228,7 +6272,7 @@
Reads the next JSON token from the stream.
</summary>
<returns>
true if the next token was read successfully; false if there are no more tokens to read.
<c>true</c> if the next token was read successfully; <c>false</c> if there are no more tokens to read.
</returns>
</member>
<member name="P:Newtonsoft.Json.Linq.JTokenReader.Path">
@@ -6670,9 +6714,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>
@@ -6698,7 +6742,7 @@
Indicates whether the current object is equal to another object of the same type.
</summary>
<returns>
true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
<c>true</c> if the current object is equal to the <paramref name="other"/> parameter; otherwise, <c>false</c>.
</returns>
<param name="other">An object to compare with this object.</param>
</member>
@@ -6708,7 +6752,7 @@
</summary>
<param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
<returns>
true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
<c>true</c> if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, <c>false</c>.
</returns>
<exception cref="T:System.NullReferenceException">
The <paramref name="obj"/> parameter is null.
@@ -6821,6 +6865,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.MemberSerialization">
<summary>
Specifies the member serialization options for the <see cref="T:Newtonsoft.Json.JsonSerializer"/>.
@@ -6834,7 +6893,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="T:System.Runtime.Serialization.DataMemberAttribute"/> are serialized.
Only members marked with <see cref="T:Newtonsoft.Json.JsonPropertyAttribute"/> or <see cref="T:System.Runtime.Serialization.DataMemberAttribute"/> are serialized.
This member serialization mode can also be set by marking the class with <see cref="T:System.Runtime.Serialization.DataContractAttribute"/>.
</summary>
</member>
@@ -7577,6 +7636,64 @@
</note>
</summary>
</member>
<member name="T:Newtonsoft.Json.Serialization.SnakeCaseNamingStrategy">
<summary>
A snake case naming strategy.
</summary>
</member>
<member name="M:Newtonsoft.Json.Serialization.SnakeCaseNamingStrategy.#ctor(System.Boolean,System.Boolean)">
<summary>
Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.SnakeCaseNamingStrategy"/> class.
</summary>
<param name="processDictionaryKeys">
A flag indicating whether dictionary keys should be processed.
</param>
<param name="overrideSpecifiedNames">
A flag indicating whether explicitly specified property names should be processed,
e.g. a property name customized with a <see cref="T:Newtonsoft.Json.JsonPropertyAttribute"/>.
</param>
</member>
<member name="M:Newtonsoft.Json.Serialization.SnakeCaseNamingStrategy.#ctor">
<summary>
Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.SnakeCaseNamingStrategy"/> class.
</summary>
</member>
<member name="M:Newtonsoft.Json.Serialization.SnakeCaseNamingStrategy.ResolvePropertyName(System.String)">
<summary>
Resolves the specified property name.
</summary>
<param name="name">The property name to resolve.</param>
<returns>The resolved property name.</returns>
</member>
<member name="T:Newtonsoft.Json.Serialization.CamelCaseNamingStrategy">
<summary>
A camel case naming strategy.
</summary>
</member>
<member name="M:Newtonsoft.Json.Serialization.CamelCaseNamingStrategy.#ctor(System.Boolean,System.Boolean)">
<summary>
Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.CamelCaseNamingStrategy"/> class.
</summary>
<param name="processDictionaryKeys">
A flag indicating whether dictionary keys should be processed.
</param>
<param name="overrideSpecifiedNames">
A flag indicating whether explicitly specified property names should be processed,
e.g. a property name customized with a <see cref="T:Newtonsoft.Json.JsonPropertyAttribute"/>.
</param>
</member>
<member name="M:Newtonsoft.Json.Serialization.CamelCaseNamingStrategy.#ctor">
<summary>
Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.CamelCaseNamingStrategy"/> class.
</summary>
</member>
<member name="M:Newtonsoft.Json.Serialization.CamelCaseNamingStrategy.ResolvePropertyName(System.String)">
<summary>
Resolves the specified property name.
</summary>
<param name="name">The property name to resolve.</param>
<returns>The resolved property name.</returns>
</member>
<member name="T:Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver">
<summary>
Resolves member mappings for a type, camel casing property names.
@@ -7587,13 +7704,6 @@
Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver"/> class.
</summary>
</member>
<member name="M:Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver.ResolvePropertyName(System.String)">
<summary>
Resolves the name of the property.
</summary>
<param name="propertyName">Name of the property.</param>
<returns>The property name camel cased.</returns>
</member>
<member name="T:Newtonsoft.Json.Serialization.DefaultContractResolver">
<summary>
Used by <see cref="T:Newtonsoft.Json.JsonSerializer"/> to resolves a <see cref="T:Newtonsoft.Json.Serialization.JsonContract"/> for a given <see cref="T:System.Type"/>.
@@ -7638,6 +7748,12 @@
<c>true</c> if the <see cref="T:System.SerializableAttribute"/> attribute will be ignored when serializing and deserializing types; otherwise, <c>false</c>.
</value>
</member>
<member name="P:Newtonsoft.Json.Serialization.DefaultContractResolver.NamingStrategy">
<summary>
Gets or sets the naming strategy used to resolve how property names and dictionary keys are serialized.
</summary>
<value>The naming strategy used to resolve how property names and dictionary keys are serialized.</value>
</member>
<member name="M:Newtonsoft.Json.Serialization.DefaultContractResolver.#ctor">
<summary>
Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.DefaultContractResolver"/> class.
@@ -7798,6 +7914,18 @@
<param name="propertyName">Name of the property.</param>
<returns>Name of the property.</returns>
</member>
<member name="T:Newtonsoft.Json.Serialization.DefaultNamingStrategy">
<summary>
The default naming strategy. Property names and dictionary keys are unchanged.
</summary>
</member>
<member name="M:Newtonsoft.Json.Serialization.DefaultNamingStrategy.ResolvePropertyName(System.String)">
<summary>
Resolves the specified property name.
</summary>
<param name="name">The property name to resolve.</param>
<returns>The resolved property name.</returns>
</member>
<member name="T:Newtonsoft.Json.Serialization.DefaultSerializationBinder">
<summary>
The default serialization binder used when resolving and loading classes from type names.
@@ -7989,6 +8117,46 @@
<param name="type">The type to resolve a contract for.</param>
<returns>The contract for a given type.</returns>
</member>
<member name="T:Newtonsoft.Json.Serialization.NamingStrategy">
<summary>
A base class for resolving how property names and dictionary keys are serialized.
</summary>
</member>
<member name="P:Newtonsoft.Json.Serialization.NamingStrategy.ProcessDictionaryKeys">
<summary>
A flag indicating whether dictionary keys should be processed.
Defaults to <c>false</c>.
</summary>
</member>
<member name="P:Newtonsoft.Json.Serialization.NamingStrategy.OverrideSpecifiedNames">
<summary>
A flag indicating whether explicitly specified property names,
e.g. a property name customized with a <see cref="T:Newtonsoft.Json.JsonPropertyAttribute"/>, should be processed.
Defaults to <c>false</c>.
</summary>
</member>
<member name="M:Newtonsoft.Json.Serialization.NamingStrategy.GetPropertyName(System.String,System.Boolean)">
<summary>
Gets the serialized name for a given property name.
</summary>
<param name="name">The initial property name.</param>
<param name="hasSpecifiedName">A flag indicating whether the property has had a name explicitly specfied.</param>
<returns>The serialized property name.</returns>
</member>
<member name="M:Newtonsoft.Json.Serialization.NamingStrategy.GetDictionaryKey(System.String)">
<summary>
Gets the serialized key for a given dictionary key.
</summary>
<param name="key">The initial dictionary key.</param>
<returns>The serialized dictionary key.</returns>
</member>
<member name="M:Newtonsoft.Json.Serialization.NamingStrategy.ResolvePropertyName(System.String)">
<summary>
Resolves the specified property name.
</summary>
<param name="name">The property name to resolve.</param>
<returns>The resolved property name.</returns>
</member>
<member name="T:Newtonsoft.Json.Serialization.IReferenceResolver">
<summary>
Used to resolve references when serializing and deserializing JSON by the <see cref="T:Newtonsoft.Json.JsonSerializer"/>.
@@ -8704,13 +8872,6 @@
<param name="converterArgs">Optional arguments to pass to an initializing constructor of the JsonConverter.
If null, the default constructor is used.</param>
</member>
<member name="M:Newtonsoft.Json.Serialization.JsonTypeReflector.GetJsonConverterCreator(System.Type)">
<summary>
Create a factory function that can be used to create instances of a JsonConverter described by the
argument type. The returned function can then be used to either invoke the converter's default ctor, or any
parameterized constructors by way of an object array.
</summary>
</member>
<member name="T:Newtonsoft.Json.Serialization.MemoryTraceWriter">
<summary>
Represents a trace writer that writes to memory. When the trace message limit is
@@ -8887,16 +9048,6 @@
<param name="initial">The list to add to.</param>
<param name="collection">The collection of elements to add.</param>
</member>
<member name="M:Newtonsoft.Json.Utilities.CollectionUtils.IndexOf``1(System.Collections.Generic.IEnumerable{``0},``0,System.Collections.Generic.IEqualityComparer{``0})">
<summary>
Returns the index of the first occurrence in a sequence by using a specified IEqualityComparer{TSource}.
</summary>
<typeparam name="TSource">The type of the elements of source.</typeparam>
<param name="list">A sequence in which to locate a value.</param>
<param name="value">The object to locate in the sequence</param>
<param name="comparer">An equality comparer to compare values.</param>
<returns>The zero-based index of the first occurrence of value within the entire sequence, if found; otherwise, 1.</returns>
</member>
<member name="M:Newtonsoft.Json.Utilities.ConvertUtils.ConvertOrCast(System.Object,System.Globalization.CultureInfo,System.Type)">
<summary>
Converts the value to the specified type. If the value is unable to be converted, the
@@ -9032,13 +9183,6 @@
<c>true</c> if the string is all white space; otherwise, <c>false</c>.
</returns>
</member>
<member name="M:Newtonsoft.Json.Utilities.StringUtils.NullEmptyString(System.String)">
<summary>
Nulls an empty string.
</summary>
<param name="s">The string.</param>
<returns>Null if the string was null, otherwise the string unchanged.</returns>
</member>
<member name="T:Newtonsoft.Json.WriteState">
<summary>
Specifies the state of the <see cref="T:Newtonsoft.Json.JsonWriter"/>.

Some files were not shown because too many files have changed in this diff Show More