Compare commits

...

276 Commits

Author SHA1 Message Date
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
JustArchi
25da5fb650 Delay main RequestLimiter further, #115 2016-02-22 18:36:56 +01:00
JustArchi
a1e8af0660 Until Mono 4.3 stabilizes, use .NET 4.5.1 2016-02-22 18:35:27 +01:00
JustArchi
af61ad66c6 Big code review 2016-02-22 18:34:45 +01:00
JustArchi
a06af3402a Speedup 2016-02-21 22:21:17 +01:00
JustArchi
08b7f18796 Make Bot.RefreshCMs() not fatal
Works around latest nightly Mono fuckup by the way
2016-02-21 22:19:55 +01:00
JustArchi
6feb1a46cc Misc 2016-02-20 23:00:30 +01:00
JustArchi
3f8e6125d1 Misc 2016-02-20 17:07:05 +01:00
JustArchi
ff887196c3 Add some misc delay between restarts 2016-02-20 02:44:01 +01:00
JustArchi
3509b1be8c Fix rare unexpected exit 2016-02-20 02:41:43 +01:00
JustArchi
b27aa50083 Misc 2016-02-16 08:33:11 +01:00
JustArchi
45230a57ee Misc 2016-02-16 08:25:27 +01:00
JustArchi
b3e420d423 Bump to 1.6 2016-02-16 08:14:19 +01:00
JustArchi
65d9f4fbee SteamAuth sync 2016-02-16 08:11:07 +01:00
JustArchi
935e11ab2a Enhance !addlicense to multiple games, closes #108 2016-02-16 08:08:06 +01:00
JustArchi
c2cd7ca52d Make use only of trading items, closes #109 2016-02-16 07:58:01 +01:00
Łukasz Domeradzki
120691c6bf Update README.md 2016-02-13 23:41:45 +01:00
JustArchi
334d2df4ef Don't interrupt automatic farming with nonsense requests 2016-02-13 23:31:58 +01:00
JustArchi
1a2616c648 Make !play accept more than one game, #106 2016-02-13 23:21:47 +01:00
JustArchi
9858c0e0c1 Code review 2016-02-12 16:51:06 +01:00
JustArchi
7f19d04e93 Add !leave 2016-02-12 16:21:33 +01:00
JustArchi
8ba497855b Misc 2016-02-12 16:07:59 +01:00
JustArchi
7ef8b224eb Misc 2016-02-12 16:06:57 +01:00
JustArchi
a841b9494a Fix SteamParental crash 2016-02-10 02:42:05 +01:00
JustArchi
856351aea0 Fix infinite loop 2016-02-09 10:26:51 +01:00
JustArchi
6a172ce269 Schedule semaphore release in background 2016-02-05 20:15:28 +01:00
JustArchi
f86bc32f0d Misc fix 2016-02-05 20:11:04 +01:00
JustArchi
18800ff8d5 Major cleanup and improvements, closes #100 2016-02-05 20:10:15 +01:00
JustArchi
3b610432ec Decode game names, closes #99 2016-02-05 18:19:06 +01:00
JustArchi
b20423e415 Misc 2016-02-03 21:53:30 +01:00
JustArchi
e4189b2bc4 Don't bother with nice shutdown, closes #94
We really don't need any of that when we're exiting process anyway, framework does that better
2016-02-03 05:57:58 +01:00
JustArchi
c5ac0196de Do not shutdown when WCF is active 2016-02-03 05:46:44 +01:00
JustArchi
8934e004fb Add StartOnLaunch property 2016-02-03 05:43:50 +01:00
JustArchi
ed0fd3df78 Misc 2016-01-29 19:20:00 +01:00
JustArchi
bd19cc794a Use .NET 4.6.1 2016-01-29 00:53:35 +01:00
JustArchi
c52a4be7e7 EXPERIMENTAL: Use .NET 4.6 2016-01-28 23:58:41 +01:00
JustArchi
7ae29aa122 SteamAuth sync 2016-01-28 23:54:25 +01:00
JustArchi
49d213290f Limit inventory requests, #88 2016-01-26 23:05:53 +01:00
JustArchi
fc618cb24f This is redundant now 2016-01-25 20:10:13 +01:00
JustArchi
2826fdb475 Catch all exceptions instead, #85 2016-01-25 20:08:30 +01:00
JustArchi
b0e990f9f3 Misc 2016-01-25 19:17:01 +01:00
JustArchi
6a7a568e59 Misc style fixes 2016-01-25 19:15:22 +01:00
Łukasz Domeradzki
547be25b57 Merge pull request #87 from Ryzhehvost/distribute
Distribute keys among bots
2016-01-25 19:09:15 +01:00
Ryzhehvost
9559bcb3b8 Distribute keys among bots 2016-01-25 09:42:59 +02:00
JustArchi
049b78eb02 Rewrite logging a bit 2016-01-24 18:08:27 +01:00
JustArchi
d55c734718 CMs refresh has to be done only once 2016-01-24 17:38:45 +01:00
JustArchi
79b6fc8b17 Fix SteamDirectory crash, closes #85 2016-01-24 17:31:41 +01:00
JustArchi
8603e7a579 Only for loops need a copy
I knew something is fishy here, https://blogs.msdn.microsoft.com/ericlippert/2009/11/12/closing-over-the-loop-variable-considered-harmful/
2016-01-24 03:58:19 +01:00
JustArchi
ea1b401228 Derp, it's late 2016-01-24 00:00:27 +01:00
JustArchi
ee73a12440 Do not send more than 5 trade offers at once, #81 2016-01-23 23:57:46 +01:00
JustArchi
cad47368ac Bump to 1.5 2016-01-23 23:55:52 +01:00
JustArchi
50c7d32ed7 Bump to 1.4.1 2016-01-23 23:45:49 +01:00
JustArchi
eab136f635 Remove debug I added 2016-01-23 23:45:22 +01:00
JustArchi
ae829d7942 Fix .NET being too fast 2016-01-23 23:44:08 +01:00
JustArchi
a3ab01bf0e Set MaxItemsPerTrade correctly, closes #81 2016-01-23 22:23:38 +01:00
JustArchi
4913d6c0a0 Speedup mono a bit 2016-01-22 12:26:33 +01:00
JustArchi
8c1ffda944 Mono fixes 2016-01-22 11:59:39 +01:00
JustArchi
8d5d407c90 Mono fixes 2016-01-22 11:46:49 +01:00
JustArchi
af4117690f Derp 2016-01-22 11:41:39 +01:00
JustArchi
f02be5753c ILMerge -> ILRepack, closes #39
Should also work on Linux/OS X now, but I'm too lazy to check
2016-01-22 11:35:54 +01:00
JustArchi
691949eb08 General code review 2016-01-22 11:02:36 +01:00
JustArchi
42758eadc3 General code review 2016-01-22 11:01:33 +01:00
JustArchi
319822d1a1 Add licenses, closes #77 2016-01-22 10:56:06 +01:00
JustArchi
948787f8ba Implement manual farming, closes #78 2016-01-22 10:45:01 +01:00
JustArchi
73b5246a88 ArchiBoT codebase sync 2016-01-22 10:19:19 +01:00
JustArchi
670091c293 Limit EQ size requests, #81 2016-01-22 10:14:18 +01:00
Łukasz Domeradzki
1332d12087 Update README.md 2016-01-16 21:01:51 +01:00
JustArchi
55633c8d14 Redundant 2016-01-16 05:17:59 +01:00
JustArchi
42f47740db Actually call OnFarmingFinished() when there's nothing to farm 2016-01-16 05:17:03 +01:00
JustArchi
aaf10cf8f8 General: ArchiBoT codebase sync 2016-01-16 05:03:15 +01:00
JustArchi
3d54772da5 Hooray for 2016 2016-01-16 04:21:36 +01:00
JustArchi
1b552b305b We don't need double precision here 2016-01-16 04:17:51 +01:00
JustArchi
af93dc5b0e CardsFarmer: Greatly reduce time spent on checking pages and hours 2016-01-16 04:12:10 +01:00
JustArchi
63e1598ebf General: ArchiBoT codebase sync, closes #72 2016-01-14 20:37:01 +01:00
JustArchi
aa2cf6dbe4 General: ArchiBoT codebase sync 2016-01-14 02:48:56 +01:00
JustArchi
d507b40c97 Trading: Further improvements
I even merged some classes together, I'm magician
2016-01-14 02:24:55 +01:00
JustArchi
3df34ece61 Trading: Lots of cleanup and improvements 2016-01-14 01:46:44 +01:00
Łukasz Domeradzki
047af8912f Merge pull request #71 from Ryzhehvost/trade
Bot->Master trades
2016-01-14 00:11:04 +01:00
Ryzhehvost
fdef39887a you want it - you got it 2016-01-13 22:59:57 +02:00
Ryzhehvost
e0d944efc5 Bot->Master trades 2016-01-13 11:17:58 +02:00
JustArchi
74e416394a Validate only non-redeem commands, #69 2016-01-11 04:49:12 +01:00
JustArchi
35fb7c4e7f Improve valid cd-key checks, closes #69 2016-01-10 22:25:15 +01:00
JustArchi
8d06dd90d8 Do not attempt to try sending request to disconnected clients 2016-01-10 20:55:00 +01:00
JustArchi
254b0a7773 Include key in response 2016-01-10 20:53:32 +01:00
JustArchi
3fc51af261 Don't keep trying when key is Duplicated or Invalid 2016-01-10 20:45:53 +01:00
JustArchi
20ea58981d Implement multiple key activations + keys forwarding 2016-01-10 20:36:56 +01:00
JustArchi
f8c1582aeb Misc enhancements 2016-01-10 19:35:03 +01:00
JustArchi
13722ca8f8 Misc style fixes 2016-01-10 19:31:21 +01:00
Łukasz Domeradzki
da3537eba2 Merge pull request #68 from Ryzhehvost/status_all
command !status all to bring them all
2016-01-10 19:28:26 +01:00
Ryzhehvost
67f0b0f8e9 cleanup 2016-01-10 20:22:46 +02:00
Ryzhehvost
08a962e688 statusall 2016-01-10 19:39:12 +02:00
Ryzhehvost
5b711f158d remove excessive else 2016-01-10 18:50:17 +02:00
Ryzhehvost
0ddcf004c1 command !status all to bring them all 2016-01-10 18:34:30 +02:00
JustArchi
4997cc48ca SteamAuth sync 2016-01-09 19:58:50 +01:00
JustArchi
51bcf73e15 Version bump 2016-01-09 19:55:47 +01:00
JustArchi
d7b21ded96 Packages update 2016-01-09 19:55:32 +01:00
JustArchi
daf4efa054 Log also unhandled exceptions 2016-01-09 19:54:08 +01:00
JustArchi
6a12a26612 WCF: Make it possible to launch also non-targetted commands 2016-01-07 22:29:55 +01:00
JustArchi
533058aa3f Always initialize CM list from SteamDirectory, closes #65, thanks to @xPaw 2016-01-07 22:08:06 +01:00
JustArchi
804d1260f2 Misc 2016-01-07 05:46:31 +01:00
JustArchi
78fcf53606 Improve config help a little 2016-01-07 04:18:06 +01:00
JustArchi
ceb641d1fa WCF 2/2 2016-01-04 00:02:18 +01:00
JustArchi
47fdff2993 WCF 1.5/2 2016-01-03 22:23:30 +01:00
JustArchi
1a96a975f9 WCF 1/2
Server part done
2016-01-03 20:36:13 +01:00
JustArchi
cd460c5ec5 Sync more code with ArchiBoT 2016-01-02 17:31:55 +01:00
JustArchi
70b7f9d5dd Misc 2016-01-02 17:23:30 +01:00
JustArchi
f236d6c33f Sync with ArchiBoT, closes #58 2016-01-02 17:22:35 +01:00
JustArchi
356df9098e Update packages 2016-01-02 17:05:39 +01:00
JustArchi
9a8aa1e10d Misc 2016-01-01 12:49:41 +01:00
JustArchi
7aa985154f Always navigate to executable location first 2016-01-01 12:46:49 +01:00
JustArchi
d287674d69 Fix crash 2015-12-28 15:52:58 +01:00
JustArchi
40b1e8f4c2 Update packages 2015-12-28 11:35:14 +01:00
JustArchi
d38fde0359 Fix ASF not farming accounts with more than 6 pages 2015-12-28 11:30:48 +01:00
JustArchi
e6015ca999 Bump 2015-12-25 17:49:15 +01:00
JustArchi
3bcecc6256 Keep reconnecting if logging in to ISteamUserAuth fails 2015-12-25 17:49:00 +01:00
JustArchi
6cf264dcf7 Misc 2015-12-23 16:34:57 +01:00
JustArchi
b12db11956 Bump to 1.2.1 2015-12-23 11:02:54 +01:00
JustArchi
591d665d56 Fix #47 2015-12-23 10:58:14 +01:00
Łukasz Domeradzki
327cae955a Update README.md 2015-12-23 09:45:12 +01:00
96 changed files with 23642 additions and 13065 deletions

12
.gitignore vendored
View File

@@ -4,8 +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
ArchiSteamFarm/log.txt
ArchiSteamFarm/debug/*
# Ignore out
out/
#################
## Eclipse

10
.travis.yml Normal file
View File

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

View File

@@ -1,12 +1,17 @@

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
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -21,6 +26,10 @@ 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
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2"/>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.1"/>
</startup>
</configuration>

View File

@@ -5,7 +5,7 @@
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
Copyright 2015 Łukasz "JustArchi" Domeradzki
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
Contact: JustArchi@JustArchi.net
Licensed under the Apache License, Version 2.0 (the "License");
@@ -27,79 +27,191 @@ using SteamKit2.Internal;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Threading.Tasks;
namespace ArchiSteamFarm {
internal sealed class ArchiHandler : ClientMsgHandler {
/*
____ _ _ _ _
/ ___| __ _ | || || |__ __ _ ___ | | __ ___
| | / _` || || || '_ \ / _` | / __|| |/ // __|
| |___| (_| || || || |_) || (_| || (__ | < \__ \
\____|\__,_||_||_||_.__/ \__,_| \___||_|\_\|___/
*/
internal sealed class NotificationsCallback : CallbackMsg {
internal sealed class Notification {
internal enum ENotificationType : uint {
Unknown = 0,
Trading = 1,
// Only custom below, different than ones available as user_notification_type
Items = 514
}
internal readonly ENotificationType NotificationType;
internal Notification(ENotificationType notificationType) {
NotificationType = notificationType;
}
}
internal readonly List<Notification> Notifications;
internal NotificationsCallback(JobID jobID, CMsgClientUserNotifications msg) {
JobID = jobID;
if (msg == null || msg.notifications == null) {
return;
}
Notifications = new List<Notification>(msg.notifications.Count);
foreach (var notification in msg.notifications) {
Notifications.Add(new Notification((Notification.ENotificationType) notification.user_notification_type));
}
}
internal NotificationsCallback(JobID jobID, CMsgClientItemAnnouncements msg) {
JobID = jobID;
if (msg == null) {
return;
}
if (msg.count_new_items > 0) {
Notifications = new List<Notification>(1) {
new Notification(Notification.ENotificationType.Items)
};
}
}
}
internal sealed class OfflineMessageCallback : CallbackMsg {
internal uint OfflineMessages { get; private set; }
internal List<uint> Users { get; private set; }
internal OfflineMessageCallback(CMsgClientOfflineMessageNotification body) {
OfflineMessages = body.offline_messages;
Users = body.friends_with_offline_messages;
internal readonly uint OfflineMessages;
internal readonly List<uint> Users;
internal OfflineMessageCallback(JobID jobID, CMsgClientOfflineMessageNotification msg) {
JobID = jobID;
if (msg == null) {
return;
}
OfflineMessages = msg.offline_messages;
Users = msg.friends_with_offline_messages;
}
}
internal sealed class PurchaseResponseCallback : CallbackMsg {
internal enum EPurchaseResult {
internal enum EPurchaseResult : int {
Unknown = -1,
OK = 0,
AlreadyOwned = 9,
RegionLockedKey = 13,
RegionLocked = 13,
InvalidKey = 14,
DuplicatedKey = 15,
BaseGameRequired = 24,
OnCooldown = 53
}
internal EResult Result { get; private set; }
internal EPurchaseResult PurchaseResult { get; private set; }
internal KeyValue ReceiptInfo { get; private set; } = new KeyValue();
internal Dictionary<uint, string> Items { get; private set; } = new Dictionary<uint, string>();
internal readonly EResult Result;
internal readonly EPurchaseResult PurchaseResult;
internal readonly KeyValue ReceiptInfo;
internal readonly Dictionary<uint, string> Items;
internal PurchaseResponseCallback(CMsgClientPurchaseResponse body) {
Result = (EResult) body.eresult;
PurchaseResult = (EPurchaseResult) body.purchase_result_details;
internal PurchaseResponseCallback(JobID jobID, CMsgClientPurchaseResponse msg) {
JobID = jobID;
using (MemoryStream ms = new MemoryStream(body.purchase_receipt_info)) {
if (msg == null) {
return;
}
Result = (EResult) msg.eresult;
PurchaseResult = (EPurchaseResult) msg.purchase_result_details;
if (msg.purchase_receipt_info == null) {
return;
}
ReceiptInfo = new KeyValue();
using (MemoryStream ms = new MemoryStream(msg.purchase_receipt_info)) {
if (!ReceiptInfo.TryReadAsBinary(ms)) {
return;
}
foreach (KeyValue lineItem in ReceiptInfo["lineitems"].Children) {
Items.Add((uint) lineItem["PackageID"].AsUnsignedLong(), lineItem["ItemDescription"].AsString());
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);
}
}
}
}
internal sealed class NotificationCallback : CallbackMsg {
internal enum ENotificationType {
Unknown = 0,
Trading = 1,
}
/*
__ __ _ _ _
| \/ | ___ | |_ | |__ ___ __| | ___
| |\/| | / _ \| __|| '_ \ / _ \ / _` |/ __|
| | | || __/| |_ | | | || (_) || (_| |\__ \
|_| |_| \___| \__||_| |_| \___/ \__,_||___/
internal ENotificationType NotificationType { get; private set; }
internal NotificationCallback(CMsgClientUserNotifications.Notification body) {
NotificationType = (ENotificationType) body.user_notification_type;
}
}
*/
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 PlayGame(string gameName) {
if (!Client.IsConnected) {
return;
}
var request = new ClientMsgProtobuf<CMsgClientGamesPlayed>(EMsg.ClientGamesPlayed);
var gamePlayed = new CMsgClientGamesPlayed.GamePlayed();
if (!string.IsNullOrEmpty(gameName)) {
gamePlayed.game_id = new GameID() {
AppType = GameID.GameType.Shortcut,
ModID = uint.MaxValue
};
gamePlayed.game_extra_info = gameName;
}
request.Body.games_played.Add(gamePlayed);
Client.Send(request);
}
internal void PlayGames(params uint[] gameIDs) {
if (!Client.IsConnected) {
return;
}
var request = new ClientMsgProtobuf<CMsgClientGamesPlayed>(EMsg.ClientGamesPlayed);
foreach (uint gameID in gameIDs) {
if (gameID == 0) {
@@ -110,10 +222,15 @@ namespace ArchiSteamFarm {
game_id = new GameID(gameID),
});
}
Client.Send(request);
}
internal void PlayGames(ICollection<uint> gameIDs) {
if (gameIDs == null || !Client.IsConnected) {
return;
}
var request = new ClientMsgProtobuf<CMsgClientGamesPlayed>(EMsg.ClientGamesPlayed);
foreach (uint gameID in gameIDs) {
if (gameID == 0) {
@@ -124,17 +241,70 @@ namespace ArchiSteamFarm {
game_id = new GameID(gameID),
});
}
Client.Send(request);
}
// Will provide result in ClientPurchaseResponse, regardless if success or not
internal void RedeemKey(string key) {
var request = new ClientMsgProtobuf<CMsgClientRegisterKey>(EMsg.ClientRegisterKey);
internal async Task<PurchaseResponseCallback> RedeemKey(string key) {
if (string.IsNullOrEmpty(key) || !Client.IsConnected) {
return null;
}
var request = new ClientMsgProtobuf<CMsgClientRegisterKey>(EMsg.ClientRegisterKey) {
SourceJobID = Client.GetNextJobID()
};
request.Body.key = key;
Client.Send(request);
try {
return await new AsyncJob<PurchaseResponseCallback>(Client, request.SourceJobID);
} catch (Exception e) {
Logging.LogGenericException(e);
return null;
}
}
public sealed override void HandleMsg(IPacketMsg packetMsg) {
// TODO: Please remove me immediately after https://github.com/SteamRE/SteamKit/issues/254 gets fixed
internal void HackedLogOn(SteamUser.LogOnDetails details) {
if (!Client.IsConnected) {
return;
}
SteamID steamID = new SteamID(details.AccountID, details.AccountInstance, Client.ConnectedUniverse, EAccountType.Individual);
var logon = new ClientMsgProtobuf<CMsgClientLogon>(EMsg.ClientLogon);
logon.Body.obfustucated_private_ip = details.LoginID.Value;
logon.ProtoHeader.client_sessionid = 0;
logon.ProtoHeader.steamid = steamID.ConvertToUInt64();
logon.Body.account_name = details.Username;
logon.Body.password = details.Password;
logon.Body.should_remember_password = details.ShouldRememberPassword;
logon.Body.protocol_version = MsgClientLogon.CurrentProtocol;
logon.Body.client_os_type = (uint) details.ClientOSType;
logon.Body.client_language = details.ClientLanguage;
logon.Body.cell_id = details.CellID;
logon.Body.steam2_ticket_request = details.RequestSteam2Ticket;
logon.Body.client_package_version = 1771;
logon.Body.auth_code = details.AuthCode;
logon.Body.two_factor_code = details.TwoFactorCode;
logon.Body.login_key = details.LoginKey;
logon.Body.sha_sentryfile = details.SentryFileHash;
logon.Body.eresult_sentryfile = (int) (details.SentryFileHash != null ? EResult.OK : EResult.FileNotFound);
Client.Send(logon);
}
/*
_ _ _ _
| | | | __ _ _ __ __| || | ___ _ __ ___
| |_| | / _` || '_ \ / _` || | / _ \| '__|/ __|
| _ || (_| || | | || (_| || || __/| | \__ \
|_| |_| \__,_||_| |_| \__,_||_| \___||_| |___/
*/
public override void HandleMsg(IPacketMsg packetMsg) {
if (packetMsg == null) {
return;
}
@@ -143,6 +313,9 @@ namespace ArchiSteamFarm {
case EMsg.ClientFSOfflineMessageNotification:
HandleFSOfflineMessageNotification(packetMsg);
break;
case EMsg.ClientItemAnnouncements:
HandleItemAnnouncements(packetMsg);
break;
case EMsg.ClientPurchaseResponse:
HandlePurchaseResponse(packetMsg);
break;
@@ -158,7 +331,16 @@ namespace ArchiSteamFarm {
}
var response = new ClientMsgProtobuf<CMsgClientOfflineMessageNotification>(packetMsg);
Client.PostCallback(new OfflineMessageCallback(response.Body));
Client.PostCallback(new OfflineMessageCallback(packetMsg.TargetJobID, response.Body));
}
private void HandleItemAnnouncements(IPacketMsg packetMsg) {
if (packetMsg == null) {
return;
}
var response = new ClientMsgProtobuf<CMsgClientItemAnnouncements>(packetMsg);
Client.PostCallback(new NotificationsCallback(packetMsg.TargetJobID, response.Body));
}
private void HandlePurchaseResponse(IPacketMsg packetMsg) {
@@ -167,7 +349,7 @@ namespace ArchiSteamFarm {
}
var response = new ClientMsgProtobuf<CMsgClientPurchaseResponse>(packetMsg);
Client.PostCallback(new PurchaseResponseCallback(response.Body));
Client.PostCallback(new PurchaseResponseCallback(packetMsg.TargetJobID, response.Body));
}
private void HandleUserNotifications(IPacketMsg packetMsg) {
@@ -176,9 +358,7 @@ namespace ArchiSteamFarm {
}
var response = new ClientMsgProtobuf<CMsgClientUserNotifications>(packetMsg);
foreach (var notification in response.Body.notifications) {
Client.PostCallback(new NotificationCallback(notification));
}
Client.PostCallback(new NotificationsCallback(packetMsg.TargetJobID, response.Body));
}
}
}

View File

@@ -9,9 +9,10 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>ArchiSteamFarm</RootNamespace>
<AssemblyName>ArchiSteamFarm</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<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>
@@ -47,7 +47,7 @@
</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<DocumentationFile>
</DocumentationFile>
<Prefer32Bit>true</Prefer32Bit>
@@ -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.1-beta3\lib\net45\Newtonsoft.Json.dll</HintPath>
<HintPath>..\packages\Newtonsoft.Json.8.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="protobuf-net, Version=2.0.0.668, Culture=neutral, PublicKeyToken=257b51d87d2e4d67, processorArchitecture=MSIL">
@@ -79,6 +89,7 @@
</Reference>
<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" />
@@ -90,20 +101,34 @@
<Compile Include="ArchiHandler.cs" />
<Compile Include="ArchiWebHandler.cs" />
<Compile Include="Bot.cs" />
<Compile Include="BotConfig.cs" />
<Compile Include="GlobalDatabase.cs" />
<Compile Include="BotDatabase.cs" />
<Compile Include="CardsFarmer.cs" />
<Compile Include="CMsgClientClanInviteAction.cs" />
<Compile Include="CMsgs\CMsgClientClanInviteAction.cs" />
<Compile Include="Debugging.cs" />
<Compile Include="GlobalConfig.cs" />
<Compile Include="JSON\GitHub.cs" />
<Compile Include="JSON\Steam.cs" />
<Compile Include="Logging.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SteamItem.cs" />
<Compile Include="SteamTradeOffer.cs" />
<Compile Include="Trading.cs" />
<Compile Include="Utilities.cs" />
<Compile Include="WCF.cs" />
<Compile Include="WebBrowser.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<None Include="config\ASF.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="config\example.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="config\minimal.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
@@ -120,12 +145,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">
@@ -133,18 +152,29 @@
<Name>SteamAuth</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PreBuildEvent>
</PreBuildEvent>
</PropertyGroup>
<PropertyGroup>
<PostBuildEvent Condition=" '$(OS)' != 'Unix' ">if $(ConfigurationName) == Release (
mkdir "$(TargetDir)out" "$(TargetDir)out\config"
copy "$(TargetDir)config\example.xml" "$(TargetDir)out\config"
copy "$(TargetDir)config\minimal.xml" "$(TargetDir)out\config"
"$(SolutionDir)tools\ILMerge.exe" /ndebug /internalize /out:"$(TargetDir)out\ASF.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll" /target:exe /targetplatform:v4,"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5.2" /wildcards
)</PostBuildEvent>
<PostBuildEvent Condition=" '$(OS)' != 'Unix' AND '$(ConfigurationName)' == 'Release' ">
mkdir "$(SolutionDir)out\config"
copy "$(TargetDir)config\ASF.json" "$(SolutionDir)out\config"
copy "$(TargetDir)config\example.json" "$(SolutionDir)out\config"
copy "$(TargetDir)config\minimal.json" "$(SolutionDir)out\config"
"$(SolutionDir)tools\ILRepack\ILRepack.exe" /ndebug /internalize /parallel /targetplatform:v4 /wildcards /out:"$(SolutionDir)out\ASF.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll"
del "$(SolutionDir)out\ASF.exe.config"
</PostBuildEvent>
<PostBuildEvent Condition=" '$(OS)' == 'Unix' AND '$(ConfigurationName)' == 'Release' ">
mkdir -p "$(SolutionDir)out/config"
cp "$(TargetDir)config/ASF.json" "$(SolutionDir)out/config"
cp "$(TargetDir)config/example.json" "$(SolutionDir)out/config"
cp "$(TargetDir)config/minimal.json" "$(SolutionDir)out/config"
mono -O=all "$(SolutionDir)tools/ILRepack/ILRepack.exe" /ndebug /internalize /parallel /targetplatform:v4 /wildcards /out:"$(SolutionDir)out/ASF.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll"
rm "$(SolutionDir)out/ASF.exe.config"
</PostBuildEvent>
</PropertyGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.

View File

@@ -5,7 +5,7 @@
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
Copyright 2015 Łukasz "JustArchi" Domeradzki
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
Contact: JustArchi@JustArchi.net
Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,111 +21,73 @@
limitations under the License.
*/
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using HtmlAgilityPack;
using SteamKit2;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
namespace ArchiSteamFarm {
internal sealed class ArchiWebHandler {
private const int Timeout = 1000 * WebBrowser.HttpTimeout; // In miliseconds
private const string SteamCommunity = "steamcommunity.com";
private static string SteamCommunityURL = "https://" + SteamCommunity;
private static int Timeout = 30 * 1000;
private readonly Bot Bot;
private readonly string ApiKey;
private readonly Dictionary<string, string> SteamCookieDictionary = new Dictionary<string, string>();
private readonly Dictionary<string, string> Cookie = new Dictionary<string, string>(4);
private ulong SteamID;
private string VanityURL;
// This is required because home_process request must be done on final URL
private string GetHomeProcess() {
if (!string.IsNullOrEmpty(VanityURL)) {
return "http://steamcommunity.com/id/" + VanityURL + "/home_process";
} else if (SteamID != 0) {
return "http://steamcommunity.com/profiles/" + SteamID + "/home_process";
} else {
return null;
}
internal static void Init() {
Timeout = Program.GlobalConfig.HttpTimeout * 1000;
SteamCommunityURL = (Program.GlobalConfig.ForceHttp ? "http://" : "https://") + SteamCommunity;
}
private async Task UnlockParentalAccount(string parentalPin) {
if (string.IsNullOrEmpty(parentalPin) || parentalPin.Equals("0")) {
internal ArchiWebHandler(Bot bot) {
if (bot == null) {
return;
}
Logging.LogGenericInfo(Bot.BotName, "Unlocking parental account...");
Dictionary<string, string> postData = new Dictionary<string, string>() {
{ "pin", parentalPin }
};
HttpResponseMessage response = await WebBrowser.UrlPost("https://steamcommunity.com/parental/ajaxunlock", postData, SteamCookieDictionary, "https://steamcommunity.com/").ConfigureAwait(false);
if (response == null) {
Logging.LogGenericInfo(Bot.BotName, "Failed!");
return;
}
IEnumerable<string> setCookieValues;
if (!response.Headers.TryGetValues("Set-Cookie", out setCookieValues)) {
Logging.LogGenericInfo(Bot.BotName, "Failed!");
return;
}
foreach (string setCookieValue in setCookieValues) {
if (setCookieValue.Contains("steamparental=")) {
string setCookie = setCookieValue.Substring(setCookieValue.IndexOf("steamparental=") + 14);
setCookie = setCookie.Substring(0, setCookie.IndexOf(';'));
SteamCookieDictionary.Add("steamparental", setCookie);
Logging.LogGenericInfo(Bot.BotName, "Success!");
return;
}
}
Logging.LogGenericInfo(Bot.BotName, "Failed!");
}
internal ArchiWebHandler(Bot bot, string apiKey) {
Bot = bot;
if (!string.IsNullOrEmpty(apiKey) && !apiKey.Equals("null")) {
ApiKey = apiKey;
}
}
internal async Task Init(SteamClient steamClient, string webAPIUserNonce, string vanityURL, string parentalPin) {
internal async Task<bool> Init(SteamClient steamClient, string webAPIUserNonce, string parentalPin) {
if (steamClient == null || steamClient.SteamID == null || string.IsNullOrEmpty(webAPIUserNonce)) {
return;
return false;
}
SteamID = steamClient.SteamID;
VanityURL = vanityURL;
string sessionID = Convert.ToBase64String(Encoding.UTF8.GetBytes(SteamID.ToString(CultureInfo.InvariantCulture)));
string sessionID = Convert.ToBase64String(Encoding.UTF8.GetBytes(SteamID.ToString()));
// Generate an AES session key
byte[] sessionKey = CryptoHelper.GenerateRandomBlock(32);
// RSA encrypt it with the public key for the universe we're on
byte[] cryptedSessionKey = null;
byte[] cryptedSessionKey;
using (RSACrypto rsa = new RSACrypto(KeyDictionary.GetPublicKey(steamClient.ConnectedUniverse))) {
cryptedSessionKey = rsa.Encrypt(sessionKey);
}
// Copy our login key
byte[] loginKey = new byte[20];
byte[] loginKey = new byte[webAPIUserNonce.Length];
Array.Copy(Encoding.ASCII.GetBytes(webAPIUserNonce), loginKey, webAPIUserNonce.Length);
// AES encrypt the loginkey with our session key
byte[] cryptedLoginKey = CryptoHelper.SymmetricEncrypt(loginKey, sessionKey);
// Send the magic
// Do the magic
Logging.LogGenericInfo("Logging in to ISteamUserAuth...", Bot.BotName);
KeyValue authResult;
Logging.LogGenericInfo(Bot.BotName, "Logging in to ISteamUserAuth...");
using (dynamic iSteamUserAuth = WebAPI.GetInterface("ISteamUserAuth")) {
iSteamUserAuth.Timeout = Timeout;
@@ -135,32 +97,32 @@ namespace ArchiSteamFarm {
sessionkey: Encoding.ASCII.GetString(WebUtility.UrlEncodeToBytes(cryptedSessionKey, 0, cryptedSessionKey.Length)),
encrypted_loginkey: Encoding.ASCII.GetString(WebUtility.UrlEncodeToBytes(cryptedLoginKey, 0, cryptedLoginKey.Length)),
method: WebRequestMethods.Http.Post,
secure: true
secure: !Program.GlobalConfig.ForceHttp
);
} catch (Exception e) {
Logging.LogGenericException(Bot.BotName, e);
steamClient.Disconnect(); // We may get 403 if we use the same webAPIUserNonce twice
return;
Logging.LogGenericException(e, Bot.BotName);
return false;
}
}
if (authResult == null) {
steamClient.Disconnect(); // Try again
return;
return false;
}
Logging.LogGenericInfo(Bot.BotName, "Success!");
Logging.LogGenericInfo("Success!", Bot.BotName);
string steamLogin = authResult["token"].AsString();
string steamLoginSecure = authResult["tokensecure"].AsString();
SteamCookieDictionary.Clear();
SteamCookieDictionary.Add("sessionid", sessionID);
SteamCookieDictionary.Add("steamLogin", steamLogin);
SteamCookieDictionary.Add("steamLoginSecure", steamLoginSecure);
SteamCookieDictionary.Add("birthtime", "-473356799"); // ( ͡° ͜ʖ ͡°)
Cookie["sessionid"] = sessionID;
Cookie["steamLogin"] = steamLogin;
Cookie["steamLoginSecure"] = steamLoginSecure;
// The below is used for display purposes only
Cookie["webTradeEligibility"] = "{\"allowed\":0,\"reason\":0,\"allowed_at_time\":0,\"steamguard_required_days\":0,\"sales_this_year\":0,\"max_sales_per_year\":0,\"forms_requested\":0}";
await UnlockParentalAccount(parentalPin).ConfigureAwait(false);
return true;
}
internal async Task<bool?> IsLoggedIn() {
@@ -168,8 +130,13 @@ namespace ArchiSteamFarm {
return false;
}
HtmlDocument htmlDocument = await WebBrowser.UrlGetToHtmlDocument("http://steamcommunity.com/my/profile", SteamCookieDictionary).ConfigureAwait(false);
HtmlDocument htmlDocument = null;
for (byte i = 0; i < WebBrowser.MaxRetries && htmlDocument == null; i++) {
htmlDocument = await WebBrowser.UrlGetToHtmlDocument(SteamCommunityURL + "/my/profile", Cookie).ConfigureAwait(false);
}
if (htmlDocument == null) {
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
return null;
}
@@ -180,80 +147,111 @@ namespace ArchiSteamFarm {
internal async Task<bool> ReconnectIfNeeded() {
bool? isLoggedIn = await IsLoggedIn().ConfigureAwait(false);
if (isLoggedIn.HasValue && !isLoggedIn.Value) {
Logging.LogGenericInfo(Bot.BotName, "Reconnecting because our sessionID expired!");
var restart = Task.Run(async () => await Bot.Restart().ConfigureAwait(false));
Logging.LogGenericInfo("Reconnecting because our sessionID expired!", Bot.BotName);
Bot.RestartIfRunning().Forget();
return true;
}
return false;
}
internal List<SteamTradeOffer> GetTradeOffers() {
if (ApiKey == null) {
internal async Task<Dictionary<uint, string>> GetOwnedGames() {
if (SteamID == 0) {
return null;
}
KeyValue response;
using (dynamic iEconService = WebAPI.GetInterface("IEconService")) {
// Timeout
string request = SteamCommunityURL + "/profiles/" + SteamID + "/games/?xml=1";
XmlDocument response = null;
for (byte i = 0; i < WebBrowser.MaxRetries && response == null; i++) {
response = await WebBrowser.UrlGetToXML(request, Cookie).ConfigureAwait(false);
}
if (response == null) {
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
return null;
}
XmlNodeList xmlNodeList = response.SelectNodes("gamesList/games/game");
if (xmlNodeList == null || xmlNodeList.Count == 0) {
return null;
}
Dictionary<uint, string> result = new Dictionary<uint, string>(xmlNodeList.Count);
foreach (XmlNode xmlNode in xmlNodeList) {
XmlNode appNode = xmlNode.SelectSingleNode("appID");
if (appNode == null) {
continue;
}
uint appID;
if (!uint.TryParse(appNode.InnerText, out appID)) {
continue;
}
XmlNode nameNode = xmlNode.SelectSingleNode("name");
if (nameNode == null) {
continue;
}
result[appID] = nameNode.InnerText;
}
return result;
}
internal List<Steam.TradeOffer> GetTradeOffers() {
if (string.IsNullOrEmpty(Bot.BotConfig.SteamApiKey)) {
return null;
}
KeyValue response = null;
using (dynamic iEconService = WebAPI.GetInterface("IEconService", Bot.BotConfig.SteamApiKey)) {
iEconService.Timeout = Timeout;
try {
response = iEconService.GetTradeOffers(
key: ApiKey,
get_received_offers: 1,
active_only: 1,
secure: true
);
} catch (Exception e) {
Logging.LogGenericException(Bot.BotName, e);
return null;
for (byte i = 0; i < WebBrowser.MaxRetries && response == null; i++) {
try {
response = iEconService.GetTradeOffers(
get_received_offers: 1,
active_only: 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", Bot.BotName);
return null;
}
List<SteamTradeOffer> result = new List<SteamTradeOffer>();
List<Steam.TradeOffer> result = new List<Steam.TradeOffer>();
foreach (KeyValue trade in response["trade_offers_received"].Children) {
SteamTradeOffer tradeOffer = new SteamTradeOffer {
Steam.TradeOffer tradeOffer = new Steam.TradeOffer {
tradeofferid = trade["tradeofferid"].AsString(),
accountid_other = trade["accountid_other"].AsInteger(),
message = trade["message"].AsString(),
expiration_time = trade["expiration_time"].AsInteger(),
trade_offer_state = (SteamTradeOffer.ETradeOfferState) trade["trade_offer_state"].AsInteger(),
items_to_give = new List<SteamItem>(),
items_to_receive = new List<SteamItem>(),
is_our_offer = trade["is_our_offer"].AsBoolean(),
time_created = trade["time_created"].AsInteger(),
time_updated = trade["time_updated"].AsInteger(),
from_real_time_trade = trade["from_real_time_trade"].AsBoolean(),
escrow_end_date = trade["escrow_end_date"].AsInteger(),
confirmation_method = (SteamTradeOffer.ETradeOfferConfirmationMethod) trade["confirmation_method"].AsInteger()
accountid_other = (uint) trade["accountid_other"].AsUnsignedLong(), // TODO: Correct this when SK2 with https://github.com/SteamRE/SteamKit/pull/255 gets released
trade_offer_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 {
tradeOffer.items_to_give.Add(new Steam.Item {
appid = item["appid"].AsString(),
contextid = item["contextid"].AsString(),
assetid = item["assetid"].AsString(),
currencyid = item["currencyid"].AsString(),
classid = item["classid"].AsString(),
instanceid = item["instanceid"].AsString(),
amount = item["amount"].AsString(),
missing = item["missing"].AsBoolean()
});
}
foreach (KeyValue item in trade["items_to_receive"].Children) {
tradeOffer.items_to_receive.Add(new SteamItem {
tradeOffer.items_to_receive.Add(new Steam.Item {
appid = item["appid"].AsString(),
contextid = item["contextid"].AsString(),
assetid = item["assetid"].AsString(),
currencyid = item["currencyid"].AsString(),
classid = item["classid"].AsString(),
instanceid = item["instanceid"].AsString(),
amount = item["amount"].AsString(),
missing = item["missing"].AsBoolean()
});
}
result.Add(tradeOffer);
@@ -262,44 +260,34 @@ namespace ArchiSteamFarm {
return result;
}
internal async Task JoinClan(ulong clanID) {
internal async Task<bool> JoinClan(ulong clanID) {
if (clanID == 0) {
return;
return false;
}
string sessionID;
if (!SteamCookieDictionary.TryGetValue("sessionid", out sessionID)) {
return;
if (!Cookie.TryGetValue("sessionid", out sessionID)) {
return false;
}
string request = "http://steamcommunity.com/gid/" + clanID;
string request = SteamCommunityURL + "/gid/" + clanID;
Dictionary<string, string> postData = new Dictionary<string, string>() {
Dictionary<string, string> data = new Dictionary<string, string>(2) {
{"sessionID", sessionID},
{"action", "join"}
};
await WebBrowser.UrlPost(request, postData, SteamCookieDictionary).ConfigureAwait(false);
}
internal async Task LeaveClan(ulong clanID) {
if (clanID == 0) {
return;
HttpResponseMessage response = null;
for (byte i = 0; i < WebBrowser.MaxRetries && response == null; i++) {
response = await WebBrowser.UrlPost(request, data, Cookie).ConfigureAwait(false);
}
string sessionID;
if (!SteamCookieDictionary.TryGetValue("sessionid", out sessionID)) {
return;
if (response == null) {
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
return false;
}
string request = GetHomeProcess();
Dictionary<string, string> postData = new Dictionary<string, string>() {
{"sessionID", sessionID},
{"action", "leaveGroup"},
{"groupId", clanID.ToString()}
};
await WebBrowser.UrlPost(request, postData, SteamCookieDictionary).ConfigureAwait(false);
return true;
}
internal async Task<bool> AcceptTradeOffer(ulong tradeID) {
@@ -308,84 +296,251 @@ namespace ArchiSteamFarm {
}
string sessionID;
if (!SteamCookieDictionary.TryGetValue("sessionid", out sessionID)) {
if (!Cookie.TryGetValue("sessionid", out sessionID)) {
return false;
}
string referer = "https://steamcommunity.com/tradeoffer/" + tradeID + "/";
string request = referer + "accept";
string referer = SteamCommunityURL + "/tradeoffer/" + tradeID;
string request = referer + "/accept";
Dictionary<string, string> postData = new Dictionary<string, string>() {
Dictionary<string, string> data = new Dictionary<string, string>(3) {
{"sessionid", sessionID},
{"serverid", "1"},
{"tradeofferid", tradeID.ToString()}
};
HttpResponseMessage result = await WebBrowser.UrlPost(request, postData, SteamCookieDictionary, referer).ConfigureAwait(false);
if (result == null) {
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;
}
bool success = result.IsSuccessStatusCode;
if (!success) {
Logging.LogGenericWarning(Bot.BotName, "Request failed, reason: " + result.ReasonPhrase);
switch (result.StatusCode) {
case HttpStatusCode.InternalServerError:
Logging.LogGenericWarning(Bot.BotName, "That might be caused by 7-days trade lock from new device");
Logging.LogGenericWarning(Bot.BotName, "Try again in 7 days, declining that offer for now");
DeclineTradeOffer(tradeID);
break;
}
}
return success;
return true;
}
internal bool DeclineTradeOffer(ulong tradeID) {
if (ApiKey == null) {
if (tradeID == 0 || string.IsNullOrEmpty(Bot.BotConfig.SteamApiKey)) {
return false;
}
if (tradeID == 0) {
return false;
}
KeyValue response;
using (dynamic iEconService = WebAPI.GetInterface("IEconService")) {
// Timeout
KeyValue response = null;
using (dynamic iEconService = WebAPI.GetInterface("IEconService", Bot.BotConfig.SteamApiKey)) {
iEconService.Timeout = Timeout;
for (byte i = 0; i < WebBrowser.MaxRetries && response == null; i++) {
try {
response = iEconService.DeclineTradeOffer(
tradeofferid: tradeID.ToString(),
method: WebRequestMethods.Http.Post,
secure: !Program.GlobalConfig.ForceHttp
);
} catch (Exception e) {
Logging.LogGenericException(e, Bot.BotName);
}
}
}
if (response == null) {
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
return false;
}
return true;
}
internal async Task<List<Steam.Item>> GetMyTradableInventory() {
JObject jObject = null;
for (byte i = 0; i < WebBrowser.MaxRetries && jObject == null; i++) {
jObject = await WebBrowser.UrlGetToJObject(SteamCommunityURL + "/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<Steam.Item> result = new List<Steam.Item>();
foreach (JToken jToken in jTokens) {
try {
response = iEconService.DeclineTradeOffer(
key: ApiKey,
tradeofferid: tradeID.ToString(),
method: WebRequestMethods.Http.Post,
secure: true
);
result.Add(JsonConvert.DeserializeObject<Steam.Item>(jToken.ToString()));
} catch (Exception e) {
Logging.LogGenericException(Bot.BotName, e);
Logging.LogGenericException(e, Bot.BotName);
}
}
return result;
}
internal async Task<bool> SendTradeOffer(List<Steam.Item> inventory, ulong partnerID, string token = null) {
if (inventory == null || inventory.Count == 0 || partnerID == 0) {
return false;
}
string sessionID;
if (!Cookie.TryGetValue("sessionid", out sessionID)) {
return false;
}
List<Steam.TradeOfferRequest> trades = new List<Steam.TradeOfferRequest>(1 + inventory.Count / Trading.MaxItemsPerTrade);
Steam.TradeOfferRequest singleTrade = null;
for (ushort i = 0; i < inventory.Count; i++) {
if (i % Trading.MaxItemsPerTrade == 0) {
if (trades.Count >= Trading.MaxTradesPerAccount) {
break;
}
singleTrade = new Steam.TradeOfferRequest();
trades.Add(singleTrade);
}
Steam.Item item = inventory[i];
singleTrade.me.assets.Add(new Steam.Item() {
appid = "753",
contextid = "6",
amount = item.amount,
assetid = item.id
});
}
string referer = SteamCommunityURL + "/tradeoffer/new";
string request = referer + "/send";
foreach (Steam.TradeOfferRequest trade in trades) {
Dictionary<string, string> data = new Dictionary<string, string>(6) {
{"sessionid", sessionID},
{"serverid", "1"},
{"partner", partnerID.ToString()},
{"tradeoffermessage", "Sent by ASF"},
{"json_tradeoffer", JsonConvert.SerializeObject(trade)},
{"trade_offer_create_params", string.IsNullOrEmpty(token) ? "" : $"{{\"trade_offer_access_token\":\"{token}\"}}"}
};
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 response != null; // Steam API doesn't respond with any error code, assume any response is a success
return true;
}
internal async Task<HtmlDocument> GetBadgePage(int page) {
if (SteamID == 0 || page == 0) {
internal async Task<HtmlDocument> GetBadgePage(byte page) {
if (page == 0 || SteamID == 0) {
return null;
}
return await WebBrowser.UrlGetToHtmlDocument("http://steamcommunity.com/profiles/" + SteamID + "/badges?p=" + page, SteamCookieDictionary).ConfigureAwait(false);
HtmlDocument htmlDocument = null;
for (byte i = 0; i < WebBrowser.MaxRetries && htmlDocument == null; i++) {
htmlDocument = await WebBrowser.UrlGetToHtmlDocument(SteamCommunityURL + "/profiles/" + SteamID + "/badges?l=english&p=" + page, Cookie).ConfigureAwait(false);
}
if (htmlDocument == null) {
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
return null;
}
return htmlDocument;
}
internal async Task<HtmlDocument> GetGameCardsPage(ulong appID) {
if (SteamID == 0 || appID == 0) {
if (appID == 0 || SteamID == 0) {
return null;
}
return await WebBrowser.UrlGetToHtmlDocument("http://steamcommunity.com/profiles/" + SteamID + "/gamecards/" + appID, SteamCookieDictionary).ConfigureAwait(false);
HtmlDocument htmlDocument = null;
for (byte i = 0; i < WebBrowser.MaxRetries && htmlDocument == null; i++) {
htmlDocument = await WebBrowser.UrlGetToHtmlDocument(SteamCommunityURL + "/profiles/" + SteamID + "/gamecards/" + appID + "?l=english", Cookie).ConfigureAwait(false);
}
if (htmlDocument == null) {
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
return null;
}
return htmlDocument;
}
internal async Task<bool> MarkInventory() {
if (SteamID == 0) {
return false;
}
HttpResponseMessage response = null;
for (byte i = 0; i < WebBrowser.MaxRetries && response == null; i++) {
response = await WebBrowser.UrlGet(SteamCommunityURL + "/profiles/" + SteamID + "/inventory", Cookie).ConfigureAwait(false);
}
if (response == null) {
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
return false;
}
return true;
}
private async Task UnlockParentalAccount(string parentalPin) {
if (string.IsNullOrEmpty(parentalPin) || parentalPin.Equals("0")) {
return;
}
Logging.LogGenericInfo("Unlocking parental account...", Bot.BotName);
Dictionary<string, string> data = new Dictionary<string, string>(1) {
{ "pin", parentalPin }
};
string referer = SteamCommunityURL;
string request = referer + "/parental/ajaxunlock";
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;
}
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=")) {
continue;
}
string setCookie = setCookieValue.Substring(setCookieValue.IndexOf("steamparental=", StringComparison.Ordinal) + 14);
int index = setCookie.IndexOf(';');
if (index > 0) {
setCookie = setCookie.Substring(0, index);
}
Cookie["steamparental"] = setCookie;
Logging.LogGenericInfo("Success!", Bot.BotName);
return;
}
Logging.LogGenericWarning("Failed to unlock parental account!", Bot.BotName);
}
}
}

File diff suppressed because it is too large Load Diff

258
ArchiSteamFarm/BotConfig.cs Normal file
View File

@@ -0,0 +1,258 @@
/*
_ _ _ ____ _ _____
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
Contact: JustArchi@JustArchi.net
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
namespace ArchiSteamFarm {
internal sealed class BotConfig {
[JsonProperty(Required = Required.DisallowNull)]
internal bool Enabled { get; private set; } = false;
[JsonProperty(Required = Required.DisallowNull)]
internal bool StartOnLaunch { get; private set; } = true;
[JsonProperty]
internal string SteamLogin { get; set; } = null;
[JsonProperty]
internal string SteamPassword { get; set; } = null;
[JsonProperty]
internal string SteamParentalPIN { get; set; } = "0";
[JsonProperty]
internal string SteamApiKey { get; private set; } = null;
[JsonProperty(Required = Required.DisallowNull)]
internal ulong SteamMasterID { get; private set; } = 0;
[JsonProperty(Required = Required.DisallowNull)]
internal ulong SteamMasterClanID { get; private set; } = 0;
[JsonProperty(Required = Required.DisallowNull)]
internal bool CardDropsRestricted { get; private set; } = false;
[JsonProperty(Required = Required.DisallowNull)]
internal bool DismissInventoryNotifications { get; private set; } = true;
[JsonProperty(Required = Required.DisallowNull)]
internal bool FarmOffline { get; private set; } = false;
[JsonProperty(Required = Required.DisallowNull)]
internal bool HandleOfflineMessages { get; private set; } = false;
[JsonProperty(Required = Required.DisallowNull)]
internal bool 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 path) {
if (!File.Exists(path)) {
return null;
}
BotConfig botConfig;
try {
botConfig = JsonConvert.DeserializeObject<BotConfig>(File.ReadAllText(path));
} catch (Exception e) {
Logging.LogGenericException(e);
return null;
}
return botConfig;
}
// TODO: This should be removed soon
internal static BotConfig LoadOldFormat(string path) {
if (!File.Exists(path)) {
return null;
}
BotConfig botConfig = new BotConfig();
try {
using (XmlReader reader = XmlReader.Create(path)) {
while (reader.Read()) {
if (reader.NodeType != XmlNodeType.Element) {
continue;
}
string key = reader.Name;
if (string.IsNullOrEmpty(key)) {
continue;
}
string value = reader.GetAttribute("value");
if (string.IsNullOrEmpty(value)) {
continue;
}
switch (key) {
case "Enabled":
botConfig.Enabled = bool.Parse(value);
break;
case "SteamLogin":
botConfig.SteamLogin = value;
break;
case "SteamPassword":
botConfig.SteamPassword = value;
break;
case "SteamApiKey":
botConfig.SteamApiKey = value;
break;
case "SteamTradeToken":
botConfig.SteamTradeToken = value;
break;
case "SteamParentalPIN":
botConfig.SteamParentalPIN = value;
break;
case "SteamMasterID":
botConfig.SteamMasterID = ulong.Parse(value);
break;
case "SteamMasterClanID":
botConfig.SteamMasterClanID = ulong.Parse(value);
break;
case "StartOnLaunch":
botConfig.StartOnLaunch = bool.Parse(value);
break;
case "UseAsfAsMobileAuthenticator":
botConfig.UseAsfAsMobileAuthenticator = bool.Parse(value);
break;
case "CardDropsRestricted":
botConfig.CardDropsRestricted = bool.Parse(value);
break;
case "FarmOffline":
botConfig.FarmOffline = bool.Parse(value);
break;
case "HandleOfflineMessages":
botConfig.HandleOfflineMessages = bool.Parse(value);
break;
case "ForwardKeysToOtherBots":
botConfig.ForwardKeysToOtherBots = bool.Parse(value);
break;
case "DistributeKeys":
botConfig.DistributeKeys = bool.Parse(value);
break;
case "ShutdownOnFarmingFinished":
botConfig.ShutdownOnFarmingFinished = bool.Parse(value);
break;
case "SendOnFarmingFinished":
botConfig.SendOnFarmingFinished = bool.Parse(value);
break;
case "SendTradePeriod":
botConfig.SendTradePeriod = byte.Parse(value);
break;
case "GamesPlayedWhileIdle":
botConfig.GamesPlayedWhileIdle.Clear();
foreach (string appID in value.Split(',')) {
botConfig.GamesPlayedWhileIdle.Add(uint.Parse(appID));
}
break;
case "Statistics":
case "Blacklist":
case "SteamNickname":
break;
default:
Logging.LogGenericWarning("Unrecognized config value: " + key + "=" + value);
break;
}
}
}
} catch (Exception e) {
Logging.LogGenericException(e);
Logging.LogGenericError("Your config for this bot instance is invalid, it won't run!");
return null;
}
// Fixups for new format
if (botConfig.SteamLogin != null && botConfig.SteamLogin.Equals("null")) {
botConfig.SteamLogin = null;
}
if (botConfig.SteamPassword != null && botConfig.SteamPassword.Equals("null")) {
botConfig.SteamPassword = null;
}
if (botConfig.SteamApiKey != null && botConfig.SteamApiKey.Equals("null")) {
botConfig.SteamApiKey = null;
}
if (botConfig.SteamParentalPIN != null && botConfig.SteamParentalPIN.Equals("null")) {
botConfig.SteamParentalPIN = null;
}
if (botConfig.SteamTradeToken != null && botConfig.SteamTradeToken.Equals("null")) {
botConfig.SteamTradeToken = null;
}
return botConfig;
}
// This constructor is used only by deserializer
private BotConfig() { }
// TODO: This should be removed soon
internal bool Convert(string path) {
try {
File.WriteAllText(path, JsonConvert.SerializeObject(this, Newtonsoft.Json.Formatting.Indented));
} catch (Exception e) {
Logging.LogGenericException(e);
return false;
}
Logging.LogGenericWarning("Your config was converted to new ASF V2.0 format");
return true;
}
}
}

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 Newtonsoft.Json;
using SteamAuth;
using System;
using System.IO;
namespace ArchiSteamFarm {
internal sealed class BotDatabase {
internal string LoginKey {
get {
return _LoginKey;
}
set {
if (_LoginKey == value) {
return;
}
_LoginKey = value;
Save();
}
}
internal SteamGuardAccount SteamGuardAccount {
get {
return _SteamGuardAccount;
}
set {
if (_SteamGuardAccount == value) {
return;
}
_SteamGuardAccount = value;
Save();
}
}
[JsonProperty]
private string _LoginKey;
[JsonProperty]
private SteamGuardAccount _SteamGuardAccount;
private string FilePath;
internal static BotDatabase Load(string filePath) {
if (!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;
}
botDatabase.FilePath = filePath;
return botDatabase;
}
// This constructor is used when creating new database
private BotDatabase(string filePath) {
FilePath = filePath;
Save();
}
// This constructor is used only by deserializer
private BotDatabase() { }
internal void Save() {
lock (FilePath) {
try {
File.WriteAllText(FilePath, JsonConvert.SerializeObject(this));
} catch (Exception e) {
Logging.LogGenericException(e);
}
}
}
}
}

View File

@@ -5,7 +5,7 @@
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
Copyright 2015 Łukasz "JustArchi" Domeradzki
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
Contact: JustArchi@JustArchi.net
Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,19 +24,18 @@
using SteamKit2;
using SteamKit2.Internal;
using System;
using System.IO;
namespace ArchiSteamFarm {
internal sealed class CMsgClientClanInviteAction : ISteamSerializableMessage, ISteamSerializable {
internal sealed class CMsgClientClanInviteAction : ISteamSerializableMessage {
internal ulong GroupID { get; set; } = 0;
internal bool AcceptInvite { get; set; } = true;
EMsg ISteamSerializableMessage.GetEMsg() {
return EMsg.ClientAcknowledgeClanInvite;
}
internal ulong GroupID = 0;
internal bool AcceptInvite = true;
public CMsgClientClanInviteAction() { }
void ISteamSerializable.Serialize(Stream stream) {
if (stream == null) {
return;
@@ -46,8 +45,8 @@ namespace ArchiSteamFarm {
BinaryWriter binaryWriter = new BinaryWriter(stream);
binaryWriter.Write(GroupID);
binaryWriter.Write(AcceptInvite);
} catch {
throw new IOException();
} catch (Exception e) {
Logging.LogGenericException(e);
}
}
@@ -60,8 +59,8 @@ namespace ArchiSteamFarm {
BinaryReader binaryReader = new BinaryReader(stream);
GroupID = binaryReader.ReadUInt64();
AcceptInvite = binaryReader.ReadBoolean();
} catch {
throw new IOException();
} catch (Exception e) {
Logging.LogGenericException(e);
}
}
}

View File

@@ -5,7 +5,7 @@
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
Copyright 2015 Łukasz "JustArchi" Domeradzki
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
Contact: JustArchi@JustArchi.net
Licensed under the Apache License, Version 2.0 (the "License");
@@ -27,261 +27,114 @@ 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 HashSet<uint> CurrentGamesFarming = new HashSet<uint>();
private readonly ManualResetEvent FarmResetEvent = new ManualResetEvent(false);
private readonly SemaphoreSlim Semaphore = new SemaphoreSlim(1);
private readonly Bot Bot;
private readonly Timer Timer;
internal readonly ConcurrentDictionary<uint, double> GamesToFarm = new ConcurrentDictionary<uint, double>();
internal readonly List<uint> CurrentGamesFarming = new List<uint>();
private bool NowFarming = false;
private volatile bool ManualMode, NowFarming;
internal CardsFarmer(Bot bot) {
if (bot == null) {
return;
}
Bot = bot;
Timer = new Timer(
async e => await CheckGamesForFarming().ConfigureAwait(false),
null,
TimeSpan.FromMinutes(15), // Delay
TimeSpan.FromMinutes(15) // Period
);
if (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, double> gamesToFarm) {
if (gamesToFarm == null) {
return null;
}
List<uint> result = new List<uint>();
foreach (KeyValuePair<uint, double> keyValue in gamesToFarm) {
if (keyValue.Value >= 2) {
result.Add(keyValue.Key);
}
}
return result;
}
internal static uint GetAnyGameToFarm(ConcurrentDictionary<uint, double> gamesToFarm) {
if (gamesToFarm == null) {
return 0;
}
foreach (uint appID in gamesToFarm.Keys) {
return appID;
}
return 0;
}
internal bool FarmMultiple() {
if (GamesToFarm.Count == 0) {
return true;
}
double maxHour = -1;
foreach (double hour in GamesToFarm.Values) {
if (hour > maxHour) {
maxHour = hour;
}
}
CurrentGamesFarming.Clear();
foreach (uint appID in GamesToFarm.Keys) {
CurrentGamesFarming.Add(appID);
}
Logging.LogGenericInfo(Bot.BotName, "Now farming: " + string.Join(", ", GamesToFarm.Keys));
if (Farm(maxHour, GamesToFarm.Keys)) {
CurrentGamesFarming.Clear();
return true;
} else {
CurrentGamesFarming.Clear();
internal async Task<bool> SwitchToManualMode(bool manualMode) {
if (ManualMode == manualMode) {
return false;
}
}
internal async Task<bool> FarmSolo(uint appID) {
if (appID == 0) {
return true;
}
ManualMode = manualMode;
CurrentGamesFarming.Clear();
CurrentGamesFarming.Add(appID);
Logging.LogGenericInfo(Bot.BotName, "Now farming: " + appID);
if (await Farm(appID).ConfigureAwait(false)) {
double hours;
GamesToFarm.TryRemove(appID, out hours);
return true;
if (ManualMode) {
Logging.LogGenericInfo("Now running in Manual Farming mode", Bot.BotName);
await StopFarming().ConfigureAwait(false);
} else {
CurrentGamesFarming.Clear();
return false;
Logging.LogGenericInfo("Now running in Automatic Farming mode", Bot.BotName);
StartFarming().Forget();
}
}
internal async Task RestartFarming() {
await StopFarming().ConfigureAwait(false);
await StartFarming().ConfigureAwait(false);
return true;
}
internal async Task StartFarming() {
if (NowFarming || ManualMode) {
return;
}
await Semaphore.WaitAsync().ConfigureAwait(false);
if (NowFarming) {
Semaphore.Release();
if (NowFarming || ManualMode) {
Semaphore.Release(); // We have nothing to do, don't forget to release semaphore
return;
}
if (await Bot.ArchiWebHandler.ReconnectIfNeeded().ConfigureAwait(false)) {
Semaphore.Release();
if (!await IsAnythingToFarm().ConfigureAwait(false)) {
Semaphore.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);
return;
}
Logging.LogGenericInfo(Bot.BotName, "Checking badges...");
// Find the number of badge pages
HtmlDocument badgesDocument = await Bot.ArchiWebHandler.GetBadgePage(1).ConfigureAwait(false);
if (badgesDocument == null) {
Logging.LogGenericWarning(Bot.BotName, "Could not get badges information, will try again later!");
Semaphore.Release();
return;
}
var maxPages = 1;
HtmlNodeCollection badgesPagesNodeCollection = badgesDocument.DocumentNode.SelectNodes("//a[@class='pagelink']");
if (badgesPagesNodeCollection != null) {
maxPages = (badgesPagesNodeCollection.Count / 2) + 1; // Don't do this at home
}
// Find APPIDs we need to farm
for (var page = 1; page <= maxPages; page++) {
Logging.LogGenericInfo(Bot.BotName, "Checking page: " + page + "/" + maxPages);
if (page > 1) { // Because we fetched page number 1 already
badgesDocument = await Bot.ArchiWebHandler.GetBadgePage(page).ConfigureAwait(false);
if (badgesDocument == null) {
break;
}
}
HtmlNodeCollection badgesPageNodes = badgesDocument.DocumentNode.SelectNodes("//a[@class='btn_green_white_innerfade btn_small_thin']");
if (badgesPageNodes == null) {
continue;
}
GamesToFarm.Clear();
foreach (HtmlNode badgesPageNode in badgesPageNodes) {
string steamLink = badgesPageNode.GetAttributeValue("href", null);
if (steamLink == null) {
continue;
}
uint appID = (uint) Utilities.OnlyNumbers(steamLink);
if (appID == 0) {
continue;
}
if (Bot.GlobalBlacklist.Contains(appID) || Bot.Blacklist.Contains(appID)) {
continue;
}
// We assume that every game has at least 2 hours played, until we actually check them
GamesToFarm.AddOrUpdate(appID, 2, (key, value) => 2);
}
}
// If we have restricted card drops, actually do check all games that are left to farm
if (Bot.CardDropsRestricted) {
foreach (uint appID in GamesToFarm.Keys) {
Logging.LogGenericInfo(Bot.BotName, "Checking hours of appID: " + appID);
HtmlDocument appPage = await Bot.ArchiWebHandler.GetGameCardsPage(appID).ConfigureAwait(false);
if (appPage == null) {
continue;
}
HtmlNode appNode = appPage.DocumentNode.SelectSingleNode("//div[@class='badge_title_stats_playtime']");
if (appNode == null) {
continue;
}
string hoursString = appNode.InnerText;
if (string.IsNullOrEmpty(hoursString)) {
continue;
}
hoursString = Regex.Match(hoursString, @"[0-9\.,]+").Value;
double hours;
if (string.IsNullOrEmpty(hoursString)) {
hours = 0;
} else {
hours = double.Parse(hoursString, CultureInfo.InvariantCulture);
}
GamesToFarm[appID] = hours;
}
}
if (GamesToFarm.Count == 0) {
Logging.LogGenericInfo(Bot.BotName, "No games to farm!");
Semaphore.Release();
return;
}
Logging.LogGenericInfo(Bot.BotName, "Farming in progress...");
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
bool farmedSomething = false;
// 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, which prioritizes farming solo titles >= 2 hours, then all at once, until any game hits mentioned 2 hours
Logging.LogGenericInfo(Bot.BotName, "Chosen farming algorithm: Complex");
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) {
List<uint> gamesToFarmSolo = GetGamesToFarmSolo(GamesToFarm);
HashSet<uint> gamesToFarmSolo = GetGamesToFarmSolo(GamesToFarm);
if (gamesToFarmSolo.Count > 0) {
while (gamesToFarmSolo.Count > 0) {
uint appID = gamesToFarmSolo[0];
bool success = await FarmSolo(appID).ConfigureAwait(false);
if (success) {
Logging.LogGenericInfo(Bot.BotName, "Done farming: " + appID);
uint appID = gamesToFarmSolo.First();
if (await FarmSolo(appID).ConfigureAwait(false)) {
farmedSomething = true;
gamesToFarmSolo.Remove(appID);
gamesToFarmSolo.TrimExcess();
} else {
NowFarming = false;
return;
}
}
} else {
bool success = FarmMultiple();
if (success) {
Logging.LogGenericInfo(Bot.BotName, "Done farming: " + string.Join(", ", GamesToFarm.Keys));
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 and farm cards one-by-one
Logging.LogGenericInfo(Bot.BotName, "Chosen farming algorithm: Simple");
} 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);
bool success = await FarmSolo(appID).ConfigureAwait(false);
if (success) {
Logging.LogGenericInfo(Bot.BotName, "Done farming: " + appID);
uint appID = GamesToFarm.Keys.FirstOrDefault();
if (await FarmSolo(appID).ConfigureAwait(false)) {
farmedSomething = true;
} else {
NowFarming = false;
return;
@@ -290,12 +143,25 @@ namespace ArchiSteamFarm {
}
CurrentGamesFarming.Clear();
CurrentGamesFarming.TrimExcess();
NowFarming = false;
Logging.LogGenericInfo(Bot.BotName, "Farming finished!");
await Bot.OnFarmingFinished().ConfigureAwait(false);
// We finished our queue for now, make sure that everything is indeed farmed before proceeding further
// Some games could be added in the meantime
if (await IsAnythingToFarm().ConfigureAwait(false)) {
StartFarming().Forget();
return;
}
Logging.LogGenericInfo("Farming finished!", Bot.BotName);
await Bot.OnFarmingFinished(farmedSomething).ConfigureAwait(false);
}
internal async Task StopFarming() {
if (!NowFarming) {
return;
}
await Semaphore.WaitAsync().ConfigureAwait(false);
if (!NowFarming) {
@@ -303,26 +169,173 @@ namespace ArchiSteamFarm {
return;
}
Logging.LogGenericInfo(Bot.BotName, "Sending signal to stop farming");
Logging.LogGenericInfo("Sending signal to stop farming", Bot.BotName);
FarmResetEvent.Set();
for (var i = 0; i < 5 && NowFarming; i++) {
Logging.LogGenericInfo(Bot.BotName, "Waiting for reaction...");
Logging.LogGenericInfo("Waiting for reaction...", Bot.BotName);
for (byte i = 0; i < Program.GlobalConfig.HttpTimeout && NowFarming; i++) {
await Utilities.SleepAsync(1000).ConfigureAwait(false);
}
if (NowFarming) {
Logging.LogGenericWarning("Timed out!", Bot.BotName);
}
FarmResetEvent.Reset();
Logging.LogGenericInfo(Bot.BotName, "Farming stopped!");
Logging.LogGenericInfo("Farming stopped!", Bot.BotName);
Semaphore.Release();
}
internal async Task RestartFarming() {
await StopFarming().ConfigureAwait(false);
await StartFarming().ConfigureAwait(false);
}
private static HashSet<uint> GetGamesToFarmSolo(ConcurrentDictionary<uint, float> gamesToFarm) {
if (gamesToFarm == null) {
return null;
}
HashSet<uint> result = new HashSet<uint>();
foreach (KeyValuePair<uint, float> keyValue in gamesToFarm) {
if (keyValue.Value >= 2) {
result.Add(keyValue.Key);
}
}
return result;
}
private async Task<bool> IsAnythingToFarm() {
if (NowFarming) {
return true;
}
if (await Bot.ArchiWebHandler.ReconnectIfNeeded().ConfigureAwait(false)) {
return false;
}
Logging.LogGenericInfo("Checking badges...", Bot.BotName);
// Find the number of badge pages
Logging.LogGenericInfo("Checking first page...", Bot.BotName);
HtmlDocument htmlDocument = await Bot.ArchiWebHandler.GetBadgePage(1).ConfigureAwait(false);
if (htmlDocument == null) {
Logging.LogGenericWarning("Could not get badges information, will try again later!", Bot.BotName);
return false;
}
byte maxPages = 1;
HtmlNodeCollection htmlNodeCollection = htmlDocument.DocumentNode.SelectNodes("//a[@class='pagelink']");
if (htmlNodeCollection != null && htmlNodeCollection.Count > 0) {
HtmlNode htmlNode = htmlNodeCollection[htmlNodeCollection.Count - 1];
if (!byte.TryParse(htmlNode.InnerText, out maxPages)) {
maxPages = 1; // Should never happen
}
}
GamesToFarm.Clear();
// Find APPIDs we need to farm
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(CheckPage(currentPage));
}
}
await Task.WhenAll(tasks).ConfigureAwait(false);
if (GamesToFarm.Count == 0) {
return false;
}
return true;
}
private void CheckPage(HtmlDocument htmlDocument) {
if (htmlDocument == null) {
return;
}
HtmlNodeCollection htmlNodes = htmlDocument.DocumentNode.SelectNodes("//div[@class='badge_title_stats']");
if (htmlNodes == null) {
return;
}
foreach (HtmlNode htmlNode in htmlNodes) {
HtmlNode farmingNode = htmlNode.SelectSingleNode(".//a[@class='btn_green_white_innerfade btn_small_thin']");
if (farmingNode == null) {
continue; // This game is not needed for farming
}
string steamLink = farmingNode.GetAttributeValue("href", null);
if (string.IsNullOrEmpty(steamLink)) {
Logging.LogNullError("steamLink", Bot.BotName);
continue;
}
uint appID = (uint) Utilities.OnlyNumbers(steamLink);
if (appID == 0) {
Logging.LogNullError("appID", Bot.BotName);
continue;
}
if (GlobalConfig.GlobalBlacklist.Contains(appID) || Program.GlobalConfig.Blacklist.Contains(appID)) {
continue;
}
HtmlNode timeNode = htmlNode.SelectSingleNode(".//div[@class='badge_title_stats_playtime']");
if (timeNode == null) {
Logging.LogNullError("timeNode", Bot.BotName);
continue;
}
string hoursString = timeNode.InnerText;
if (string.IsNullOrEmpty(hoursString)) {
Logging.LogNullError("hoursString", Bot.BotName);
continue;
}
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 CheckPage(byte page) {
if (page == 0) {
return;
}
HtmlDocument htmlDocument = await Bot.ArchiWebHandler.GetBadgePage(page).ConfigureAwait(false);
if (htmlDocument == null) {
return;
}
CheckPage(htmlDocument);
}
private async Task CheckGamesForFarming() {
if (NowFarming || 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) {
return false;
}
@@ -341,27 +354,94 @@ namespace ArchiSteamFarm {
return !htmlNode.InnerText.Contains("No card drops");
}
private bool FarmMultiple() {
if (GamesToFarm.Count == 0) {
return true;
}
float maxHour = 0;
foreach (KeyValuePair<uint, float> game in GamesToFarm) {
CurrentGamesFarming.Add(game.Key);
if (game.Value > maxHour) {
maxHour = game.Value;
}
}
if (maxHour >= 2) {
CurrentGamesFarming.Clear();
CurrentGamesFarming.TrimExcess();
return true;
}
Logging.LogGenericInfo("Now farming: " + string.Join(", ", CurrentGamesFarming), Bot.BotName);
if (FarmHours(maxHour, CurrentGamesFarming)) {
CurrentGamesFarming.Clear();
CurrentGamesFarming.TrimExcess();
return true;
} else {
CurrentGamesFarming.Clear();
CurrentGamesFarming.TrimExcess();
return false;
}
}
private async Task<bool> FarmSolo(uint appID) {
if (appID == 0) {
return true;
}
CurrentGamesFarming.Add(appID);
Logging.LogGenericInfo("Now farming: " + appID, Bot.BotName);
if (await Farm(appID).ConfigureAwait(false)) {
CurrentGamesFarming.Clear();
CurrentGamesFarming.TrimExcess();
float hours;
if (GamesToFarm.TryRemove(appID, out hours)) {
Logging.LogGenericInfo("Done farming: " + appID + " after " + hours + " hours of playtime!", Bot.BotName);
}
return true;
} else {
CurrentGamesFarming.Clear();
CurrentGamesFarming.TrimExcess();
return false;
}
}
private async Task<bool> Farm(uint appID) {
if (appID == 0) {
return false;
}
Bot.ArchiHandler.PlayGames(appID);
bool success = true;
bool? keepFarming = await ShouldFarm(appID).ConfigureAwait(false);
for (ushort farmingTime = 0; farmingTime <= MaxFarmingTime && (!keepFarming.HasValue || keepFarming.Value); farmingTime += StatusCheckSleep) {
Logging.LogGenericInfo(Bot.BotName, "Still farming: " + appID);
if (FarmResetEvent.WaitOne(1000 * 60 * StatusCheckSleep)) {
for (ushort farmingTime = 0; farmingTime <= 60 * Program.GlobalConfig.MaxFarmingTime && keepFarming.GetValueOrDefault(true); farmingTime += Program.GlobalConfig.FarmingDelay) {
if (FarmResetEvent.WaitOne(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(Bot.BotName, "Stopped farming: " + appID);
Bot.ResetGamesPlayed();
Logging.LogGenericInfo("Stopped farming: " + appID, Bot.BotName);
return success;
}
private bool Farm(double maxHour, ICollection<uint> appIDs) {
private bool FarmHours(float maxHour, HashSet<uint> appIDs) {
if (maxHour < 0 || appIDs == null || appIDs.Count == 0) {
return false;
}
if (maxHour >= 2) {
return true;
}
@@ -370,27 +450,23 @@ namespace ArchiSteamFarm {
bool success = true;
while (maxHour < 2) {
Logging.LogGenericInfo(Bot.BotName, "Still farming: " + string.Join(", ", appIDs));
if (FarmResetEvent.WaitOne(1000 * 60 * StatusCheckSleep)) {
if (FarmResetEvent.WaitOne(60 * 1000 * Program.GlobalConfig.FarmingDelay)) {
success = false;
break;
}
// Don't forget to update our GamesToFarm hours
double timePlayed = StatusCheckSleep / 60.0;
foreach (KeyValuePair<uint, double> keyValue in GamesToFarm) {
if (!appIDs.Contains(keyValue.Key)) {
continue;
}
GamesToFarm[keyValue.Key] = keyValue.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(Bot.BotName, "Stopped farming: " + string.Join(", ", appIDs));
Bot.ResetGamesPlayed();
Logging.LogGenericInfo("Stopped farming: " + string.Join(", ", appIDs), Bot.BotName);
return success;
}
}

View File

@@ -5,7 +5,7 @@
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
Copyright 2015 Łukasz "JustArchi" Domeradzki
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
Contact: JustArchi@JustArchi.net
Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,20 +22,42 @@
*/
using System.Diagnostics;
using SteamKit2;
using System;
using System.IO;
namespace ArchiSteamFarm {
internal static class Debugging {
internal static bool IsDebugBuild { get; private set; } = false;
internal static bool IsReleaseBuild { get { return !IsDebugBuild; } }
#if DEBUG
internal static readonly bool IsDebugBuild = true;
#else
internal static readonly bool IsDebugBuild = false;
#endif
static Debugging() {
MarkIfDebug();
}
internal static bool IsReleaseBuild => !IsDebugBuild;
[Conditional("DEBUG")]
private static void MarkIfDebug() {
IsDebugBuild = true;
internal static bool NetHookAlreadyInitialized { get; set; } = false;
internal sealed class DebugListener : IDebugListener {
private readonly string FilePath;
internal DebugListener(string filePath) {
if (string.IsNullOrEmpty(filePath)) {
return;
}
FilePath = filePath;
}
public void WriteLine(string category, string msg) {
lock (FilePath) {
try {
File.AppendAllText(FilePath, category + " | " + msg + Environment.NewLine);
} catch (Exception e) {
Logging.LogGenericException(e);
}
}
}
}
}
}

View File

@@ -0,0 +1,161 @@
/*
_ _ _ ____ _ _____
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
Contact: JustArchi@JustArchi.net
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Sockets;
namespace ArchiSteamFarm {
internal sealed class GlobalConfig {
internal enum EUpdateChannel : byte {
Unknown,
Stable,
Experimental
}
private const byte DefaultMaxFarmingTime = 10;
private const byte DefaultFarmingDelay = 5;
private const byte DefaultHttpTimeout = 60;
private const ushort DefaultWCFPort = 1242;
private static readonly 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 AutoUpdates { get; private set; } = true;
[JsonProperty(Required = Required.DisallowNull)]
internal EUpdateChannel UpdateChannel { get; private set; } = EUpdateChannel.Stable;
[JsonProperty(Required = Required.DisallowNull)]
internal ProtocolType SteamProtocol { get; private set; } = DefaultSteamProtocol;
[JsonProperty(Required = Required.DisallowNull)]
internal ulong SteamOwnerID { get; private set; } = 0;
[JsonProperty(Required = Required.DisallowNull)]
internal byte MaxFarmingTime { get; private set; } = DefaultMaxFarmingTime;
[JsonProperty(Required = Required.DisallowNull)]
internal byte IdleFarmingPeriod { get; private set; } = 3;
[JsonProperty(Required = Required.DisallowNull)]
internal byte FarmingDelay { get; private set; } = DefaultFarmingDelay;
[JsonProperty(Required = Required.DisallowNull)]
internal byte AccountPlayingDelay { get; private set; } = 5;
[JsonProperty(Required = Required.DisallowNull)]
internal byte LoginLimiterDelay { get; private set; } = 7;
[JsonProperty(Required = Required.DisallowNull)]
internal byte InventoryLimiterDelay { get; private set; } = 3;
[JsonProperty(Required = Required.DisallowNull)]
internal bool ForceHttp { get; private set; } = false;
[JsonProperty(Required = Required.DisallowNull)]
internal byte HttpTimeout { get; private set; } = DefaultHttpTimeout;
[JsonProperty]
internal string WCFHostname { get; set; } = "localhost";
[JsonProperty(Required = Required.DisallowNull)]
internal ushort WCFPort { get; private set; } = DefaultWCFPort;
[JsonProperty(Required = Required.DisallowNull)]
internal bool LogToFile { get; private set; } = true;
[JsonProperty(Required = Required.DisallowNull)]
internal bool Statistics { get; private set; } = true;
// TODO: Please remove me immediately after https://github.com/SteamRE/SteamKit/issues/254 gets fixed
[JsonProperty(Required = Required.DisallowNull)]
internal bool HackIgnoreMachineID { get; private set; } = false;
[JsonProperty(Required = Required.DisallowNull)]
internal HashSet<uint> Blacklist { get; private set; } = new HashSet<uint>(GlobalBlacklist);
internal static GlobalConfig Load() {
string filePath = Path.Combine(Program.ConfigDirectory, Program.GlobalConfigFile);
if (!File.Exists(filePath)) {
return null;
}
GlobalConfig globalConfig;
try {
globalConfig = JsonConvert.DeserializeObject<GlobalConfig>(File.ReadAllText(filePath));
} catch (Exception e) {
Logging.LogGenericException(e);
return null;
}
// SK2 supports only TCP and UDP steam protocols
// Ensure that user can't screw this up
switch (globalConfig.SteamProtocol) {
case ProtocolType.Tcp:
case ProtocolType.Udp:
break;
default:
Logging.LogGenericWarning("Configured SteamProtocol is invalid: " + globalConfig.SteamProtocol + ". Value of " + DefaultSteamProtocol + " will be used instead");
globalConfig.SteamProtocol = DefaultSteamProtocol;
break;
}
// User might not know what he's doing
// Ensure that he can't screw core ASF variables
if (globalConfig.MaxFarmingTime == 0) {
Logging.LogGenericWarning("Configured MaxFarmingTime is invalid: " + globalConfig.MaxFarmingTime + ". Value of " + DefaultMaxFarmingTime + " will be used instead");
globalConfig.MaxFarmingTime = DefaultMaxFarmingTime;
}
if (globalConfig.FarmingDelay == 0) {
Logging.LogGenericWarning("Configured FarmingDelay is invalid: " + globalConfig.FarmingDelay + ". Value of " + DefaultFarmingDelay + " will be used instead");
globalConfig.FarmingDelay = DefaultFarmingDelay;
}
if (globalConfig.HttpTimeout == 0) {
Logging.LogGenericWarning("Configured HttpTimeout is invalid: " + globalConfig.HttpTimeout + ". Value of " + DefaultHttpTimeout + " will be used instead");
globalConfig.HttpTimeout = DefaultHttpTimeout;
}
if (globalConfig.WCFPort == 0) {
Logging.LogGenericWarning("Configured WCFPort is invalid: " + globalConfig.WCFPort + ". Value of " + DefaultWCFPort + " will be used instead");
globalConfig.WCFPort = DefaultWCFPort;
}
return globalConfig;
}
// This constructor is used only by deserializer
private GlobalConfig() { }
}
}

View File

@@ -0,0 +1,79 @@
/*
_ _ _ ____ _ _____
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
Contact: JustArchi@JustArchi.net
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
using Newtonsoft.Json;
using System;
using System.IO;
namespace ArchiSteamFarm {
internal sealed class GlobalDatabase {
private static readonly string FilePath = Path.Combine(Program.ConfigDirectory, Program.GlobalDatabaseFile);
internal uint CellID {
get {
return _CellID;
}
set {
if (_CellID == value) {
return;
}
_CellID = value;
Save();
}
}
[JsonProperty(Required = Required.DisallowNull)]
private uint _CellID = 0;
internal static GlobalDatabase Load() {
if (!File.Exists(FilePath)) {
return new GlobalDatabase();
}
GlobalDatabase globalDatabase;
try {
globalDatabase = JsonConvert.DeserializeObject<GlobalDatabase>(File.ReadAllText(FilePath));
} catch (Exception e) {
Logging.LogGenericException(e);
return null;
}
return globalDatabase;
}
// This constructor is used only by deserializer
private GlobalDatabase() { }
private void Save() {
lock (FilePath) {
try {
File.WriteAllText(FilePath, JsonConvert.SerializeObject(this));
} catch (Exception e) {
Logging.LogGenericException(e);
}
}
}
}
}

View File

@@ -0,0 +1,46 @@
/*
_ _ _ ____ _ _____
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
Contact: JustArchi@JustArchi.net
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
using Newtonsoft.Json;
using System.Collections.Generic;
namespace ArchiSteamFarm {
internal static class GitHub {
internal sealed class 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; }
}
internal sealed class ReleaseResponse {
[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,122 @@
/*
_ _ _ ____ _ _____
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
Contact: JustArchi@JustArchi.net
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
using Newtonsoft.Json;
using SteamKit2;
using System.Collections.Generic;
namespace ArchiSteamFarm {
internal static class Steam {
internal sealed class Item {
// REF: https://developer.valvesoftware.com/wiki/Steam_Web_API/IEconService#CEcon_Asset
[JsonProperty(Required = Required.DisallowNull)]
internal string appid { get; set; }
[JsonProperty(Required = Required.DisallowNull)]
internal string contextid { get; set; }
[JsonProperty(Required = Required.DisallowNull)]
internal string assetid { get; set; }
[JsonProperty(Required = Required.DisallowNull)]
internal string id {
get { return assetid; }
set { assetid = value; }
}
[JsonProperty(Required = Required.AllowNull)]
internal string classid { get; set; }
[JsonProperty(Required = Required.AllowNull)]
internal string instanceid { get; set; }
[JsonProperty(Required = Required.Always)]
internal string amount { get; set; }
}
internal sealed class ItemList {
[JsonProperty(Required = Required.Always)]
internal List<Steam.Item> assets { get; } = new List<Steam.Item>();
}
internal sealed class TradeOffer {
// REF: https://developer.valvesoftware.com/wiki/Steam_Web_API/IEconService#CEcon_TradeOffer
internal enum ETradeOfferState : byte {
Unknown,
Invalid,
Active,
Accepted,
Countered,
Expired,
Canceled,
Declined,
InvalidItems,
EmailPending,
EmailCanceled,
OnHold
}
[JsonProperty(Required = Required.Always)]
internal string tradeofferid { get; set; }
[JsonProperty(Required = Required.Always)]
internal uint accountid_other { get; set; }
[JsonProperty(Required = Required.Always)]
internal ETradeOfferState trade_offer_state { get; set; }
[JsonProperty(Required = Required.Always)]
internal List<Steam.Item> items_to_give { get; } = new List<Steam.Item>();
[JsonProperty(Required = Required.Always)]
internal List<Steam.Item> items_to_receive { get; } = new List<Steam.Item>();
// Extra
private ulong _OtherSteamID64 = 0;
internal ulong OtherSteamID64 {
get {
if (_OtherSteamID64 == 0 && accountid_other != 0) {
_OtherSteamID64 = new SteamID(accountid_other, EUniverse.Public, EAccountType.Individual).ConvertToUInt64();
}
return _OtherSteamID64;
}
}
}
internal sealed class TradeOfferRequest {
[JsonProperty(Required = Required.Always)]
internal bool newversion { get; } = true;
[JsonProperty(Required = Required.Always)]
internal int version { get; } = 2;
[JsonProperty(Required = Required.Always)]
internal Steam.ItemList me { get; } = new Steam.ItemList();
[JsonProperty(Required = Required.Always)]
internal Steam.ItemList them { get; } = new Steam.ItemList();
}
}
}

View File

@@ -5,7 +5,7 @@
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
Copyright 2015 Łukasz "JustArchi" Domeradzki
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
Contact: JustArchi@JustArchi.net
Licensed under the Apache License, Version 2.0 (the "License");
@@ -31,8 +31,82 @@ namespace ArchiSteamFarm {
internal static class Logging {
private static readonly object FileLock = new object();
private static bool LogToFile = false;
internal static void Init() {
File.Delete(Program.LogFile);
LogToFile = Program.GlobalConfig.LogToFile;
if (LogToFile) {
lock (FileLock) {
try {
File.Delete(Program.LogFile);
} catch (Exception e) {
LogGenericException(e);
}
}
}
}
internal static void LogGenericWTF(string message, string botName = "Main", [CallerMemberName] string previousMethodName = "") {
if (string.IsNullOrEmpty(message)) {
return;
}
Log("[!!] WTF: " + previousMethodName + "() <" + botName + "> " + message + ", WTF?");
}
internal static void LogGenericError(string message, string botName = "Main", [CallerMemberName] string previousMethodName = "") {
if (string.IsNullOrEmpty(message)) {
return;
}
Log("[!!] ERROR: " + previousMethodName + "() <" + botName + "> " + message);
}
internal static void LogGenericException(Exception exception, string botName = "Main", [CallerMemberName] string previousMethodName = "") {
if (exception == null) {
return;
}
Log("[!] EXCEPTION: " + previousMethodName + "() <" + botName + "> " + exception.Message);
Log("[!] StackTrace:" + Environment.NewLine + exception.StackTrace);
if (exception.InnerException != null) {
LogGenericException(exception.InnerException, botName, previousMethodName);
}
}
internal static void LogGenericWarning(string message, string botName = "Main", [CallerMemberName] string previousMethodName = "") {
if (string.IsNullOrEmpty(message)) {
return;
}
Log("[!] WARNING: " + previousMethodName + "() <" + botName + "> " + message);
}
internal static void LogGenericInfo(string message, string botName = "Main", [CallerMemberName] string previousMethodName = "") {
if (string.IsNullOrEmpty(message)) {
return;
}
Log("[*] INFO: " + previousMethodName + "() <" + botName + "> " + message);
}
internal static void LogNullError(string nullObjectName, string botName = "Main", [CallerMemberName] string previousMethodName = "") {
if (string.IsNullOrEmpty(nullObjectName)) {
return;
}
LogGenericError(nullObjectName + " is null!", botName, previousMethodName);
}
[Conditional("DEBUG")]
internal static void LogGenericDebug(string message, string botName = "Main", [CallerMemberName] string previousMethodName = "") {
if (string.IsNullOrEmpty(message)) {
return;
}
Log("[#] DEBUG: " + previousMethodName + "() <" + botName + "> " + message);
}
private static void Log(string message) {
@@ -44,37 +118,21 @@ namespace ArchiSteamFarm {
// Write on console only when not awaiting response from user
if (!Program.ConsoleIsBusy) {
Console.Write(loggedMessage);
try {
Console.Write(loggedMessage);
} catch { }
}
lock (FileLock) {
File.AppendAllText(Program.LogFile, loggedMessage);
if (LogToFile) {
lock (FileLock) {
try {
File.AppendAllText(Program.LogFile, loggedMessage);
} catch (Exception e) {
LogToFile = false;
LogGenericException(e);
}
}
}
}
internal static void LogGenericError(string botName, string message, [CallerMemberName] string previousMethodName = "") {
Log("[!!] ERROR: " + previousMethodName + "() <" + botName + "> " + message);
}
internal static void LogGenericException(string botName, Exception exception, [CallerMemberName] string previousMethodName = "") {
Log("[!] EXCEPTION: " + previousMethodName + "() <" + botName + "> " + exception.Message);
}
internal static void LogGenericWarning(string botName, string message, [CallerMemberName] string previousMethodName = "") {
Log("[!] WARNING: " + previousMethodName + "() <" + botName + "> " + message);
}
internal static void LogGenericInfo(string botName, string message, [CallerMemberName] string previousMethodName = "") {
Log("[*] INFO: " + previousMethodName + "() <" + botName + "> " + message);
}
internal static void LogGenericNotice(string botName, string message, [CallerMemberName] string previousMethodName = "") {
Log("[*] NOTICE: " + previousMethodName + "() <" + botName + "> " + message);
}
[Conditional("DEBUG")]
internal static void LogGenericDebug(string botName, string message, [CallerMemberName] string previousMethodName = "") {
Log("[#] DEBUG: " + previousMethodName + "() <" + botName + "> " + message);
}
}
}

View File

@@ -5,7 +5,7 @@
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
Copyright 2015 Łukasz "JustArchi" Domeradzki
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
Contact: JustArchi@JustArchi.net
Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,16 +22,21 @@
*/
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
namespace ArchiSteamFarm {
internal static class Program {
internal enum EUserInputType {
internal enum EUserInputType : byte {
Unknown,
DeviceID,
Login,
Password,
PhoneNumber,
@@ -40,98 +45,266 @@ namespace ArchiSteamFarm {
SteamParentalPIN,
RevocationCode,
TwoFactorAuthentication,
WCFHostname
}
private const string LatestGithubReleaseURL = "https://api.github.com/repos/JustArchi/ArchiSteamFarm/releases/latest";
internal const string ConfigDirectoryPath = "config";
internal enum EMode : byte {
Unknown,
Normal, // Standard most common usage
Client, // WCF client only
Server // Normal + WCF server
}
internal const string ASF = "ASF";
internal const string ConfigDirectory = "config";
internal const string DebugDirectory = "debug";
internal const string LogFile = "log.txt";
internal const string GlobalConfigFile = ASF + ".json";
internal const string GlobalDatabaseFile = ASF + ".db";
private const string GithubReleaseURL = "https://api.github.com/repos/JustArchi/ArchiSteamFarm/releases"; // GitHub API is HTTPS only
private static readonly Assembly Assembly = Assembly.GetExecutingAssembly();
internal static readonly Version Version = Assembly.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 ExecutablePath = Assembly.Location;
private static readonly AssemblyName AssemblyName = Assembly.GetName();
private static readonly object ConsoleLock = new object();
//private static readonly string ExeName = AssemblyName.Name + ".exe";
internal static readonly string LogFile = Path.Combine(Path.GetDirectoryName(ExecutablePath), "log.txt");
internal static readonly string Version = AssemblyName.Version.ToString();
private static readonly string ExecutableFile = Assembly.Location;
private static readonly string ExecutableName = Path.GetFileName(ExecutableFile);
private static readonly string ExecutableDirectory = Path.GetDirectoryName(ExecutableFile);
private static readonly WCF WCF = new WCF();
internal static GlobalConfig GlobalConfig { get; private set; }
internal static GlobalDatabase GlobalDatabase { get; private set; }
internal static bool ConsoleIsBusy { get; private set; } = false;
private static async Task CheckForUpdate() {
JObject response = await WebBrowser.UrlGetToJObject(LatestGithubReleaseURL).ConfigureAwait(false);
if (response == null) {
private static Timer AutoUpdatesTimer;
private static EMode Mode = EMode.Normal;
internal static async Task CheckForUpdate() {
string oldExeFile = ExecutableFile + ".old";
// We booted successfully so we can now remove old exe file
if (File.Exists(oldExeFile)) {
try {
File.Delete(oldExeFile);
} catch (Exception e) {
Logging.LogGenericException(e);
return;
}
}
if (GlobalConfig.UpdateChannel == GlobalConfig.EUpdateChannel.Unknown) {
return;
}
string remoteVersion = response["tag_name"].ToString();
if (string.IsNullOrEmpty(remoteVersion)) {
string releaseURL = GithubReleaseURL;
if (GlobalConfig.UpdateChannel == GlobalConfig.EUpdateChannel.Stable) {
releaseURL += "/latest";
}
string response = null;
Logging.LogGenericInfo("Checking new version...");
for (byte i = 0; i < WebBrowser.MaxRetries && string.IsNullOrEmpty(response); i++) {
response = await WebBrowser.UrlGetToContent(releaseURL).ConfigureAwait(false);
}
if (string.IsNullOrEmpty(response)) {
Logging.LogGenericWarning("Could not check latest version!");
return;
}
string localVersion = Version;
GitHub.ReleaseResponse releaseResponse;
if (GlobalConfig.UpdateChannel == GlobalConfig.EUpdateChannel.Stable) {
try {
releaseResponse = JsonConvert.DeserializeObject<GitHub.ReleaseResponse>(response);
} catch (JsonException e) {
Logging.LogGenericException(e);
return;
}
} else {
List<GitHub.ReleaseResponse> releases;
try {
releases = JsonConvert.DeserializeObject<List<GitHub.ReleaseResponse>>(response);
} catch (JsonException e) {
Logging.LogGenericException(e);
return;
}
Logging.LogGenericNotice("", "Local version: " + localVersion);
Logging.LogGenericNotice("", "Remote version: " + remoteVersion);
if (releases == null || releases.Count == 0) {
Logging.LogGenericWarning("Could not check latest version!");
return;
}
int comparisonResult = localVersion.CompareTo(remoteVersion);
if (comparisonResult < 0) {
Logging.LogGenericNotice("", "New version is available!");
Logging.LogGenericNotice("", "Consider updating yourself!");
releaseResponse = releases[0];
}
if (string.IsNullOrEmpty(releaseResponse.Tag)) {
Logging.LogGenericWarning("Could not check latest version!");
return;
}
Version newVersion = new Version(releaseResponse.Tag);
Logging.LogGenericInfo("Local version: " + Version + " | Remote version: " + newVersion);
if (Version.CompareTo(newVersion) >= 0) { // If local version is the same or newer than remote version
if (AutoUpdatesTimer == null && GlobalConfig.AutoUpdates) {
Logging.LogGenericInfo("ASF will automatically check for new versions every 24 hours");
AutoUpdatesTimer = new Timer(
async e => await CheckForUpdate().ConfigureAwait(false),
null,
TimeSpan.FromDays(1), // Delay
TimeSpan.FromDays(1) // Period
);
}
return;
}
if (!GlobalConfig.AutoUpdates) {
Logging.LogGenericInfo("New version is available!");
Logging.LogGenericInfo("Consider updating yourself!");
await Utilities.SleepAsync(5000).ConfigureAwait(false);
} else if (comparisonResult > 0) {
Logging.LogGenericNotice("", "You're currently using pre-release version!");
Logging.LogGenericNotice("", "Be careful!");
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.Asset binaryAsset = null;
foreach (var asset in releaseResponse.Assets) {
if (string.IsNullOrEmpty(asset.Name) || !asset.Name.Equals(ExecutableName)) {
continue;
}
binaryAsset = asset;
break;
}
if (binaryAsset == null) {
Logging.LogGenericWarning("Could not proceed with update because there is no asset that relates to currently running binary!");
return;
}
if (string.IsNullOrEmpty(binaryAsset.DownloadURL)) {
Logging.LogGenericWarning("Could not proceed with update because download URL is empty!");
return;
}
Logging.LogGenericInfo("Downloading new version...");
Stream newExe = await WebBrowser.UrlGetToStream(binaryAsset.DownloadURL).ConfigureAwait(false);
if (newExe == null) {
Logging.LogGenericWarning("Could not download new version!");
return;
}
// We start deep update logic here
string newExeFile = ExecutableFile + ".new";
// Firstly we create new exec
try {
using (FileStream fileStream = File.Open(newExeFile, FileMode.Create)) {
await newExe.CopyToAsync(fileStream).ConfigureAwait(false);
}
} catch (Exception e) {
Logging.LogGenericException(e);
return;
}
// Now we move current -> old
try {
File.Move(ExecutableFile, oldExeFile);
} catch (Exception e) {
Logging.LogGenericException(e);
try {
// Cleanup
File.Delete(newExeFile);
} catch { }
return;
}
// Now we move new -> current
try {
File.Move(newExeFile, ExecutableFile);
} catch (Exception e) {
Logging.LogGenericException(e);
try {
// Cleanup
File.Move(oldExeFile, ExecutableFile);
File.Delete(newExeFile);
} catch { }
return;
}
Logging.LogGenericInfo("Update process is finished! ASF will now restart itself...");
await Utilities.SleepAsync(5000);
Restart();
}
internal static async Task Exit(int exitCode = 0) {
await Bot.ShutdownAllBots().ConfigureAwait(false);
Environment.Exit(exitCode);
}
internal static async Task Restart() {
await Bot.ShutdownAllBots().ConfigureAwait(false);
System.Diagnostics.Process.Start(ExecutablePath);
Environment.Exit(0);
internal static void Restart() {
try {
Process.Start(ExecutableFile, string.Join(" ", Environment.GetCommandLineArgs().Skip(1)));
Environment.Exit(0);
} catch (Exception e) {
Logging.LogGenericException(e);
}
}
internal static async Task LimitSteamRequestsAsync() {
await SteamSemaphore.WaitAsync().ConfigureAwait(false);
await Utilities.SleepAsync(5 * 1000).ConfigureAwait(false); // We must add some delay to not get caught by Steam anty-DoS
SteamSemaphore.Release();
Task.Run(async () => {
await Utilities.SleepAsync(GlobalConfig.LoginLimiterDelay * 1000).ConfigureAwait(false);
SteamSemaphore.Release();
}).Forget();
}
internal static string GetUserInput(string botLogin, EUserInputType userInputType, string extraInformation = null) {
internal static string GetUserInput(EUserInputType userInputType, string botName = null, string extraInformation = null) {
if (userInputType == EUserInputType.Unknown) {
return null;
}
string result;
lock (ConsoleLock) {
ConsoleIsBusy = true;
switch (userInputType) {
case EUserInputType.DeviceID:
Console.Write((string.IsNullOrEmpty(botName) ? "" : "<" + botName + "> ") + "Please enter your Device ID (including \"android:\"): ");
break;
case EUserInputType.Login:
Console.Write("<" + botLogin + "> Please enter your login: ");
Console.Write((string.IsNullOrEmpty(botName) ? "" : "<" + botName + "> ") + "Please enter your login: ");
break;
case EUserInputType.Password:
Console.Write("<" + botLogin + "> Please enter your password: ");
Console.Write((string.IsNullOrEmpty(botName) ? "" : "<" + botName + "> ") + "Please enter your password: ");
break;
case EUserInputType.PhoneNumber:
Console.Write("<" + botLogin + "> Please enter your full phone number (e.g. +1234567890): ");
Console.Write((string.IsNullOrEmpty(botName) ? "" : "<" + 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((string.IsNullOrEmpty(botName) ? "" : "<" + botName + "> ") + "Please enter SMS code sent on your mobile: ");
break;
case EUserInputType.SteamGuard:
Console.Write("<" + botLogin + "> Please enter the auth code sent to your email: ");
Console.Write((string.IsNullOrEmpty(botName) ? "" : "<" + botName + "> ") + "Please enter the auth code sent to your email: ");
break;
case EUserInputType.SteamParentalPIN:
Console.Write("<" + botLogin + "> Please enter steam parental PIN: ");
Console.Write((string.IsNullOrEmpty(botName) ? "" : "<" + botName + "> ") + "Please enter steam parental PIN: ");
break;
case EUserInputType.RevocationCode:
Console.WriteLine("<" + 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.Write((string.IsNullOrEmpty(botName) ? "" : "<" + botName + "> ") + "PLEASE WRITE DOWN YOUR REVOCATION CODE: " + extraInformation);
Console.Write("Hit enter once ready...");
break;
case EUserInputType.TwoFactorAuthentication:
Console.Write("<" + botLogin + "> Please enter your 2 factor auth code from your authenticator app: ");
Console.Write((string.IsNullOrEmpty(botName) ? "" : "<" + botName + "> ") + "Please enter your 2 factor auth code from your authenticator app: ");
break;
case EUserInputType.WCFHostname:
Console.Write((string.IsNullOrEmpty(botName) ? "" : "<" + botName + "> ") + "Please enter your WCF hostname: ");
break;
default:
Console.Write((string.IsNullOrEmpty(botName) ? "" : "<" + botName + "> ") + "Please enter not documented yet value of \"" + userInputType + "\": ");
break;
}
result = Console.ReadLine();
@@ -139,57 +312,201 @@ namespace ArchiSteamFarm {
ConsoleIsBusy = false;
}
return result.Trim(); // Get rid of all whitespace characters
return string.IsNullOrEmpty(result) ? null : result.Trim();
}
internal static async void OnBotShutdown() {
if (Bot.GetRunningBotsCount() == 0) {
Logging.LogGenericInfo("Main", "No bots are running, exiting");
await Utilities.SleepAsync(5000).ConfigureAwait(false); // This might be the only message user gets, consider giving him some time
ShutdownResetEvent.Set();
internal static void OnBotShutdown() {
foreach (Bot bot in Bot.Bots.Values) {
if (bot.KeepRunning) {
return;
}
}
if (WCF.IsServerRunning()) {
return;
}
Logging.LogGenericInfo("No bots are running, exiting");
Thread.Sleep(5000);
ShutdownResetEvent.Set();
}
private static void InitServices() {
Logging.Init();
GlobalConfig = GlobalConfig.Load();
if (GlobalConfig == null) {
Logging.LogGenericError("Global config could not be loaded, please make sure that ASF.json exists and is valid!");
Thread.Sleep(5000);
Environment.Exit(1);
}
GlobalDatabase = GlobalDatabase.Load();
if (GlobalDatabase == null) {
Logging.LogGenericError("Global database could not be loaded!");
Thread.Sleep(5000);
Environment.Exit(1);
}
ArchiWebHandler.Init();
WebBrowser.Init();
WCF.Init();
}
private static void Main(string[] args) {
private static void ParseArgs(string[] args) {
foreach (string arg in args) {
switch (arg) {
case "--client":
Mode = EMode.Client;
break;
case "--server":
Mode = EMode.Server;
WCF.StartServer();
break;
default:
if (arg.StartsWith("--")) {
Logging.LogGenericWarning("Unrecognized parameter: " + arg);
continue;
}
if (Mode != EMode.Client) {
Logging.LogGenericWarning("Ignoring command because --client wasn't specified: " + arg);
continue;
}
Logging.LogGenericInfo("Command sent: " + arg);
// We intentionally execute this async block synchronously
Logging.LogGenericInfo("Response received: " + WCF.SendCommand(arg));
/*
Task.Run(async () => {
Logging.LogGenericNotice("WCF", "Response received: " + await WCF.SendCommand(arg).ConfigureAwait(false));
}).Wait();
*/
break;
}
}
}
private static void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs args) {
if (sender == null || args == null) {
return;
}
Logging.LogGenericException((Exception) args.ExceptionObject);
}
private static void UnobservedTaskExceptionHandler(object sender, UnobservedTaskExceptionEventArgs args) {
if (sender == null || args == null) {
return;
}
Logging.LogGenericException(args.Exception);
}
private static void Init(string[] args) {
AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionHandler;
TaskScheduler.UnobservedTaskException += UnobservedTaskExceptionHandler;
Logging.LogGenericInfo("Archi's Steam Farm, version " + Version);
Directory.SetCurrentDirectory(ExecutableDirectory);
InitServices();
Logging.LogGenericInfo("Main", "Archi's Steam Farm, version " + Version);
Task.Run(async () => await CheckForUpdate().ConfigureAwait(false)).Wait();
// Allow loading configs from source tree if it's a debug build
if (Debugging.IsDebugBuild) {
// Common structure is bin/(x64/)Debug/ArchiSteamFarm.exe, so we allow up to 4 directories up
for (var i = 0; i < 4; i++) {
Directory.SetCurrentDirectory("..");
if (Directory.Exists(ConfigDirectoryPath)) {
if (Directory.Exists(ConfigDirectory)) {
break;
}
}
}
if (!Directory.Exists(ConfigDirectoryPath)) {
Logging.LogGenericError("Main", "Config directory doesn't exist!");
Console.ReadLine();
Task.Run(async () => await Exit(1).ConfigureAwait(false)).Wait();
}
foreach (var configFile in Directory.EnumerateFiles(ConfigDirectoryPath, "*.xml")) {
string botName = Path.GetFileNameWithoutExtension(configFile);
Bot bot = new Bot(botName);
if (!bot.Enabled) {
Logging.LogGenericInfo(botName, "Not starting this instance because it's disabled in config file");
// If config directory doesn't exist after our adjustment, abort all of that
if (!Directory.Exists(ConfigDirectory)) {
Directory.SetCurrentDirectory(ExecutableDirectory);
}
}
// Check if we got any bots running
OnBotShutdown();
// If debugging is on, we prepare debug directory prior to running
if (GlobalConfig.Debug) {
if (Directory.Exists(DebugDirectory)) {
Directory.Delete(DebugDirectory, true);
Thread.Sleep(1000); // Dirty workaround giving Windows some time to sync
}
Directory.CreateDirectory(DebugDirectory);
SteamKit2.DebugLog.AddListener(new Debugging.DebugListener(Path.Combine(Program.DebugDirectory, "debug.txt")));
SteamKit2.DebugLog.Enabled = true;
}
// Parse args
ParseArgs(args);
// If we ran ASF as a client, we're done by now
if (Mode == EMode.Client) {
Environment.Exit(0);
}
// From now on it's server mode
Logging.Init();
if (!Directory.Exists(ConfigDirectory)) {
Logging.LogGenericError("Config directory doesn't exist!");
Thread.Sleep(5000);
Environment.Exit(1);
}
CheckForUpdate().Wait();
// Before attempting to connect, initialize our list of CMs
Bot.RefreshCMs(GlobalDatabase.CellID).Wait();
bool isRunning = false;
foreach (var configFile in Directory.EnumerateFiles(ConfigDirectory, "*.json")) {
string botName = Path.GetFileNameWithoutExtension(configFile);
switch (botName) {
case ASF:
case "example":
case "minimal":
continue;
}
Bot bot = new Bot(botName);
if (bot.BotConfig != null && bot.BotConfig.Enabled) {
isRunning = true;
} else {
Logging.LogGenericInfo("Not starting this instance because it's disabled in config file", botName);
}
}
// CONVERSION START
foreach (var configFile in Directory.EnumerateFiles(ConfigDirectory, "*.xml")) {
string botName = Path.GetFileNameWithoutExtension(configFile);
Logging.LogGenericWarning("Found legacy " + botName + ".xml config file, it will now be converted to new ASF V2.0 format!");
Bot bot = new Bot(botName);
if (bot.BotConfig != null && bot.BotConfig.Enabled) {
isRunning = true;
} else {
Logging.LogGenericInfo("Not starting this instance because it's disabled in config file", botName);
}
}
// CONVERSION END
// Check if we got any bots running
if (!isRunning) {
OnBotShutdown();
}
}
private static void Main(string[] args) {
Init(args);
// Wait for signal to shutdown
ShutdownResetEvent.WaitOne();
// We got a signal to shutdown
Environment.Exit(0);
}
}
}

View File

@@ -10,7 +10,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ArchiSteamFarm")]
[assembly: AssemblyCopyright("Copyright © Łukasz Domeradzki 2015")]
[assembly: AssemblyCopyright("Copyright © Łukasz Domeradzki 2015-2016")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.2.0.0")]
[assembly: AssemblyFileVersion("1.2.0.0")]
[assembly: AssemblyVersion("2.0.1.9")]
[assembly: AssemblyFileVersion("2.0.1.9")]

View File

@@ -1,78 +0,0 @@
/*
_ _ _ ____ _ _____
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
Copyright 2015 Łukasz "JustArchi" Domeradzki
Contact: JustArchi@JustArchi.net
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
using SteamKit2;
using System.Collections.Generic;
namespace ArchiSteamFarm {
internal sealed class SteamTradeOffer {
// REF: https://developer.valvesoftware.com/wiki/Steam_Web_API/IEconService
internal enum ETradeOfferState {
Unknown,
Invalid,
Active,
Accepted,
Countered,
Expired,
Canceled,
Declined,
InvalidItems,
EmailPending,
EmailCanceled,
OnHold
}
internal enum ETradeOfferConfirmationMethod {
Invalid,
Email,
MobileApp
}
internal string tradeofferid { get; set; }
internal int accountid_other { get; set; }
internal string message { get; set; }
internal int expiration_time { get; set; }
internal ETradeOfferState trade_offer_state { get; set; }
internal List<SteamItem> items_to_give { get; set; }
internal List<SteamItem> items_to_receive { get; set; }
internal bool is_our_offer { get; set; }
internal int time_created { get; set; }
internal int time_updated { get; set; }
internal bool from_real_time_trade { get; set; }
internal int escrow_end_date { get; set; }
internal ETradeOfferConfirmationMethod confirmation_method { get; set; }
// Extra
private ulong _OtherSteamID64 = 0;
internal ulong OtherSteamID64 {
get {
if (_OtherSteamID64 == 0 && accountid_other != 0) {
_OtherSteamID64 = new SteamID((uint) accountid_other, EUniverse.Public, EAccountType.Individual).ConvertToUInt64();
}
return _OtherSteamID64;
}
}
}
}

View File

@@ -5,7 +5,7 @@
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
Copyright 2015 Łukasz "JustArchi" Domeradzki
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
Contact: JustArchi@JustArchi.net
Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,50 +22,71 @@
*/
using SteamAuth;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace ArchiSteamFarm {
internal sealed class Trading {
internal const byte MaxItemsPerTrade = 150; // This is due to limit on POST size in WebBrowser
internal const byte MaxTradesPerAccount = 5; // This is limit introduced by Valve
private static readonly SemaphoreSlim InventorySemaphore = new SemaphoreSlim(1);
private readonly Bot Bot;
private readonly SemaphoreSlim Semaphore = new SemaphoreSlim(1);
private volatile byte ParsingTasks = 0;
internal static async Task LimitInventoryRequestsAsync() {
await InventorySemaphore.WaitAsync().ConfigureAwait(false);
Task.Run(async () => {
await Utilities.SleepAsync(Program.GlobalConfig.InventoryLimiterDelay * 1000).ConfigureAwait(false);
InventorySemaphore.Release();
}).Forget();
}
internal Trading(Bot bot) {
if (bot == null) {
return;
}
Bot = bot;
}
internal async void CheckTrades() {
if (ParsingTasks < 2) {
ParsingTasks++;
await Semaphore.WaitAsync().ConfigureAwait(false);
await ParseActiveTrades().ConfigureAwait(false);
Semaphore.Release();
ParsingTasks--;
if (ParsingTasks >= 2) {
return;
}
ParsingTasks++;
await Semaphore.WaitAsync().ConfigureAwait(false);
await ParseActiveTrades().ConfigureAwait(false);
Semaphore.Release();
ParsingTasks--;
}
private async Task ParseActiveTrades() {
List<SteamTradeOffer> tradeOffers = Bot.ArchiWebHandler.GetTradeOffers();
List<Steam.TradeOffer> tradeOffers = Bot.ArchiWebHandler.GetTradeOffers();
if (tradeOffers == null) {
return;
}
List<Task> tasks = new List<Task>();
foreach (SteamTradeOffer tradeOffer in tradeOffers) {
if (tradeOffer.trade_offer_state == SteamTradeOffer.ETradeOfferState.Active) {
foreach (Steam.TradeOffer tradeOffer in tradeOffers) {
if (tradeOffer.trade_offer_state == Steam.TradeOffer.ETradeOfferState.Active) {
tasks.Add(Task.Run(async () => await ParseTrade(tradeOffer).ConfigureAwait(false)));
}
}
await Task.WhenAll(tasks).ConfigureAwait(false);
await Bot.AcceptAllConfirmations().ConfigureAwait(false);
await Bot.AcceptConfirmations(Confirmation.ConfirmationType.Trade).ConfigureAwait(false);
}
private async Task ParseTrade(SteamTradeOffer tradeOffer) {
private async Task ParseTrade(Steam.TradeOffer tradeOffer) {
if (tradeOffer == null) {
return;
}
@@ -75,11 +96,11 @@ namespace ArchiSteamFarm {
return;
}
if (tradeOffer.items_to_give.Count == 0 || tradeOffer.OtherSteamID64 == Bot.SteamMasterID) {
Logging.LogGenericInfo(Bot.BotName, "Accepting trade: " + tradeID);
if (tradeOffer.items_to_give.Count == 0 || tradeOffer.OtherSteamID64 == Bot.BotConfig.SteamMasterID) {
Logging.LogGenericInfo("Accepting trade: " + tradeID, Bot.BotName);
await Bot.ArchiWebHandler.AcceptTradeOffer(tradeID).ConfigureAwait(false);
} else {
Logging.LogGenericInfo(Bot.BotName, "Ignoring trade: " + tradeID);
Logging.LogGenericInfo("Ignoring trade: " + tradeID, Bot.BotName);
}
}
}

View File

@@ -5,7 +5,7 @@
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
Copyright 2015 Łukasz "JustArchi" Domeradzki
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
Contact: JustArchi@JustArchi.net
Licensed under the Apache License, Version 2.0 (the "License");
@@ -27,6 +27,8 @@ using System.Threading.Tasks;
namespace ArchiSteamFarm {
internal static class Utilities {
internal static void Forget(this Task task) { }
internal static async Task SleepAsync(int miliseconds) {
await Task.Delay(miliseconds).ConfigureAwait(false);
}
@@ -56,5 +58,20 @@ namespace ArchiSteamFarm {
return Regex.Replace(text, @"[^\d]", "");
}
internal static uint GetCharCountInString(string s, char c) {
if (string.IsNullOrEmpty(s)) {
return 0;
}
uint count = 0;
foreach (char singleChar in s) {
if (singleChar == c) {
count++;
}
}
return count;
}
}
}

142
ArchiSteamFarm/WCF.cs Normal file
View File

@@ -0,0 +1,142 @@
/*
_ _ _ ____ _ _____
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
Contact: JustArchi@JustArchi.net
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
using System;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Channels;
namespace ArchiSteamFarm {
[ServiceContract]
internal interface IWCF {
[OperationContract]
string HandleCommand(string input);
}
internal sealed class WCF : IWCF {
private static string URL = "http://localhost:1242/ASF";
private ServiceHost ServiceHost;
private Client Client;
internal static void Init() {
if (string.IsNullOrEmpty(Program.GlobalConfig.WCFHostname)) {
Program.GlobalConfig.WCFHostname = Program.GetUserInput(Program.EUserInputType.WCFHostname);
}
URL = "http://" + Program.GlobalConfig.WCFHostname + ":" + Program.GlobalConfig.WCFPort + "/ASF";
}
internal bool IsServerRunning() {
return ServiceHost != null;
}
internal void StartServer() {
if (ServiceHost != null) {
return;
}
Logging.LogGenericInfo("Starting WCF server...");
ServiceHost = new ServiceHost(typeof(WCF));
ServiceHost.AddServiceEndpoint(typeof(IWCF), new BasicHttpBinding(), URL);
try {
ServiceHost.Open();
} catch (AddressAccessDeniedException) {
Logging.LogGenericWarning("WCF service could not be started because of AddressAccessDeniedException");
Logging.LogGenericWarning("If you want to use WCF service provided by ASF, consider starting ASF as administrator, or giving proper permissions");
return;
} catch (Exception e) {
Logging.LogGenericException(e);
return;
}
Logging.LogGenericInfo("WCF server ready!");
}
internal void StopServer() {
if (ServiceHost == null) {
return;
}
ServiceHost.Close();
ServiceHost = null;
}
internal string SendCommand(string input) {
if (Client == null) {
Client = new Client(new BasicHttpBinding(), new EndpointAddress(URL));
}
return Client.HandleCommand(input);
}
public string HandleCommand(string input) {
if (string.IsNullOrEmpty(input)) {
return null;
}
string[] args = input.Split(' ');
string botName;
if (args.Length > 1) { // If we have args[1] provided, use given botName
botName = args[1];
} else { // If not, just pick first one
botName = Bot.Bots.Keys.FirstOrDefault();
}
if (string.IsNullOrEmpty(botName)) {
return "ERROR: Invalid botName: " + botName;
}
Bot bot;
if (!Bot.Bots.TryGetValue(botName, out bot)) {
return "ERROR: Couldn't find any bot named: " + botName;
}
Logging.LogGenericInfo("Received command: " + input);
string command = '!' + input;
string output = bot.Response(Program.GlobalConfig.SteamOwnerID, command).Result; // TODO: This should be asynchronous
Logging.LogGenericInfo("Answered to command: " + input + " with: " + output);
return output;
}
}
internal sealed class Client : ClientBase<IWCF>, IWCF {
internal Client(Binding binding, EndpointAddress address) : base(binding, address) { }
public string HandleCommand(string input) {
try {
return Channel.HandleCommand(input);
} catch (Exception e) {
Logging.LogGenericException(e);
return null;
}
}
}
}

View File

@@ -5,7 +5,7 @@
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
Copyright 2015 Łukasz "JustArchi" Domeradzki
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
Contact: JustArchi@JustArchi.net
Licensed under the Apache License, Version 2.0 (the "License");
@@ -26,6 +26,7 @@ using HtmlAgilityPack;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Text;
@@ -34,58 +35,37 @@ using System.Xml;
namespace ArchiSteamFarm {
internal static class WebBrowser {
internal const byte HttpTimeout = 180; // In seconds
internal const byte MaxRetries = 5; // Defines maximum number of retries, UrlRequest() does not handle retry by itself (it's app responsibility)
private static readonly HttpClientHandler HttpClientHandler = new HttpClientHandler { UseCookies = false };
private static readonly HttpClient HttpClient = new HttpClient(HttpClientHandler) { Timeout = TimeSpan.FromSeconds(HttpTimeout) };
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(60)
};
internal static void Init() {
HttpClient.DefaultRequestHeaders.UserAgent.ParseAdd("ArchiSteamFarm/" + Program.Version);
HttpClient.Timeout = TimeSpan.FromSeconds(Program.GlobalConfig.HttpTimeout);
// Don't limit maximum number of allowed concurrent connections
// It's application's responsibility to handle that stuff
ServicePointManager.DefaultConnectionLimit = int.MaxValue;
// 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);
// Don't use Expect100Continue, we don't need to do that
// Set max connection limit from default of 2 to desired value
ServicePointManager.DefaultConnectionLimit = MaxConnections;
// Set max idle time from default of 100 seconds (100 * 1000) to desired value
ServicePointManager.MaxServicePointIdleTime = MaxIdleTime * 1000;
// Don't use Expect100Continue, we're sure about our POSTs, save some TCP packets
ServicePointManager.Expect100Continue = false;
}
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) {
return null;
}
HttpRequestMessage requestMessage = new HttpRequestMessage(httpMethod, request);
if (httpMethod == HttpMethod.Post && data != null) {
requestMessage.Content = new FormUrlEncodedContent(data);
}
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) {
requestMessage.Headers.Referrer = new Uri(referer);
}
HttpResponseMessage responseMessage;
try {
responseMessage = await HttpClient.SendAsync(requestMessage).ConfigureAwait(false);
} catch { // Request failed, we don't need to know the exact reason, swallow exception
return null;
}
if (responseMessage == null || !responseMessage.IsSuccessStatusCode) {
return null;
}
return responseMessage;
// Reuse ports if possible
// TODO: Mono doesn't support that feature yet
//ServicePointManager.ReusePort = true;
}
internal static async Task<HttpResponseMessage> UrlGet(string request, Dictionary<string, string> cookies = null, string referer = null) {
@@ -96,29 +76,12 @@ namespace ArchiSteamFarm {
return await UrlRequest(request, HttpMethod.Get, null, cookies, referer).ConfigureAwait(false);
}
internal static async Task<HttpResponseMessage> UrlPost(string request, Dictionary<string, string> postData = null, Dictionary<string, string> cookies = null, string referer = null) {
internal static async Task<HttpResponseMessage> UrlPost(string request, Dictionary<string, string> data = null, Dictionary<string, string> cookies = null, string referer = null) {
if (string.IsNullOrEmpty(request)) {
return null;
}
return await UrlRequest(request, HttpMethod.Post, postData, cookies, referer).ConfigureAwait(false);
}
internal static async Task<HtmlDocument> HttpResponseToHtmlDocument(HttpResponseMessage httpResponse) {
if (httpResponse == null || httpResponse.Content == null) {
return null;
}
string content = await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
if (string.IsNullOrEmpty(content)) {
return null;
}
content = WebUtility.HtmlDecode(content);
HtmlDocument htmlDocument = new HtmlDocument();
htmlDocument.LoadHtml(content);
return htmlDocument;
return await UrlRequest(request, HttpMethod.Post, data, cookies, referer).ConfigureAwait(false);
}
internal static async Task<string> UrlGetToContent(string request, Dictionary<string, string> cookies = null, string referer = null) {
@@ -126,59 +89,36 @@ namespace ArchiSteamFarm {
return null;
}
HttpResponseMessage responseMessage = await UrlGet(request, cookies, referer).ConfigureAwait(false);
if (responseMessage == null || responseMessage.Content == null) {
return null;
}
return await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false);
}
internal static async Task<string> UrlPostToContent(string request, Dictionary<string, string> postData = null, Dictionary<string, string> cookies = null, string referer = null) {
if (string.IsNullOrEmpty(request)) {
return null;
}
HttpResponseMessage responseMessage = await UrlPost(request, postData, cookies, referer).ConfigureAwait(false);
if (responseMessage == null || responseMessage.Content == null) {
return null;
}
return await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false);
}
internal static async Task<string> UrlGetToTitle(string request, Dictionary<string, string> cookies = null, string referer = null) {
if (string.IsNullOrEmpty(request)) {
return null;
}
HtmlDocument htmlDocument = await UrlGetToHtmlDocument(request, cookies, referer).ConfigureAwait(false);
if (htmlDocument == null) {
return null;
}
HtmlNode htmlNode = htmlDocument.DocumentNode.SelectSingleNode("//head/title");
if (htmlNode == null) {
return null;
}
return htmlNode.InnerText;
}
internal static async Task<HtmlDocument> UrlGetToHtmlDocument(string request, Dictionary<string, string> cookies = null, string referer = null) {
if (string.IsNullOrEmpty(request)) {
return null;
}
HttpResponseMessage httpResponse = await UrlGet(request, cookies, referer).ConfigureAwait(false);
if (httpResponse == null) {
return null;
}
return await HttpResponseToHtmlDocument(httpResponse).ConfigureAwait(false);
if (httpResponse.Content == null) {
return null;
}
return await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
}
internal static async Task<JArray> UrlGetToJArray(string request, Dictionary<string, string> cookies = null, string referer = null) {
internal static async Task<Stream> UrlGetToStream(string request, Dictionary<string, string> cookies = null, string referer = null) {
if (string.IsNullOrEmpty(request)) {
return null;
}
HttpResponseMessage httpResponse = await UrlGet(request, cookies, referer).ConfigureAwait(false);
if (httpResponse == null) {
return null;
}
if (httpResponse.Content == null) {
return null;
}
return await httpResponse.Content.ReadAsStreamAsync().ConfigureAwait(false);
}
internal static async Task<HtmlDocument> UrlGetToHtmlDocument(string request, Dictionary<string, string> cookies = null, string referer = null) {
if (string.IsNullOrEmpty(request)) {
return null;
}
@@ -188,16 +128,11 @@ namespace ArchiSteamFarm {
return null;
}
JArray jArray;
content = WebUtility.HtmlDecode(content);
HtmlDocument htmlDocument = new HtmlDocument();
htmlDocument.LoadHtml(content);
try {
jArray = JArray.Parse(content);
} catch (Exception e) {
Logging.LogGenericException("WebBrowser", e);
return null;
}
return jArray;
return htmlDocument;
}
internal static async Task<JObject> UrlGetToJObject(string request, Dictionary<string, string> cookies = null, string referer = null) {
@@ -215,7 +150,7 @@ namespace ArchiSteamFarm {
try {
jObject = JObject.Parse(content);
} catch (Exception e) {
Logging.LogGenericException("WebBrowser", e);
Logging.LogGenericException(e);
return null;
}
@@ -237,11 +172,62 @@ namespace ArchiSteamFarm {
try {
xmlDocument.LoadXml(content);
} catch (XmlException e) {
Logging.LogGenericException("WebBrowser", e);
Logging.LogGenericException(e);
return null;
}
return xmlDocument;
}
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) {
return null;
}
HttpResponseMessage responseMessage;
using (HttpRequestMessage requestMessage = new HttpRequestMessage(httpMethod, request)) {
if (data != null && data.Count > 0) {
try {
requestMessage.Content = new FormUrlEncodedContent(data);
} catch (UriFormatException e) {
Logging.LogGenericException(e);
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 (!string.IsNullOrEmpty(referer)) {
requestMessage.Headers.Referrer = new Uri(referer);
}
try {
responseMessage = await HttpClient.SendAsync(requestMessage).ConfigureAwait(false);
} catch { // Request failed, we don't need to know the exact reason, swallow exception
return null;
}
}
if (responseMessage == null) {
return null;
}
if (!responseMessage.IsSuccessStatusCode) {
if (Program.GlobalConfig.Debug) {
Logging.LogGenericError("Request: " + request + "failed!");
Logging.LogGenericError("Status code: " + responseMessage.StatusCode);
Logging.LogGenericError("Content: " + await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false));
}
return null;
}
return responseMessage;
}
}
}

View File

@@ -0,0 +1,27 @@
{
"Debug": false,
"AutoUpdates": true,
"UpdateChannel": 1,
"SteamProtocol": 6,
"SteamOwnerID": 0,
"MaxFarmingTime": 10,
"IdleFarmingPeriod": 3,
"FarmingDelay": 5,
"AccountPlayingDelay": 5,
"LoginLimiterDelay": 7,
"InventoryLimiterDelay": 3,
"ForceHttp": false,
"HttpTimeout": 60,
"WCFHostname": "localhost",
"WCFPort": 1242,
"LogToFile": true,
"Statistics": true,
"HackIgnoreMachineID": false,
"Blacklist": [
267420,
303700,
335590,
368020,
425280
]
}

View File

@@ -0,0 +1,26 @@
{
"Enabled": false,
"StartOnLaunch": true,
"SteamLogin": null,
"SteamPassword": null,
"SteamParentalPIN": "0",
"SteamApiKey": null,
"SteamMasterID": 0,
"SteamMasterClanID": 0,
"CardDropsRestricted": false,
"DismissInventoryNotifications": true,
"FarmOffline": false,
"HandleOfflineMessages": false,
"ForwardKeysToOtherBots": false,
"DistributeKeys": false,
"UseAsfAsMobileAuthenticator": false,
"ShutdownOnFarmingFinished": false,
"SendOnFarmingFinished": false,
"SteamTradeToken": null,
"SendTradePeriod": 0,
"AcceptConfirmationsPeriod": 0,
"CustomGamePlayedWhileIdle": null,
"GamesPlayedWhileIdle": [
0
]
}

View File

@@ -1,92 +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 -->
<!-- Default values used in config match default ASF values when given config property is not found -->
<!-- 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 -->
<!-- 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 -->
<!-- TIP: This bot instance won't run unless below switch is set to "true" -->
<Enabled type="bool" value="false"/>
<!-- This is your steam login, the one you use for logging in to steam -->
<!-- TIP: You can use "null" if you wish to enter login on every startup -->
<SteamLogin type="string" value="null"/>
<!-- This is your steam password, the one you use for logging in to steam -->
<!-- TIP: You can use "null" if you wish to enter password on every startup -->
<SteamPassword type="string" value="null"/>
<!-- This is steam nickname, the one you want to use for bot. Can be anything up to 32 characters -->
<!-- TIP: You can use "null" if you wish to preserve your actual nickname -->
<SteamNickname type="string" value="null"/>
<!-- This is your bot's API key, get one at https://steamcommunity.com/dev/apikey while logged in as bot, domain doesn't matter -->
<!-- TIP: You can use "null", but it will disable all API-based functionalities such as trading -->
<SteamApiKey type="string" value="null"/>
<!-- This is your parental PIN if you use steam parental functionality -->
<!-- TIP: Most likely you don't want to change it. You can use "null" if you wish to enter PIN on every startup, 0 means there is no PIN -->
<SteamParentalPIN type="string" value="0"/>
<!-- This is steamID64 of the bot-master - you, for example "76561198006963719" -->
<!-- You can get one e.g. by logging in to http://steamrep.com/ -->
<!-- TIP: You can use "0", but bot won't accept steam cd-keys or trades from anybody" -->
<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" -->
<!-- TIP: 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 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 -->
<!-- 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 -->
<!-- As there is no magical way to detect it by ASF, I made this option config-based switch -->
<!-- TIP: 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 -->
<!-- Please note that bot won't be able to respond to any commands when this property is set to "true" -->
<!-- TIP: Setting this to "true" may be useful for primary accounts, to not show as online when you're not here -->
<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 -->
<HandleOfflineMessages type="bool" value="false"/>
<!-- This switch defines if bot should disconnect once farming is finished -->
<!-- When no bots are active, ASF will shutdown as well -->
<!-- Some people may want to keep their bots 24/7, other disconnect them after job is done -->
<!-- Choose yourself what you prefer -->
<ShutdownOnFarmingFinished type="bool" value="false"/>
<!-- Comma-separated list of IDs that should not be considered for farming -->
<!-- Default value includes appIDs that are wrongly appearing on the profile, e.g. Monster Summer Sale -->
<!-- TIP: Most likely you don't want to change it -->
<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 -->
<!-- Consider leaving it at "true", this way I can check how many running bots are active -->
<!-- That directly affects my willings to work on the project, as I can see how many users are actually using it -->
<!-- TIP: Group link is 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,8 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<!-- This is minimalistic config to "just make ASF work" -->
<!-- For full-fledged config, please take a look at example.xml -->
<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.1-beta3" targetFramework="net45" />
<package id="Newtonsoft.Json" version="8.0.3" targetFramework="net451" />
<package id="protobuf-net" version="2.0.0.668" targetFramework="net45" />
<package id="SteamKit2" version="1.7.0" targetFramework="net452" />
</packages>

View File

@@ -0,0 +1,86 @@
/*
_ _ _ ____ _ _____
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
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 class ASFConfig {
internal static HashSet<ASFConfig> ASFConfigs = new HashSet<ASFConfig>();
internal string FilePath { get; set; }
protected ASFConfig() {
ASFConfigs.Add(this);
}
protected ASFConfig(string filePath) : this() {
FilePath = filePath;
}
internal virtual void Save() {
lock (FilePath) {
try {
File.WriteAllText(FilePath, JsonConvert.SerializeObject(this, Formatting.Indented));
} catch (Exception e) {
Logging.LogGenericException(e);
}
}
}
internal virtual void Remove() {
string queryPath = Path.GetFileNameWithoutExtension(FilePath);
lock (FilePath) {
foreach (var configFile in Directory.EnumerateFiles(Program.ConfigDirectory, queryPath + ".*")) {
try {
File.Delete(configFile);
} catch (Exception e) {
Logging.LogGenericException(e);
}
}
}
ASFConfigs.Remove(this);
}
internal virtual void Rename(string botName) {
if (string.IsNullOrEmpty(botName)) {
return;
}
string queryPath = Path.GetFileNameWithoutExtension(FilePath);
lock (FilePath) {
foreach (var file in Directory.EnumerateFiles(Program.ConfigDirectory, queryPath + ".*")) {
try {
File.Move(file, Path.Combine(Program.ConfigDirectory, botName + Path.GetExtension(file)));
} 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,130 @@
/*
_ _ _ ____ _ _____
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
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 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]
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 ForwardKeysToOtherBots { get; set; } = false;
[JsonProperty(Required = Required.DisallowNull)]
public bool DistributeKeys { get; set; } = false;
[JsonProperty(Required = Required.DisallowNull)]
public bool UseAsfAsMobileAuthenticator { get; set; } = false;
[JsonProperty(Required = Required.DisallowNull)]
public bool ShutdownOnFarmingFinished { get; set; } = false;
[JsonProperty(Required = Required.DisallowNull)]
public bool SendOnFarmingFinished { get; set; } = false;
[JsonProperty]
public string SteamTradeToken { get; set; } = null;
[JsonProperty(Required = Required.DisallowNull)]
public byte SendTradePeriod { get; set; } = 0;
[JsonProperty(Required = Required.DisallowNull)]
public byte AcceptConfirmationsPeriod { get; set; } = 0;
[JsonProperty]
public string CustomGamePlayedWhileIdle { get; set; } = null;
[JsonProperty(Required = Required.DisallowNull)]
public List<uint> GamesPlayedWhileIdle { get; set; } = new List<uint>();
internal static BotConfig Load(string filePath) {
if (string.IsNullOrEmpty(filePath)) {
return null;
}
if (!File.Exists(filePath)) {
return new BotConfig(filePath);
}
BotConfig botConfig;
try {
botConfig = JsonConvert.DeserializeObject<BotConfig>(File.ReadAllText(filePath));
} catch (Exception e) {
Logging.LogGenericException(e);
return new BotConfig(filePath);
}
botConfig.FilePath = filePath;
return botConfig;
}
// This constructor is used only by deserializer
private BotConfig() : base() { }
private BotConfig(string filePath) : base(filePath) {
FilePath = filePath;
GamesPlayedWhileIdle.Add(0);
Save();
}
}
}

View File

@@ -0,0 +1,128 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{C3F6FE68-5E75-415E-BEA1-1E7C16D6A433}</ProjectGuid>
<OutputType>WinExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>ConfigGenerator</RootNamespace>
<AssemblyName>ConfigGenerator</AssemblyName>
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>none</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>
</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>cirno.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.8.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="ASFConfig.cs" />
<Compile Include="BotConfig.cs" />
<Compile Include="DialogBox.cs" />
<Compile Include="EnhancedPropertyGrid.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Debugging.cs" />
<Compile Include="GlobalConfig.cs" />
<Compile Include="ConfigPage.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Logging.cs" />
<Compile Include="MainForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="MainForm.Designer.cs">
<DependentUpon>MainForm.cs</DependentUpon>
</Compile>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Tutorial.cs" />
<EmbeddedResource Include="ConfigPage.resx">
<DependentUpon>ConfigPage.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="MainForm.resx">
<DependentUpon>MainForm.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
</EmbeddedResource>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<None Include="packages.config" />
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<Content Include="cirno.ico" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent Condition=" '$(OS)' != 'Unix' AND '$(ConfigurationName)' == 'Release' ">
"$(SolutionDir)tools\ILRepack\ILRepack.exe" /ndebug /internalize /parallel /targetplatform:v4 /wildcards /out:"$(SolutionDir)out\ASF-GUI.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll"
del "$(SolutionDir)out\ASF-GUI.exe.config"
</PostBuildEvent>
<PostBuildEvent Condition=" '$(OS)' == 'Unix' AND '$(ConfigurationName)' == 'Release' ">
mono -O=all "$(SolutionDir)tools/ILRepack/ILRepack.exe" /ndebug /internalize /parallel /targetplatform:v4 /wildcards /out:"$(SolutionDir)out/ASF-GUI.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll"
rm "$(SolutionDir)out/ASF-GUI.exe.config"
</PostBuildEvent>
</PropertyGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@@ -0,0 +1,57 @@
/*
_ _ _ ____ _ _____
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
Contact: JustArchi@JustArchi.net
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
using System.IO;
using System.Windows.Forms;
namespace ConfigGenerator {
internal class ConfigPage : TabPage {
internal readonly ASFConfig ASFConfig;
private EnhancedPropertyGrid EnhancedPropertyGrid;
internal ConfigPage(ASFConfig config) : base() {
if (config == null) {
return;
}
ASFConfig = config;
RefreshText();
EnhancedPropertyGrid = new EnhancedPropertyGrid(config);
Controls.Add(EnhancedPropertyGrid);
}
internal void RefreshText() {
Text = Path.GetFileNameWithoutExtension(ASFConfig.FilePath);
}
private void InitializeComponent() {
SuspendLayout();
ResumeLayout(false);
}
}
}

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

@@ -5,7 +5,7 @@
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
Copyright 2015 Łukasz "JustArchi" Domeradzki
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
Contact: JustArchi@JustArchi.net
Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,16 +22,14 @@
*/
namespace ArchiSteamFarm {
internal sealed class SteamItem {
// REF: https://developer.valvesoftware.com/wiki/Steam_Web_API/IEconService
internal string appid { get; set; }
internal string contextid { get; set; }
internal string assetid { get; set; }
internal string currencyid { get; set; }
internal string classid { get; set; }
internal string instanceid { get; set; }
internal string amount { get; set; }
internal bool missing { get; set; }
namespace ConfigGenerator {
internal static class Debugging {
#if DEBUG
internal static readonly bool IsDebugBuild = true;
#else
internal static readonly bool IsDebugBuild = false;
#endif
internal static bool IsReleaseBuild => !IsDebugBuild;
}
}

View File

@@ -0,0 +1,119 @@
/*
_ _ _ ____ _ _____
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
Contact: JustArchi@JustArchi.net
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
using System;
using System.Drawing;
using System.Windows.Forms;
namespace ConfigGenerator {
class DialogBox {
public static DialogResult InputBox(string title, string promptText, out string value) {
if (string.IsNullOrEmpty(title) || string.IsNullOrEmpty(promptText)) {
value = null;
return DialogResult.Abort;
}
Form form = new Form();
Label label = new Label();
TextBox textBox = new TextBox();
textBox.Width = 1000;
Button buttonOk = new Button();
Button buttonCancel = new Button();
form.Text = title;
label.Text = promptText;
buttonOk.Text = "OK";
buttonCancel.Text = "Cancel";
buttonOk.DialogResult = DialogResult.OK;
buttonCancel.DialogResult = DialogResult.Cancel;
label.SetBounds(9, 20, 372, 13);
textBox.SetBounds(12, 36, 372, 20);
buttonOk.SetBounds(228, 72, 75, 23);
buttonCancel.SetBounds(309, 72, 75, 23);
label.AutoSize = true;
textBox.Anchor = textBox.Anchor | AnchorStyles.Right;
buttonOk.Anchor = AnchorStyles.Bottom | AnchorStyles.Right;
buttonCancel.Anchor = AnchorStyles.Bottom | AnchorStyles.Right;
form.ClientSize = new Size(396, 107);
form.Controls.AddRange(new Control[] { label, textBox, buttonOk, buttonCancel });
form.ClientSize = new Size(Math.Max(300, label.Right + 10), form.ClientSize.Height);
form.FormBorderStyle = FormBorderStyle.FixedDialog;
form.StartPosition = FormStartPosition.CenterScreen;
form.MinimizeBox = false;
form.MaximizeBox = false;
form.AcceptButton = buttonOk;
form.CancelButton = buttonCancel;
DialogResult dialogResult = form.ShowDialog();
value = textBox.Text;
return dialogResult;
}
public static DialogResult YesNoBox(string title, string promptText) {
if (string.IsNullOrEmpty(title) || string.IsNullOrEmpty(promptText)) {
return DialogResult.Abort;
}
Form form = new Form();
Label label = new Label();
Button buttonOk = new Button();
Button buttonCancel = new Button();
form.Text = title;
label.Text = promptText;
buttonOk.Text = "Yes";
buttonCancel.Text = "No";
buttonOk.DialogResult = DialogResult.Yes;
buttonCancel.DialogResult = DialogResult.No;
label.SetBounds(9, 20, 372, 13);
buttonOk.SetBounds(228, 50, 75, 23);
buttonCancel.SetBounds(309, 50, 75, 23);
label.AutoSize = true;
buttonOk.Anchor = AnchorStyles.Bottom | AnchorStyles.Right;
buttonCancel.Anchor = AnchorStyles.Bottom | AnchorStyles.Right;
form.ClientSize = new Size(396, 80);
form.Controls.AddRange(new Control[] { label, buttonOk, buttonCancel });
form.ClientSize = new Size(Math.Max(300, label.Right + 10), form.ClientSize.Height);
form.FormBorderStyle = FormBorderStyle.FixedDialog;
form.StartPosition = FormStartPosition.CenterScreen;
form.MinimizeBox = false;
form.MaximizeBox = false;
form.AcceptButton = buttonOk;
form.CancelButton = buttonCancel;
DialogResult dialogResult = form.ShowDialog();
return dialogResult;
}
}
}

View File

@@ -0,0 +1,73 @@
/*
_ _ _ ____ _ _____
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
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.Windows.Forms;
namespace ConfigGenerator {
internal sealed class EnhancedPropertyGrid : PropertyGrid {
private ASFConfig ASFConfig;
internal EnhancedPropertyGrid(ASFConfig config) : base() {
if (config == null) {
return;
}
ASFConfig = config;
SelectedObject = config;
Anchor = AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top;
Dock = DockStyle.Fill;
HelpVisible = false;
ToolbarVisible = false;
}
protected override void OnPropertyValueChanged(PropertyValueChangedEventArgs e) {
if (e == null) {
return;
}
base.OnPropertyValueChanged(e);
ASFConfig.Save();
BotConfig botConfig = ASFConfig as BotConfig;
if (botConfig != null) {
if (botConfig.Enabled) {
Tutorial.OnAction(Tutorial.EPhase.BotEnabled);
if (!string.IsNullOrEmpty(botConfig.SteamLogin) && !string.IsNullOrEmpty(botConfig.SteamPassword)) {
Tutorial.OnAction(Tutorial.EPhase.BotReady);
}
}
return;
}
GlobalConfig globalConfig = ASFConfig as GlobalConfig;
if (globalConfig != null) {
if (globalConfig.SteamOwnerID != 0) {
Tutorial.OnAction(Tutorial.EPhase.GlobalConfigReady);
}
return;
}
}
}
}

View File

@@ -0,0 +1,172 @@
/*
_ _ _ ____ _ _____
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
Copyright 2015-2016 Łukasz "JustArchi" Domeradzki
Contact: JustArchi@JustArchi.net
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Sockets;
namespace ConfigGenerator {
internal sealed class GlobalConfig : ASFConfig {
internal enum EUpdateChannel : byte {
Unknown,
Stable,
Experimental
}
private const byte DefaultMaxFarmingTime = 10;
private const byte DefaultFarmingDelay = 5;
private const byte DefaultHttpTimeout = 60;
private const ushort DefaultWCFPort = 1242;
private static readonly ProtocolType DefaultSteamProtocol = ProtocolType.Tcp;
// This is hardcoded blacklist which should not be possible to change
internal static readonly HashSet<uint> GlobalBlacklist = new HashSet<uint> { 267420, 303700, 335590, 368020, 425280 };
[JsonProperty(Required = Required.DisallowNull)]
public bool Debug { get; set; } = false;
[JsonProperty(Required = Required.DisallowNull)]
public bool AutoUpdates { get; set; } = true;
[JsonProperty(Required = Required.DisallowNull)]
public EUpdateChannel UpdateChannel { get; set; } = EUpdateChannel.Stable;
[JsonProperty(Required = Required.DisallowNull)]
public ProtocolType SteamProtocol { get; set; } = DefaultSteamProtocol;
[JsonProperty(Required = Required.DisallowNull)]
public ulong SteamOwnerID { get; set; } = 0;
[JsonProperty(Required = Required.DisallowNull)]
public byte MaxFarmingTime { get; set; } = DefaultMaxFarmingTime;
[JsonProperty(Required = Required.DisallowNull)]
public byte IdleFarmingPeriod { get; set; } = 3;
[JsonProperty(Required = Required.DisallowNull)]
public byte FarmingDelay { get; set; } = DefaultFarmingDelay;
[JsonProperty(Required = Required.DisallowNull)]
public byte AccountPlayingDelay { get; set; } = 5;
[JsonProperty(Required = Required.DisallowNull)]
public byte LoginLimiterDelay { get; set; } = 7;
[JsonProperty(Required = Required.DisallowNull)]
public byte InventoryLimiterDelay { get; set; } = 3;
[JsonProperty(Required = Required.DisallowNull)]
public bool ForceHttp { get; set; } = false;
[JsonProperty(Required = Required.DisallowNull)]
public byte HttpTimeout { get; set; } = DefaultHttpTimeout;
[JsonProperty]
public string WCFHostname { get; set; } = "localhost";
[JsonProperty(Required = Required.DisallowNull)]
public ushort WCFPort { get; set; } = DefaultWCFPort;
[JsonProperty(Required = Required.DisallowNull)]
public bool LogToFile { get; set; } = true;
[JsonProperty(Required = Required.DisallowNull)]
public bool Statistics { get; set; } = true;
// TODO: Please remove me immediately after https://github.com/SteamRE/SteamKit/issues/254 gets fixed
[JsonProperty(Required = Required.DisallowNull)]
public bool HackIgnoreMachineID { get; set; } = false;
[JsonProperty(Required = Required.DisallowNull)]
public List<uint> Blacklist { get; set; } = new List<uint>();
internal static GlobalConfig Load(string filePath) {
if (string.IsNullOrEmpty(filePath)) {
return null;
}
if (!File.Exists(filePath)) {
return new GlobalConfig(filePath);
}
GlobalConfig globalConfig;
try {
globalConfig = JsonConvert.DeserializeObject<GlobalConfig>(File.ReadAllText(filePath));
} catch (Exception e) {
Logging.LogGenericException(e);
return new GlobalConfig(filePath);
}
globalConfig.FilePath = filePath;
// SK2 supports only TCP and UDP steam protocols
// Ensure that user can't screw this up
switch (globalConfig.SteamProtocol) {
case ProtocolType.Tcp:
case ProtocolType.Udp:
break;
default:
Logging.LogGenericWarning("Configured SteamProtocol is invalid: " + globalConfig.SteamProtocol + ". Value of " + DefaultSteamProtocol + " will be used instead");
globalConfig.SteamProtocol = DefaultSteamProtocol;
break;
}
// User might not know what he's doing
// Ensure that he can't screw core ASF variables
if (globalConfig.MaxFarmingTime == 0) {
Logging.LogGenericWarning("Configured MaxFarmingTime is invalid: " + globalConfig.MaxFarmingTime + ". Value of " + DefaultMaxFarmingTime + " will be used instead");
globalConfig.MaxFarmingTime = DefaultMaxFarmingTime;
}
if (globalConfig.FarmingDelay == 0) {
Logging.LogGenericWarning("Configured FarmingDelay is invalid: " + globalConfig.FarmingDelay + ". Value of " + DefaultFarmingDelay + " will be used instead");
globalConfig.FarmingDelay = DefaultFarmingDelay;
}
if (globalConfig.HttpTimeout == 0) {
Logging.LogGenericWarning("Configured HttpTimeout is invalid: " + globalConfig.HttpTimeout + ". Value of " + DefaultHttpTimeout + " will be used instead");
globalConfig.HttpTimeout = DefaultHttpTimeout;
}
if (globalConfig.WCFPort == 0) {
Logging.LogGenericWarning("Configured WCFPort is invalid: " + globalConfig.WCFPort + ". Value of " + DefaultWCFPort + " will be used instead");
globalConfig.WCFPort = DefaultWCFPort;
}
return globalConfig;
}
// This constructor is used only by deserializer
private GlobalConfig() : base() { }
private GlobalConfig(string filePath) : base(filePath) {
FilePath = filePath;
Blacklist.AddRange(GlobalBlacklist);
Save();
}
}
}

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 ConfigGenerator {
internal static class Logging {
internal static void LogGenericInfo(string message) {
if (string.IsNullOrEmpty(message)) {
return;
}
MessageBox.Show(message, "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
internal static void LogGenericWTF(string message, [CallerMemberName] string previousMethodName = "") {
if (string.IsNullOrEmpty(message)) {
return;
}
MessageBox.Show(previousMethodName + "() " + message, "WTF", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
internal static void LogGenericError(string message, [CallerMemberName] string previousMethodName = "") {
if (string.IsNullOrEmpty(message)) {
return;
}
MessageBox.Show(previousMethodName + "() " + message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
internal static void LogGenericException(Exception exception, [CallerMemberName] string previousMethodName = "") {
if (exception == null) {
return;
}
MessageBox.Show(previousMethodName + "() " + exception.Message + Environment.NewLine + exception.StackTrace, "Exception", MessageBoxButtons.OK, MessageBoxIcon.Error);
if (exception.InnerException != null) {
LogGenericException(exception.InnerException, previousMethodName);
}
}
internal static void LogGenericWarning(string message, [CallerMemberName] string previousMethodName = "") {
if (string.IsNullOrEmpty(message)) {
return;
}
MessageBox.Show(previousMethodName + "() " + message, "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
internal static void LogNullError(string nullObjectName, [CallerMemberName] string previousMethodName = "") {
if (string.IsNullOrEmpty(nullObjectName)) {
return;
}
LogGenericError(nullObjectName + " is null!", previousMethodName);
}
[Conditional("DEBUG")]
internal static void LogGenericDebug(string message, [CallerMemberName] string previousMethodName = "") {
if (string.IsNullOrEmpty(message)) {
return;
}
MessageBox.Show(previousMethodName + "() " + message, "Debug", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
}

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

200
ConfigGenerator/MainForm.cs Normal file
View File

@@ -0,0 +1,200 @@
/*
_ _ _ ____ _ _____
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
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.IO;
using System.Windows.Forms;
namespace ConfigGenerator {
public partial class MainForm : Form {
private const byte ReservedTabs = 3;
private ConfigPage ASFTab;
private TabPage RemoveTab = new TabPage() {
Text = "-",
};
private TabPage RenameTab = new TabPage() {
Text = "~",
};
private TabPage NewTab = new TabPage() {
Text = "+",
};
private TabPage OldTab;
public MainForm() {
InitializeComponent();
}
private void MainForm_Load(object sender, EventArgs e) {
if (sender == null || e == null) {
return;
}
ASFTab = new ConfigPage(GlobalConfig.Load(Path.Combine(Program.ConfigDirectory, Program.GlobalConfigFile)));
MainTab.TabPages.Add(ASFTab);
foreach (var configFile in Directory.EnumerateFiles(Program.ConfigDirectory, "*.json")) {
string botName = Path.GetFileNameWithoutExtension(configFile);
switch (botName) {
case Program.ASF:
case "example":
case "minimal":
continue;
}
MainTab.TabPages.Add(new ConfigPage(BotConfig.Load(configFile)));
Tutorial.Enabled = false;
}
MainTab.TabPages.AddRange(new TabPage[] { RemoveTab, RenameTab, NewTab });
Tutorial.OnAction(Tutorial.EPhase.Start);
}
private void MainTab_Selected(object sender, TabControlEventArgs e) {
if (sender == null || e == null) {
return;
}
if (e.TabPage == RemoveTab) {
ConfigPage configPage = OldTab as ConfigPage;
if (configPage == null) {
MainTab.SelectedIndex = -1;
return;
}
if (configPage == ASFTab) {
MainTab.SelectedTab = ASFTab;
Logging.LogGenericError("You can't remove global config!");
return;
}
MainTab.SelectedTab = configPage;
if (DialogBox.YesNoBox("Removal", "Do you really want to remove this config?") != DialogResult.Yes) {
return;
}
MainTab.SelectedIndex = 0;
configPage.ASFConfig.Remove();
MainTab.TabPages.Remove(configPage);
} else if (e.TabPage == RenameTab) {
ConfigPage configPage = OldTab as ConfigPage;
if (configPage == null) {
MainTab.SelectedIndex = -1;
return;
}
if (configPage == ASFTab) {
MainTab.SelectedTab = ASFTab;
Logging.LogGenericError("You can't rename global config!");
return;
}
MainTab.SelectedTab = configPage;
string input;
if (DialogBox.InputBox("Rename", "Your new bot name:", out input) != DialogResult.OK) {
return;
}
if (string.IsNullOrEmpty(input)) {
Logging.LogGenericError("Your bot name is empty!");
return;
}
configPage.ASFConfig.Rename(input);
configPage.RefreshText();
} else if (e.TabPage == NewTab) {
ConfigPage configPage = OldTab as ConfigPage;
if (configPage == null) {
MainTab.SelectedIndex = -1;
return;
}
MainTab.SelectedTab = configPage;
Tutorial.OnAction(Tutorial.EPhase.BotNickname);
string input;
if (DialogBox.InputBox("New", "Your new bot name:", out input) != DialogResult.OK) {
return;
}
if (string.IsNullOrEmpty(input)) {
Logging.LogGenericError("Your bot name is empty!");
return;
}
foreach (ASFConfig config in ASFConfig.ASFConfigs) {
if (Path.GetFileNameWithoutExtension(config.FilePath).Equals(input)) {
Logging.LogGenericError("Bot with such name exists already!");
return;
}
}
input = Path.Combine(Program.ConfigDirectory, input + ".json");
ConfigPage newConfigPage = new ConfigPage(BotConfig.Load(input));
MainTab.TabPages.Insert(MainTab.TabPages.Count - ReservedTabs, newConfigPage);
MainTab.SelectedTab = newConfigPage;
Tutorial.OnAction(Tutorial.EPhase.BotNicknameFinished);
} else if (e.TabPage == ASFTab) {
Tutorial.OnAction(Tutorial.EPhase.GlobalConfigOpened);
}
}
private void MainTab_Deselecting(object sender, TabControlCancelEventArgs e) {
if (sender == null || e == null) {
return;
}
OldTab = e.TabPage;
}
private void MainForm_Shown(object sender, EventArgs e) {
if (sender == null || e == null) {
return;
}
Tutorial.OnAction(Tutorial.EPhase.Shown);
}
private void MainForm_HelpButtonClicked(object sender, System.ComponentModel.CancelEventArgs e) {
if (sender == null || e == null) {
return;
}
e.Cancel = true;
Tutorial.OnAction(Tutorial.EPhase.Help);
Process.Start("https://github.com/JustArchi/ArchiSteamFarm/wiki/Configuration");
Tutorial.OnAction(Tutorial.EPhase.HelpFinished);
}
}
}

File diff suppressed because it is too large Load Diff

101
ConfigGenerator/Program.cs Normal file
View File

@@ -0,0 +1,101 @@
/*
_ _ _ ____ _ _____
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
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 Assembly Assembly = Assembly.GetExecutingAssembly();
private static readonly string ExecutableFile = Assembly.Location;
private static readonly string ExecutableName = Path.GetFileName(ExecutableFile);
private static readonly string ExecutableDirectory = Path.GetDirectoryName(ExecutableFile);
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
private static void Main() {
Init();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
private static void Init() {
AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionHandler;
TaskScheduler.UnobservedTaskException += UnobservedTaskExceptionHandler;
Directory.SetCurrentDirectory(ExecutableDirectory);
// Allow loading configs from source tree if it's a debug build
if (Debugging.IsDebugBuild) {
// Common structure is bin/(x64/)Debug/ArchiSteamFarm.exe, so we allow up to 4 directories up
for (byte i = 0; i < 4; i++) {
Directory.SetCurrentDirectory("..");
if (Directory.Exists(ASFDirectory)) {
Directory.SetCurrentDirectory(ASFDirectory);
break;
}
}
// If config directory doesn't exist after our adjustment, abort all of that
if (!Directory.Exists(ConfigDirectory)) {
Directory.SetCurrentDirectory(ExecutableDirectory);
}
}
if (!Directory.Exists(ConfigDirectory)) {
Logging.LogGenericError("Config directory could not be found!");
Environment.Exit(1);
}
}
private static void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs args) {
if (sender == null || args == null) {
return;
}
Logging.LogGenericException((Exception) args.ExceptionObject);
}
private static void UnobservedTaskExceptionHandler(object sender, UnobservedTaskExceptionEventArgs args) {
if (sender == null || args == null) {
return;
}
Logging.LogGenericException(args.Exception);
}
}
}

View File

@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("ConfigGenerator")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ConfigGenerator")]
[assembly: AssemblyCopyright("Copyright © 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,62 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace ConfigGenerator.Properties {
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if ((resourceMan == null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ConfigGenerator.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
}
}

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>

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 { get; set; } = true;
private static EPhase NextPhase = EPhase.Start;
internal static void OnAction(EPhase phase) {
if (!Enabled || phase != NextPhase) {
return;
}
switch (phase) {
case EPhase.Unknown:
break;
case EPhase.Start:
Logging.LogGenericInfo("Hello there! I noticed that you're using ASF Config Generator for the first time, so let me help you a bit.");
break;
case EPhase.Shown:
Logging.LogGenericInfo("You can now notice the main ASF Config Generator screen, it's really easy to use!");
Logging.LogGenericInfo("At the top of the window you can notice currently loaded configs, and 3 extra buttons for removing, renaming and adding new ones.");
Logging.LogGenericInfo("In the middle of the window you will be able to configure all config properties that are available for you.");
Logging.LogGenericInfo("In the top right corner you can find help button [?] which will redirect you to ASF wiki where you can find more information.");
Logging.LogGenericInfo("Please click the help button to continue.");
break;
case EPhase.Help:
Logging.LogGenericInfo("That's right! On ASF wiki you can find detailed help about every config property you're going to configure in a moment.");
break;
case EPhase.HelpFinished:
Logging.LogGenericInfo("Alright, let's start configuring our ASF. Click on the plus [+] button to add your first steam account to ASF!");
break;
case EPhase.BotNickname:
Logging.LogGenericInfo("That's right! 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.");
break;
case EPhase.BotNicknameFinished:
Logging.LogGenericInfo("As you can see your bot config is now ready to configure!");
Logging.LogGenericInfo("First thing that you want to do is switching \"Enabled\" property from False to True, try it!");
break;
case EPhase.BotEnabled:
Logging.LogGenericInfo("That's right! Now your bot instance is enabled. You need to configure at least 2 more config properties - \"SteamLogin\" and \"SteamPassword\". The tutorial will continue after you're done with it. Remember to visit ASF wiki by clicking the help icon if you're unsure how given property should be configured!");
break;
case EPhase.BotReady:
Logging.LogGenericInfo("If the data you put is proper, then your bot is ready to run! We need to do only one more thing now. Visit global ASF config, which is labelled as \"ASF\" on your config tab.");
break;
case EPhase.GlobalConfigOpened:
Logging.LogGenericInfo("While bot config affects only given bot instance you're configuring, global config affects whole ASF process, including all configured bots.");
Logging.LogGenericInfo("In order to fully configure your ASF, I suggest to fill \"SteamOwnerID\" property. Remember, if you don't know what to put, help button is always there for you!");
break;
case EPhase.GlobalConfigReady:
Logging.LogGenericInfo("Your ASF is now ready! Simply launch ASF process by double-clicking ASF.exe binary and if you did everything properly, you should now notice that ASF logs in on your account and starts farming. If you have SteamGuard or 2FA authorization enabled, ASF will ask you for that once");
Logging.LogGenericInfo("Congratulations! You've done everything that is needed in order to make ASF \"work\". I highly recommend reading the wiki now, as ASF offers some really neat features for you to configure, such as offline farming or deciding upon most efficient cards farming algorithm.");
Logging.LogGenericInfo("If you'd like to add another steam account for farming, simply click the plus [+] button and add another instance. You can also rename bots and remove them with 2 other buttons. 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="8.0.3" targetFramework="net451" />
</packages>

View File

@@ -1,6 +1,15 @@
ArchiSteamFarm
===================
[![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 on one account at given time, requires steam client running in background, and launches additional processes imitiating "game playing" status, ASF doesn't require any steam client running in the background, doesn't launch any additional processes and is made to handle unlimited steam accounts at once. In addition to that, it's meant to be run on servers or other desktop-less machines, and features full Mono support, which makes it possible to launch on any Mono-supported operating system, such as Windows, Linux or OS X. ASF is based on, and possible, thanks to [SteamKit2](https://github.com/SteamRE/SteamKit).
ASF doesn't require and doesn't interfere in any way with Steam client. In addition to that, it no longer requires exclusive access to given account, which means that you can use your main account in Steam client, and use ASF for farming the same account at the same time. If you decide to launch a game, ASF will get disconnected, and resume farming once you finish playing your game, being as transparent as possible.
@@ -19,25 +28,11 @@ ASF doesn't require and doesn't interfere in any way with Steam client. In addit
**Setting up:**
Each ASF bot is defined in it's own XML config file in `config` directory. ASF comes with included ```example.xml``` config file, on which you should base all of your bots. Simply copy ```example.xml``` to a new file, and edit properties inside. Don't forget to switch ```Enabled``` property to ```true``` once you're done, as this is the master switch which enables configured bot to launch. The most minimalistic setup to make ASF working is changing only ```Enabled```, ```SteamLogin``` and ```SteamPassword``` properties, everything else is more or less optional to enable additional features.
After you set up all your bots (their configs), you should launch ```ASF.exe```. If your accounts require additional steps to unlock, such as Steam guard code, you'll need to enter those too after ASF tries to launch given bot. If everything ended properly, you should notice in the console output, as well as on your Steam, that all of your bots automatically started cards farming.
ASF doesn't require and doesn't interfere in any way with Steam client, which means that you can be logged in to Steam client as your primary account, and launch ASF at the same time, for any number of accounts, including your main one (if needed).
Detailed setting up instructions are available on **[our wiki](https://github.com/JustArchi/ArchiSteamFarm/wiki/Setting-up)**.
**Current Commands:**
- `!2fa` Generates temporary 2FA token for current bot instance
- `!2fa <BOT>` Generates temporary 2FA token for given bot instance
- `!2faoff` Deactivates 2FA for current bot instance
- `!2faoff <BOT>` Deactivates 2FA for given bot instance
- `!exit` Stops whole ASF
- `!farm` Restarts cards farming module. ASF automatically executes that if any cd-key is successfully claimed
- `!redeem <KEY>` Redeems cd-key on current bot instance. You can also paste cd-key directly to the chat
- `!start <BOT>` Starts given bot instance
- `!status` Prints current status of ASF
- `!stop` Stops current bot instance
- `!stop <BOT>` Stops given bot instance
Detailed documentation of all available commands is available on **[our wiki](https://github.com/JustArchi/ArchiSteamFarm/wiki/Commands)**.
> 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.

View File

@@ -15,7 +15,7 @@ namespace SteamAuth
/// </summary>
public class AuthenticatorLinker
{
/// <summary>
/// Set to register a new phone number when linking. If a phone number is not set on the account, this must be set. If a phone number is set on the account, this must be null.
@@ -100,21 +100,23 @@ namespace SteamAuth
public FinalizeResult FinalizeAddAuthenticator(string smsCode)
{
bool smsCodeGood = false;
//The act of checking the SMS code is necessary for Steam to finalize adding the phone number to the account.
//Of course, we only want to check it if we're adding a phone number in the first place...
if (!String.IsNullOrEmpty(this.PhoneNumber) && !this._checkSMSCode(smsCode))
{
return FinalizeResult.BadSMSCode;
}
var postData = new NameValueCollection();
postData.Add("steamid", _session.SteamID.ToString());
postData.Add("access_token", _session.OAuthToken);
postData.Add("activation_code", smsCode);
postData.Add("authenticator_code", "");
int tries = 0;
while (tries <= 30)
{
postData.Set("authenticator_code", tries == 0 ? "" : LinkedAccount.GenerateSteamGuardCode());
postData.Add("authenticator_time", TimeAligner.GetSteamTime().ToString());
if(smsCodeGood)
postData.Set("activation_code", "");
postData.Set("authenticator_code", LinkedAccount.GenerateSteamGuardCode());
postData.Set("authenticator_time", TimeAligner.GetSteamTime().ToString());
string response = SteamWeb.MobileLoginRequest(APIEndpoints.STEAMAPI_BASE + "/ITwoFactorService/FinalizeAddAuthenticator/v0001", "POST", postData);
if (response == null) return FinalizeResult.GeneralFailure;
@@ -126,14 +128,14 @@ namespace SteamAuth
return FinalizeResult.GeneralFailure;
}
if(finalizeResponse.Response.Status == 89)
if (finalizeResponse.Response.Status == 89)
{
return FinalizeResult.BadSMSCode;
}
if(finalizeResponse.Response.Status == 88)
if (finalizeResponse.Response.Status == 88)
{
if(tries >= 30)
if (tries >= 30)
{
return FinalizeResult.UnableToGenerateCorrectCodes;
}
@@ -144,9 +146,8 @@ namespace SteamAuth
return FinalizeResult.GeneralFailure;
}
if (finalizeResponse.Response.WantMore)
if (finalizeResponse.Response.WantMore)
{
smsCodeGood = true;
tries++;
continue;
}
@@ -158,6 +159,20 @@ namespace SteamAuth
return FinalizeResult.GeneralFailure;
}
private bool _checkSMSCode(string smsCode)
{
var postData = new NameValueCollection();
postData.Add("op", "check_sms_code");
postData.Add("arg", smsCode);
postData.Add("sessionid", _session.SessionID);
string response = SteamWeb.Request(APIEndpoints.COMMUNITY_BASE + "/steamguard/phoneajax", "POST", postData, _cookies);
if (response == null) return false;
var addPhoneNumberResponse = JsonConvert.DeserializeObject<AddPhoneResponse>(response);
return addPhoneNumberResponse.Success;
}
private bool _addPhoneNumber()
{
var postData = new NameValueCollection();

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

21
SteamAuth/LICENSE Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015 Joshua Coffey
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -9,8 +9,9 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>SteamAuth</RootNamespace>
<AssemblyName>SteamAuth</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@@ -22,16 +23,17 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<DebugType>none</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<DefineConstants>
</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.8.0.1-beta3\lib\net45\Newtonsoft.Json.dll</HintPath>
<HintPath>..\packages\Newtonsoft.Json.8.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="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);
}
@@ -222,6 +222,16 @@ namespace SteamAuth
return ret.ToArray();
}
public long GetConfirmationTradeOfferID(Confirmation conf)
{
var confDetails = _getConfirmationDetails(conf);
if (confDetails == null || !confDetails.Success) return -1;
Regex tradeOfferIDRegex = new Regex("<div class=\"tradeoffer\" id=\"tradeofferid_(\\d+)\" >");
if(!tradeOfferIDRegex.IsMatch(confDetails.HTML)) return -1;
return long.Parse(tradeOfferIDRegex.Match(confDetails.HTML).Groups[1].Value);
}
public bool AcceptConfirmation(Confirmation conf)
{
return _sendConfirmationAjax(conf, "allow");
@@ -258,7 +268,7 @@ namespace SteamAuth
this.Session.SteamLoginSecure = tokenSecure;
return true;
}
catch (Exception e)
catch (Exception)
{
return false;
}
@@ -290,18 +300,36 @@ namespace SteamAuth
this.Session.SteamLoginSecure = tokenSecure;
return true;
}
catch (Exception e)
catch (Exception)
{
return false;
}
}
private ConfirmationDetailsResponse _getConfirmationDetails(Confirmation conf)
{
string url = APIEndpoints.COMMUNITY_BASE + "/mobileconf/details/" + conf.ID + "?";
string queryString = GenerateConfirmationQueryParams("details");
url += queryString;
CookieContainer cookies = new CookieContainer();
this.Session.AddCookies(cookies);
string referer = GenerateConfirmationURL();
string response = SteamWeb.Request(url, "GET", null, cookies, null);
if (String.IsNullOrEmpty(response)) return null;
var confResponse = JsonConvert.DeserializeObject<ConfirmationDetailsResponse>(response);
if (confResponse == null) return null;
return confResponse;
}
private bool _sendConfirmationAjax(Confirmation conf, string op)
{
string url = APIEndpoints.COMMUNITY_BASE + "/mobileconf/ajaxop";
string queryString = "?op=" + op + "&";
queryString += GenerateConfirmationQueryParams(op);
queryString += "&cid=" + conf.ConfirmationID + "&ck=" + conf.ConfirmationKey;
queryString += "&cid=" + conf.ID + "&ck=" + conf.Key;
url += queryString;
CookieContainer cookies = new CookieContainer();
@@ -325,7 +353,7 @@ namespace SteamAuth
public string GenerateConfirmationQueryParams(string tag)
{
if (String.IsNullOrEmpty(DeviceID))
DeviceID = AuthenticatorLinker.GenerateDeviceID();
throw new ArgumentException("Device ID is not present");
long time = TimeAligner.GetSteamTime();
return "p=" + this.DeviceID + "&a=" + this.Session.SteamID.ToString() + "&k=" + _generateConfirmationHashForTime(time, tag) + "&t=" + time + "&m=android&tag=" + tag;
@@ -373,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!
}
@@ -415,5 +443,14 @@ namespace SteamAuth
[JsonProperty("success")]
public bool Success { get; set; }
}
private class ConfirmationDetailsResponse
{
[JsonProperty("success")]
public bool Success { get; set; }
[JsonProperty("html")]
public string HTML { get; set; }
}
}
}

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.1-beta3" targetFramework="net452" />
<package id="Newtonsoft.Json" version="8.0.3" targetFramework="net451" />
</packages>

Binary file not shown.

Binary file not shown.

View File

@@ -76,38 +76,6 @@
<param name="readRootValueAsArray">if set to <c>true</c> the root object will be read as a JSON array.</param>
<param name="dateTimeKindHandling">The <see cref="T:System.DateTimeKind" /> used when reading <see cref="T:System.DateTime"/> values from BSON.</param>
</member>
<member name="M:Newtonsoft.Json.Bson.BsonReader.ReadAsBytes">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Byte"/>[].
</summary>
<returns>
A <see cref="T:System.Byte"/>[] or a null reference if the next JSON token is null. This method will return <c>null</c> at the end of an array.
</returns>
</member>
<member name="M:Newtonsoft.Json.Bson.BsonReader.ReadAsDecimal">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.Bson.BsonReader.ReadAsInt32">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.Bson.BsonReader.ReadAsString">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.String"/>.
</summary>
<returns>A <see cref="T:System.String"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.Bson.BsonReader.ReadAsDateTime">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.Bson.BsonReader.Read">
<summary>
Reads the next JSON token from the stream.
@@ -636,6 +604,12 @@
Initializes a new instance of the <see cref="T:Newtonsoft.Json.Converters.StringEnumConverter"/> class.
</summary>
</member>
<member name="M:Newtonsoft.Json.Converters.StringEnumConverter.#ctor(System.Boolean)">
<summary>
Initializes a new instance of the <see cref="T:Newtonsoft.Json.Converters.StringEnumConverter"/> class.
</summary>
<param name="camelCaseText"><c>true</c> if the written enum text will be camel case; otherwise, <c>false</c>.</param>
</member>
<member name="M:Newtonsoft.Json.Converters.StringEnumConverter.WriteJson(Newtonsoft.Json.JsonWriter,System.Object,Newtonsoft.Json.JsonSerializer)">
<summary>
Writes the JSON representation of the object.
@@ -926,24 +900,24 @@
Causes child objects to be indented according to the <see cref="P:Newtonsoft.Json.JsonTextWriter.Indentation"/> and <see cref="P:Newtonsoft.Json.JsonTextWriter.IndentChar"/> settings.
</summary>
</member>
<member name="T:Newtonsoft.Json.IJsonBufferPool`1">
<member name="T:Newtonsoft.Json.IArrayPool`1">
<summary>
Provides an interface for using pooled buffers.
Provides an interface for using pooled arrays.
</summary>
<typeparam name="T">The buffer type content.</typeparam>
<typeparam name="T">The array type content.</typeparam>
</member>
<member name="M:Newtonsoft.Json.IJsonBufferPool`1.RentBuffer(System.Int32)">
<member name="M:Newtonsoft.Json.IArrayPool`1.Rent(System.Int32)">
<summary>
Rent a buffer from the pool. This buffer must be returned when it is no longer needed.
Rent a array from the pool. This array must be returned when it is no longer needed.
</summary>
<param name="minSize">The minimum required size of the buffer. The returned buffer may be larger.</param>
<returns>The rented buffer from the pool.</returns>
<param name="minimumLength">The minimum required length of the array. The returned array may be longer.</param>
<returns>The rented array from the pool. This array must be returned when it is no longer needed.</returns>
</member>
<member name="M:Newtonsoft.Json.IJsonBufferPool`1.ReturnBuffer(`0[]@)">
<member name="M:Newtonsoft.Json.IArrayPool`1.Return(`0[])">
<summary>
Return a buffer to the pool.
Return an array to the pool.
</summary>
<param name="buffer">The buffer that is being returned.</param>
<param name="array">The array that is being returned.</param>
</member>
<member name="T:Newtonsoft.Json.JsonConstructorAttribute">
<summary>
@@ -1090,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.
@@ -1107,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"/>.
@@ -2177,38 +2172,6 @@
</summary>
<param name="token">The token to read from.</param>
</member>
<member name="M:Newtonsoft.Json.Linq.JTokenReader.ReadAsBytes">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Byte"/>[].
</summary>
<returns>
A <see cref="T:System.Byte"/>[] or a null reference if the next JSON token is null. This method will return <c>null</c> at the end of an array.
</returns>
</member>
<member name="M:Newtonsoft.Json.Linq.JTokenReader.ReadAsDecimal">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.Linq.JTokenReader.ReadAsInt32">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.Linq.JTokenReader.ReadAsString">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.String"/>.
</summary>
<returns>A <see cref="T:System.String"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.Linq.JTokenReader.ReadAsDateTime">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.Linq.JTokenReader.Read">
<summary>
Reads the next JSON token from the stream.
@@ -3575,9 +3538,9 @@
</member>
<member name="M:Newtonsoft.Json.Linq.JValue.CreateUndefined">
<summary>
Creates a <see cref="T:Newtonsoft.Json.Linq.JValue"/> null value.
Creates a <see cref="T:Newtonsoft.Json.Linq.JValue"/> undefined value.
</summary>
<returns>A <see cref="T:Newtonsoft.Json.Linq.JValue"/> null value.</returns>
<returns>A <see cref="T:Newtonsoft.Json.Linq.JValue"/> undefined value.</returns>
</member>
<member name="P:Newtonsoft.Json.Linq.JValue.Type">
<summary>
@@ -4489,9 +4452,9 @@
</member>
<member name="P:Newtonsoft.Json.Serialization.JsonProperty.Order">
<summary>
Gets or sets the order of serialization and deserialization of a member.
Gets or sets the order of serialization of a member.
</summary>
<value>The numeric order of serialization or deserialization.</value>
<value>The numeric order of serialization.</value>
</member>
<member name="P:Newtonsoft.Json.Serialization.JsonProperty.UnderlyingName">
<summary>
@@ -4806,6 +4769,11 @@
Gets or sets the extension data getter.
</summary>
</member>
<member name="P:Newtonsoft.Json.Serialization.JsonObjectContract.ExtensionDataValueType">
<summary>
Gets or sets the extension data value type.
</summary>
</member>
<member name="M:Newtonsoft.Json.Serialization.JsonObjectContract.#ctor(System.Type)">
<summary>
Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.JsonObjectContract"/> class.
@@ -6418,7 +6386,7 @@
</member>
<member name="P:Newtonsoft.Json.JsonSerializerSettings.Converters">
<summary>
Gets or sets a collection <see cref="T:Newtonsoft.Json.JsonConverter"/> that will be used during serialization.
Gets or sets a <see cref="T:Newtonsoft.Json.JsonConverter"/> collection that will be used during serialization.
</summary>
<value>The converters.</value>
</member>
@@ -6432,6 +6400,11 @@
<summary>
Gets or sets how type name writing and reading is handled by the serializer.
</summary>
<remarks>
<see cref="P:Newtonsoft.Json.JsonSerializerSettings.TypeNameHandling"/> should be used with caution when your application deserializes JSON from an external source.
Incoming types should be validated with a custom <see cref="T:System.Runtime.Serialization.SerializationBinder"/>
when deserializing with a value other than <c>TypeNameHandling.None</c>.
</remarks>
<value>The type name handling.</value>
</member>
<member name="P:Newtonsoft.Json.JsonSerializerSettings.MetadataPropertyHandling">
@@ -6655,6 +6628,18 @@
</summary>
<returns>A <see cref="T:System.Nullable`1"/>.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonValidatingReader.ReadAsDouble">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonValidatingReader.ReadAsBoolean">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonValidatingReader.ReadAsString">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.String"/>.
@@ -6688,7 +6673,7 @@
</member>
<member name="F:Newtonsoft.Json.MemberSerialization.OptIn">
<summary>
Only members must be marked with <see cref="T:Newtonsoft.Json.JsonPropertyAttribute"/> or <see cref="!:DataMemberAttribute"/> are serialized.
Only members marked with <see cref="T:Newtonsoft.Json.JsonPropertyAttribute"/> or <see cref="!:DataMemberAttribute"/> are serialized.
This member serialization mode can also be set by marking the class with <see cref="!:DataContractAttribute"/>.
</summary>
</member>
@@ -6730,7 +6715,7 @@
</summary>
<param name="reader">The <c>TextReader</c> containing the XML data to read.</param>
</member>
<member name="P:Newtonsoft.Json.JsonTextReader.BufferPool">
<member name="P:Newtonsoft.Json.JsonTextReader.ArrayPool">
<summary>
Gets or sets the reader's character buffer pool.
</summary>
@@ -6743,21 +6728,13 @@
true if the next token was read successfully; false if there are no more tokens to read.
</returns>
</member>
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsBytes">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Byte"/>[].
</summary>
<returns>
A <see cref="T:System.Byte"/>[] or a null reference if the next JSON token is null. This method will return <c>null</c> at the end of an array.
</returns>
</member>
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsDecimal">
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsInt32">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsInt32">
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsDateTime">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
@@ -6769,7 +6746,25 @@
</summary>
<returns>A <see cref="T:System.String"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsDateTime">
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsBytes">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Byte"/>[].
</summary>
<returns>A <see cref="T:System.Byte"/>[] or a null reference if the next JSON token is null. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsBoolean">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsDecimal">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsDouble">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
@@ -6864,9 +6859,9 @@
</member>
<member name="P:Newtonsoft.Json.JsonPropertyAttribute.Order">
<summary>
Gets or sets the order of serialization and deserialization of a member.
Gets or sets the order of serialization of a member.
</summary>
<value>The numeric order of serialization or deserialization.</value>
<value>The numeric order of serialization.</value>
</member>
<member name="P:Newtonsoft.Json.JsonPropertyAttribute.Required">
<summary>
@@ -6921,9 +6916,9 @@
Represents a writer that provides a fast, non-cached, forward-only way of generating JSON data.
</summary>
</member>
<member name="P:Newtonsoft.Json.JsonTextWriter.BufferPool">
<member name="P:Newtonsoft.Json.JsonTextWriter.ArrayPool">
<summary>
Gets or sets the writer's character buffer pool.
Gets or sets the writer's character array pool.
</summary>
</member>
<member name="P:Newtonsoft.Json.JsonTextWriter.Indentation">
@@ -7515,6 +7510,18 @@
</summary>
<returns>A <see cref="T:System.Byte"/>[] or a null reference if the next JSON token is null. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonReader.ReadAsDouble">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonReader.ReadAsBoolean">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonReader.ReadAsDecimal">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
@@ -8121,6 +8128,11 @@
<summary>
Gets or sets how type name writing and reading is handled by the serializer.
</summary>
<remarks>
<see cref="P:Newtonsoft.Json.JsonSerializer.TypeNameHandling"/> should be used with caution when your application deserializes JSON from an external source.
Incoming types should be validated with a custom <see cref="T:System.Runtime.Serialization.SerializationBinder"/>
when deserializing with a value other than <c>TypeNameHandling.None</c>.
</remarks>
</member>
<member name="P:Newtonsoft.Json.JsonSerializer.TypeNameAssemblyFormat">
<summary>
@@ -9036,6 +9048,11 @@
<summary>
Specifies type name handling options for the <see cref="T:Newtonsoft.Json.JsonSerializer"/>.
</summary>
<remarks>
<see cref="T:Newtonsoft.Json.TypeNameHandling"/> should be used with caution when your application deserializes JSON from an external source.
Incoming types should be validated with a custom <see cref="T:System.Runtime.Serialization.SerializationBinder"/>
when deserializing with a value other than <c>TypeNameHandling.None</c>.
</remarks>
</member>
<member name="F:Newtonsoft.Json.TypeNameHandling.None">
<summary>
@@ -9590,6 +9607,12 @@
</summary>
<param name="ws">The string of white space characters.</param>
</member>
<member name="M:Newtonsoft.Json.JsonWriter.Dispose(System.Boolean)">
<summary>
Releases unmanaged and - optionally - managed resources
</summary>
<param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
</member>
<member name="M:Newtonsoft.Json.JsonWriter.SetWriteState(Newtonsoft.Json.JsonToken,System.Object)">
<summary>
Sets the state of the JsonWriter,

Binary file not shown.

View File

@@ -59,46 +59,6 @@
<param name="readRootValueAsArray">if set to <c>true</c> the root object will be read as a JSON array.</param>
<param name="dateTimeKindHandling">The <see cref="T:System.DateTimeKind" /> used when reading <see cref="T:System.DateTime"/> values from BSON.</param>
</member>
<member name="M:Newtonsoft.Json.Bson.BsonReader.ReadAsBytes">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Byte"/>[].
</summary>
<returns>
A <see cref="T:System.Byte"/>[] or a null reference if the next JSON token is null. This method will return <c>null</c> at the end of an array.
</returns>
</member>
<member name="M:Newtonsoft.Json.Bson.BsonReader.ReadAsDecimal">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.Bson.BsonReader.ReadAsInt32">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.Bson.BsonReader.ReadAsString">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.String"/>.
</summary>
<returns>A <see cref="T:System.String"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.Bson.BsonReader.ReadAsDateTime">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.Bson.BsonReader.ReadAsDateTimeOffset">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>
A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.
</returns>
</member>
<member name="M:Newtonsoft.Json.Bson.BsonReader.Read">
<summary>
Reads the next JSON token from the stream.
@@ -667,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.
@@ -977,24 +943,24 @@
Causes child objects to be indented according to the <see cref="P:Newtonsoft.Json.JsonTextWriter.Indentation"/> and <see cref="P:Newtonsoft.Json.JsonTextWriter.IndentChar"/> settings.
</summary>
</member>
<member name="T:Newtonsoft.Json.IJsonBufferPool`1">
<member name="T:Newtonsoft.Json.IArrayPool`1">
<summary>
Provides an interface for using pooled buffers.
Provides an interface for using pooled arrays.
</summary>
<typeparam name="T">The buffer type content.</typeparam>
<typeparam name="T">The array type content.</typeparam>
</member>
<member name="M:Newtonsoft.Json.IJsonBufferPool`1.RentBuffer(System.Int32)">
<member name="M:Newtonsoft.Json.IArrayPool`1.Rent(System.Int32)">
<summary>
Rent a buffer from the pool. This buffer must be returned when it is no longer needed.
Rent a array from the pool. This array must be returned when it is no longer needed.
</summary>
<param name="minSize">The minimum required size of the buffer. The returned buffer may be larger.</param>
<returns>The rented buffer from the pool.</returns>
<param name="minimumLength">The minimum required length of the array. The returned array may be longer.</param>
<returns>The rented array from the pool. This array must be returned when it is no longer needed.</returns>
</member>
<member name="M:Newtonsoft.Json.IJsonBufferPool`1.ReturnBuffer(`0[]@)">
<member name="M:Newtonsoft.Json.IArrayPool`1.Return(`0[])">
<summary>
Return a buffer to the pool.
Return an array to the pool.
</summary>
<param name="buffer">The buffer that is being returned.</param>
<param name="array">The array that is being returned.</param>
</member>
<member name="T:Newtonsoft.Json.JsonConstructorAttribute">
<summary>
@@ -1230,6 +1196,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.
@@ -1247,6 +1219,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.
@@ -2239,44 +2226,6 @@
</summary>
<param name="token">The token to read from.</param>
</member>
<member name="M:Newtonsoft.Json.Linq.JTokenReader.ReadAsBytes">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Byte"/>[].
</summary>
<returns>
A <see cref="T:System.Byte"/>[] or a null reference if the next JSON token is null. This method will return <c>null</c> at the end of an array.
</returns>
</member>
<member name="M:Newtonsoft.Json.Linq.JTokenReader.ReadAsDecimal">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.Linq.JTokenReader.ReadAsInt32">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.Linq.JTokenReader.ReadAsString">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.String"/>.
</summary>
<returns>A <see cref="T:System.String"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.Linq.JTokenReader.ReadAsDateTime">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.Linq.JTokenReader.ReadAsDateTimeOffset">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.Linq.JTokenReader.Read">
<summary>
Reads the next JSON token from the stream.
@@ -3683,9 +3632,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>
@@ -4597,9 +4546,9 @@
</member>
<member name="P:Newtonsoft.Json.Serialization.JsonProperty.Order">
<summary>
Gets or sets the order of serialization and deserialization of a member.
Gets or sets the order of serialization of a member.
</summary>
<value>The numeric order of serialization or deserialization.</value>
<value>The numeric order of serialization.</value>
</member>
<member name="P:Newtonsoft.Json.Serialization.JsonProperty.UnderlyingName">
<summary>
@@ -4914,6 +4863,11 @@
Gets or sets the extension data getter.
</summary>
</member>
<member name="P:Newtonsoft.Json.Serialization.JsonObjectContract.ExtensionDataValueType">
<summary>
Gets or sets the extension data value type.
</summary>
</member>
<member name="M:Newtonsoft.Json.Serialization.JsonObjectContract.#ctor(System.Type)">
<summary>
Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.JsonObjectContract"/> class.
@@ -5468,7 +5422,7 @@
</member>
<member name="P:Newtonsoft.Json.JsonSerializerSettings.Converters">
<summary>
Gets or sets a collection <see cref="T:Newtonsoft.Json.JsonConverter"/> that will be used during serialization.
Gets or sets a <see cref="T:Newtonsoft.Json.JsonConverter"/> collection that will be used during serialization.
</summary>
<value>The converters.</value>
</member>
@@ -5482,6 +5436,11 @@
<summary>
Gets or sets how type name writing and reading is handled by the serializer.
</summary>
<remarks>
<see cref="P:Newtonsoft.Json.JsonSerializerSettings.TypeNameHandling"/> should be used with caution when your application deserializes JSON from an external source.
Incoming types should be validated with a custom <see cref="T:System.Runtime.Serialization.SerializationBinder"/>
when deserializing with a value other than <c>TypeNameHandling.None</c>.
</remarks>
<value>The type name handling.</value>
</member>
<member name="P:Newtonsoft.Json.JsonSerializerSettings.MetadataPropertyHandling">
@@ -5705,6 +5664,18 @@
</summary>
<returns>A <see cref="T:System.Nullable`1"/>.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonValidatingReader.ReadAsDouble">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonValidatingReader.ReadAsBoolean">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonValidatingReader.ReadAsString">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.String"/>.
@@ -5744,7 +5715,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>
@@ -5786,7 +5757,7 @@
</summary>
<param name="reader">The <c>TextReader</c> containing the XML data to read.</param>
</member>
<member name="P:Newtonsoft.Json.JsonTextReader.BufferPool">
<member name="P:Newtonsoft.Json.JsonTextReader.ArrayPool">
<summary>
Gets or sets the reader's character buffer pool.
</summary>
@@ -5799,21 +5770,13 @@
true if the next token was read successfully; false if there are no more tokens to read.
</returns>
</member>
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsBytes">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Byte"/>[].
</summary>
<returns>
A <see cref="T:System.Byte"/>[] or a null reference if the next JSON token is null. This method will return <c>null</c> at the end of an array.
</returns>
</member>
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsDecimal">
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsInt32">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsInt32">
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsDateTime">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
@@ -5825,7 +5788,13 @@
</summary>
<returns>A <see cref="T:System.String"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsDateTime">
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsBytes">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Byte"/>[].
</summary>
<returns>A <see cref="T:System.Byte"/>[] or a null reference if the next JSON token is null. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsBoolean">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
@@ -5835,7 +5804,19 @@
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.DateTimeOffset"/>. This method will return <c>null</c> at the end of an array.</returns>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsDecimal">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsDouble">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonTextReader.Close">
<summary>
@@ -5926,9 +5907,9 @@
</member>
<member name="P:Newtonsoft.Json.JsonPropertyAttribute.Order">
<summary>
Gets or sets the order of serialization and deserialization of a member.
Gets or sets the order of serialization of a member.
</summary>
<value>The numeric order of serialization or deserialization.</value>
<value>The numeric order of serialization.</value>
</member>
<member name="P:Newtonsoft.Json.JsonPropertyAttribute.Required">
<summary>
@@ -5983,9 +5964,9 @@
Represents a writer that provides a fast, non-cached, forward-only way of generating JSON data.
</summary>
</member>
<member name="P:Newtonsoft.Json.JsonTextWriter.BufferPool">
<member name="P:Newtonsoft.Json.JsonTextWriter.ArrayPool">
<summary>
Gets or sets the writer's character buffer pool.
Gets or sets the writer's character array pool.
</summary>
</member>
<member name="P:Newtonsoft.Json.JsonTextWriter.Indentation">
@@ -6583,6 +6564,18 @@
</summary>
<returns>A <see cref="T:System.Byte"/>[] or a null reference if the next JSON token is null. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonReader.ReadAsDouble">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonReader.ReadAsBoolean">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonReader.ReadAsDecimal">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
@@ -7262,6 +7255,11 @@
<summary>
Gets or sets how type name writing and reading is handled by the serializer.
</summary>
<remarks>
<see cref="P:Newtonsoft.Json.JsonSerializer.TypeNameHandling"/> should be used with caution when your application deserializes JSON from an external source.
Incoming types should be validated with a custom <see cref="T:System.Runtime.Serialization.SerializationBinder"/>
when deserializing with a value other than <c>TypeNameHandling.None</c>.
</remarks>
</member>
<member name="P:Newtonsoft.Json.JsonSerializer.TypeNameAssemblyFormat">
<summary>
@@ -8177,6 +8175,11 @@
<summary>
Specifies type name handling options for the <see cref="T:Newtonsoft.Json.JsonSerializer"/>.
</summary>
<remarks>
<see cref="T:Newtonsoft.Json.TypeNameHandling"/> should be used with caution when your application deserializes JSON from an external source.
Incoming types should be validated with a custom <see cref="T:System.Runtime.Serialization.SerializationBinder"/>
when deserializing with a value other than <c>TypeNameHandling.None</c>.
</remarks>
</member>
<member name="F:Newtonsoft.Json.TypeNameHandling.None">
<summary>
@@ -8743,6 +8746,12 @@
</summary>
<param name="ws">The string of white space characters.</param>
</member>
<member name="M:Newtonsoft.Json.JsonWriter.Dispose(System.Boolean)">
<summary>
Releases unmanaged and - optionally - managed resources
</summary>
<param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
</member>
<member name="M:Newtonsoft.Json.JsonWriter.SetWriteState(Newtonsoft.Json.JsonToken,System.Object)">
<summary>
Sets the state of the JsonWriter,

Binary file not shown.

Binary file not shown.

View File

@@ -76,46 +76,6 @@
<param name="readRootValueAsArray">if set to <c>true</c> the root object will be read as a JSON array.</param>
<param name="dateTimeKindHandling">The <see cref="T:System.DateTimeKind" /> used when reading <see cref="T:System.DateTime"/> values from BSON.</param>
</member>
<member name="M:Newtonsoft.Json.Bson.BsonReader.ReadAsBytes">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Byte"/>[].
</summary>
<returns>
A <see cref="T:System.Byte"/>[] or a null reference if the next JSON token is null. This method will return <c>null</c> at the end of an array.
</returns>
</member>
<member name="M:Newtonsoft.Json.Bson.BsonReader.ReadAsDecimal">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.Bson.BsonReader.ReadAsInt32">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.Bson.BsonReader.ReadAsString">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.String"/>.
</summary>
<returns>A <see cref="T:System.String"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.Bson.BsonReader.ReadAsDateTime">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.Bson.BsonReader.ReadAsDateTimeOffset">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>
A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.
</returns>
</member>
<member name="M:Newtonsoft.Json.Bson.BsonReader.Read">
<summary>
Reads the next JSON token from the stream.
@@ -650,6 +610,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.
@@ -857,24 +823,24 @@
Causes child objects to be indented according to the <see cref="P:Newtonsoft.Json.JsonTextWriter.Indentation"/> and <see cref="P:Newtonsoft.Json.JsonTextWriter.IndentChar"/> settings.
</summary>
</member>
<member name="T:Newtonsoft.Json.IJsonBufferPool`1">
<member name="T:Newtonsoft.Json.IArrayPool`1">
<summary>
Provides an interface for using pooled buffers.
Provides an interface for using pooled arrays.
</summary>
<typeparam name="T">The buffer type content.</typeparam>
<typeparam name="T">The array type content.</typeparam>
</member>
<member name="M:Newtonsoft.Json.IJsonBufferPool`1.RentBuffer(System.Int32)">
<member name="M:Newtonsoft.Json.IArrayPool`1.Rent(System.Int32)">
<summary>
Rent a buffer from the pool. This buffer must be returned when it is no longer needed.
Rent a array from the pool. This array must be returned when it is no longer needed.
</summary>
<param name="minSize">The minimum required size of the buffer. The returned buffer may be larger.</param>
<returns>The rented buffer from the pool.</returns>
<param name="minimumLength">The minimum required length of the array. The returned array may be longer.</param>
<returns>The rented array from the pool. This array must be returned when it is no longer needed.</returns>
</member>
<member name="M:Newtonsoft.Json.IJsonBufferPool`1.ReturnBuffer(`0[]@)">
<member name="M:Newtonsoft.Json.IArrayPool`1.Return(`0[])">
<summary>
Return a buffer to the pool.
Return an array to the pool.
</summary>
<param name="buffer">The buffer that is being returned.</param>
<param name="array">The array that is being returned.</param>
</member>
<member name="T:Newtonsoft.Json.IJsonLineInfo">
<summary>
@@ -1719,9 +1685,9 @@
</member>
<member name="P:Newtonsoft.Json.JsonPropertyAttribute.Order">
<summary>
Gets or sets the order of serialization and deserialization of a member.
Gets or sets the order of serialization of a member.
</summary>
<value>The numeric order of serialization or deserialization.</value>
<value>The numeric order of serialization.</value>
</member>
<member name="P:Newtonsoft.Json.JsonPropertyAttribute.Required">
<summary>
@@ -1956,6 +1922,18 @@
</summary>
<returns>A <see cref="T:System.Byte"/>[] or a null reference if the next JSON token is null. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonReader.ReadAsDouble">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonReader.ReadAsBoolean">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonReader.ReadAsDecimal">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
@@ -2123,6 +2101,11 @@
<summary>
Gets or sets how type name writing and reading is handled by the serializer.
</summary>
<remarks>
<see cref="P:Newtonsoft.Json.JsonSerializer.TypeNameHandling"/> should be used with caution when your application deserializes JSON from an external source.
Incoming types should be validated with a custom <see cref="T:System.Runtime.Serialization.SerializationBinder"/>
when deserializing with a value other than <c>TypeNameHandling.None</c>.
</remarks>
</member>
<member name="P:Newtonsoft.Json.JsonSerializer.TypeNameAssemblyFormat">
<summary>
@@ -2433,7 +2416,7 @@
</member>
<member name="P:Newtonsoft.Json.JsonSerializerSettings.Converters">
<summary>
Gets or sets a collection <see cref="T:Newtonsoft.Json.JsonConverter"/> that will be used during serialization.
Gets or sets a <see cref="T:Newtonsoft.Json.JsonConverter"/> collection that will be used during serialization.
</summary>
<value>The converters.</value>
</member>
@@ -2447,6 +2430,11 @@
<summary>
Gets or sets how type name writing and reading is handled by the serializer.
</summary>
<remarks>
<see cref="P:Newtonsoft.Json.JsonSerializerSettings.TypeNameHandling"/> should be used with caution when your application deserializes JSON from an external source.
Incoming types should be validated with a custom <see cref="T:System.Runtime.Serialization.SerializationBinder"/>
when deserializing with a value other than <c>TypeNameHandling.None</c>.
</remarks>
<value>The type name handling.</value>
</member>
<member name="P:Newtonsoft.Json.JsonSerializerSettings.MetadataPropertyHandling">
@@ -2592,7 +2580,7 @@
</summary>
<param name="reader">The <c>TextReader</c> containing the XML data to read.</param>
</member>
<member name="P:Newtonsoft.Json.JsonTextReader.BufferPool">
<member name="P:Newtonsoft.Json.JsonTextReader.ArrayPool">
<summary>
Gets or sets the reader's character buffer pool.
</summary>
@@ -2605,21 +2593,13 @@
true if the next token was read successfully; false if there are no more tokens to read.
</returns>
</member>
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsBytes">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Byte"/>[].
</summary>
<returns>
A <see cref="T:System.Byte"/>[] or a null reference if the next JSON token is null. This method will return <c>null</c> at the end of an array.
</returns>
</member>
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsDecimal">
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsInt32">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsInt32">
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsDateTime">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
@@ -2631,7 +2611,13 @@
</summary>
<returns>A <see cref="T:System.String"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsDateTime">
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsBytes">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Byte"/>[].
</summary>
<returns>A <see cref="T:System.Byte"/>[] or a null reference if the next JSON token is null. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsBoolean">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
@@ -2641,7 +2627,19 @@
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.DateTimeOffset"/>. This method will return <c>null</c> at the end of an array.</returns>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsDecimal">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsDouble">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonTextReader.Close">
<summary>
@@ -2677,9 +2675,9 @@
Represents a writer that provides a fast, non-cached, forward-only way of generating JSON data.
</summary>
</member>
<member name="P:Newtonsoft.Json.JsonTextWriter.BufferPool">
<member name="P:Newtonsoft.Json.JsonTextWriter.ArrayPool">
<summary>
Gets or sets the writer's character buffer pool.
Gets or sets the writer's character array pool.
</summary>
</member>
<member name="P:Newtonsoft.Json.JsonTextWriter.Indentation">
@@ -3119,6 +3117,18 @@
</summary>
<returns>A <see cref="T:System.Nullable`1"/>.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonValidatingReader.ReadAsDouble">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonValidatingReader.ReadAsBoolean">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonValidatingReader.ReadAsString">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.String"/>.
@@ -3590,6 +3600,12 @@
</summary>
<param name="ws">The string of white space characters.</param>
</member>
<member name="M:Newtonsoft.Json.JsonWriter.Dispose(System.Boolean)">
<summary>
Releases unmanaged and - optionally - managed resources
</summary>
<param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
</member>
<member name="M:Newtonsoft.Json.JsonWriter.SetWriteState(Newtonsoft.Json.JsonToken,System.Object)">
<summary>
Sets the state of the JsonWriter,
@@ -4578,6 +4594,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.JToken">
<summary>
Represents an abstract JSON token.
@@ -5504,44 +5526,6 @@
</summary>
<param name="token">The token to read from.</param>
</member>
<member name="M:Newtonsoft.Json.Linq.JTokenReader.ReadAsBytes">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Byte"/>[].
</summary>
<returns>
A <see cref="T:System.Byte"/>[] or a null reference if the next JSON token is null. This method will return <c>null</c> at the end of an array.
</returns>
</member>
<member name="M:Newtonsoft.Json.Linq.JTokenReader.ReadAsDecimal">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.Linq.JTokenReader.ReadAsInt32">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.Linq.JTokenReader.ReadAsString">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.String"/>.
</summary>
<returns>A <see cref="T:System.String"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.Linq.JTokenReader.ReadAsDateTime">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.Linq.JTokenReader.ReadAsDateTimeOffset">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.Linq.JTokenReader.Read">
<summary>
Reads the next JSON token from the stream.
@@ -5989,9 +5973,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>
@@ -6114,6 +6098,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"/>.
@@ -6127,7 +6126,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>
@@ -7619,6 +7618,11 @@
Gets or sets the extension data getter.
</summary>
</member>
<member name="P:Newtonsoft.Json.Serialization.JsonObjectContract.ExtensionDataValueType">
<summary>
Gets or sets the extension data value type.
</summary>
</member>
<member name="M:Newtonsoft.Json.Serialization.JsonObjectContract.#ctor(System.Type)">
<summary>
Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.JsonObjectContract"/> class.
@@ -7655,9 +7659,9 @@
</member>
<member name="P:Newtonsoft.Json.Serialization.JsonProperty.Order">
<summary>
Gets or sets the order of serialization and deserialization of a member.
Gets or sets the order of serialization of a member.
</summary>
<value>The numeric order of serialization or deserialization.</value>
<value>The numeric order of serialization.</value>
</member>
<member name="P:Newtonsoft.Json.Serialization.JsonProperty.UnderlyingName">
<summary>
@@ -8052,6 +8056,11 @@
<summary>
Specifies type name handling options for the <see cref="T:Newtonsoft.Json.JsonSerializer"/>.
</summary>
<remarks>
<see cref="T:Newtonsoft.Json.TypeNameHandling"/> should be used with caution when your application deserializes JSON from an external source.
Incoming types should be validated with a custom <see cref="T:System.Runtime.Serialization.SerializationBinder"/>
when deserializing with a value other than <c>TypeNameHandling.None</c>.
</remarks>
</member>
<member name="F:Newtonsoft.Json.TypeNameHandling.None">
<summary>

View File

@@ -76,46 +76,6 @@
<param name="readRootValueAsArray">if set to <c>true</c> the root object will be read as a JSON array.</param>
<param name="dateTimeKindHandling">The <see cref="T:System.DateTimeKind" /> used when reading <see cref="T:System.DateTime"/> values from BSON.</param>
</member>
<member name="M:Newtonsoft.Json.Bson.BsonReader.ReadAsBytes">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Byte"/>[].
</summary>
<returns>
A <see cref="T:System.Byte"/>[] or a null reference if the next JSON token is null. This method will return <c>null</c> at the end of an array.
</returns>
</member>
<member name="M:Newtonsoft.Json.Bson.BsonReader.ReadAsDecimal">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.Bson.BsonReader.ReadAsInt32">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.Bson.BsonReader.ReadAsString">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.String"/>.
</summary>
<returns>A <see cref="T:System.String"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.Bson.BsonReader.ReadAsDateTime">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.Bson.BsonReader.ReadAsDateTimeOffset">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>
A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.
</returns>
</member>
<member name="M:Newtonsoft.Json.Bson.BsonReader.Read">
<summary>
Reads the next JSON token from the stream.
@@ -690,6 +650,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.
@@ -956,24 +922,24 @@
Causes child objects to be indented according to the <see cref="P:Newtonsoft.Json.JsonTextWriter.Indentation"/> and <see cref="P:Newtonsoft.Json.JsonTextWriter.IndentChar"/> settings.
</summary>
</member>
<member name="T:Newtonsoft.Json.IJsonBufferPool`1">
<member name="T:Newtonsoft.Json.IArrayPool`1">
<summary>
Provides an interface for using pooled buffers.
Provides an interface for using pooled arrays.
</summary>
<typeparam name="T">The buffer type content.</typeparam>
<typeparam name="T">The array type content.</typeparam>
</member>
<member name="M:Newtonsoft.Json.IJsonBufferPool`1.RentBuffer(System.Int32)">
<member name="M:Newtonsoft.Json.IArrayPool`1.Rent(System.Int32)">
<summary>
Rent a buffer from the pool. This buffer must be returned when it is no longer needed.
Rent a array from the pool. This array must be returned when it is no longer needed.
</summary>
<param name="minSize">The minimum required size of the buffer. The returned buffer may be larger.</param>
<returns>The rented buffer from the pool.</returns>
<param name="minimumLength">The minimum required length of the array. The returned array may be longer.</param>
<returns>The rented array from the pool. This array must be returned when it is no longer needed.</returns>
</member>
<member name="M:Newtonsoft.Json.IJsonBufferPool`1.ReturnBuffer(`0[]@)">
<member name="M:Newtonsoft.Json.IArrayPool`1.Return(`0[])">
<summary>
Return a buffer to the pool.
Return an array to the pool.
</summary>
<param name="buffer">The buffer that is being returned.</param>
<param name="array">The array that is being returned.</param>
</member>
<member name="T:Newtonsoft.Json.IJsonLineInfo">
<summary>
@@ -1969,9 +1935,9 @@
</member>
<member name="P:Newtonsoft.Json.JsonPropertyAttribute.Order">
<summary>
Gets or sets the order of serialization and deserialization of a member.
Gets or sets the order of serialization of a member.
</summary>
<value>The numeric order of serialization or deserialization.</value>
<value>The numeric order of serialization.</value>
</member>
<member name="P:Newtonsoft.Json.JsonPropertyAttribute.Required">
<summary>
@@ -2206,6 +2172,18 @@
</summary>
<returns>A <see cref="T:System.Byte"/>[] or a null reference if the next JSON token is null. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonReader.ReadAsDouble">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonReader.ReadAsBoolean">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonReader.ReadAsDecimal">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
@@ -2373,6 +2351,11 @@
<summary>
Gets or sets how type name writing and reading is handled by the serializer.
</summary>
<remarks>
<see cref="P:Newtonsoft.Json.JsonSerializer.TypeNameHandling"/> should be used with caution when your application deserializes JSON from an external source.
Incoming types should be validated with a custom <see cref="T:System.Runtime.Serialization.SerializationBinder"/>
when deserializing with a value other than <c>TypeNameHandling.None</c>.
</remarks>
</member>
<member name="P:Newtonsoft.Json.JsonSerializer.TypeNameAssemblyFormat">
<summary>
@@ -2683,7 +2666,7 @@
</member>
<member name="P:Newtonsoft.Json.JsonSerializerSettings.Converters">
<summary>
Gets or sets a collection <see cref="T:Newtonsoft.Json.JsonConverter"/> that will be used during serialization.
Gets or sets a <see cref="T:Newtonsoft.Json.JsonConverter"/> collection that will be used during serialization.
</summary>
<value>The converters.</value>
</member>
@@ -2697,6 +2680,11 @@
<summary>
Gets or sets how type name writing and reading is handled by the serializer.
</summary>
<remarks>
<see cref="P:Newtonsoft.Json.JsonSerializerSettings.TypeNameHandling"/> should be used with caution when your application deserializes JSON from an external source.
Incoming types should be validated with a custom <see cref="T:System.Runtime.Serialization.SerializationBinder"/>
when deserializing with a value other than <c>TypeNameHandling.None</c>.
</remarks>
<value>The type name handling.</value>
</member>
<member name="P:Newtonsoft.Json.JsonSerializerSettings.MetadataPropertyHandling">
@@ -2842,7 +2830,7 @@
</summary>
<param name="reader">The <c>TextReader</c> containing the XML data to read.</param>
</member>
<member name="P:Newtonsoft.Json.JsonTextReader.BufferPool">
<member name="P:Newtonsoft.Json.JsonTextReader.ArrayPool">
<summary>
Gets or sets the reader's character buffer pool.
</summary>
@@ -2855,21 +2843,13 @@
true if the next token was read successfully; false if there are no more tokens to read.
</returns>
</member>
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsBytes">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Byte"/>[].
</summary>
<returns>
A <see cref="T:System.Byte"/>[] or a null reference if the next JSON token is null. This method will return <c>null</c> at the end of an array.
</returns>
</member>
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsDecimal">
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsInt32">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsInt32">
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsDateTime">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
@@ -2881,7 +2861,13 @@
</summary>
<returns>A <see cref="T:System.String"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsDateTime">
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsBytes">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Byte"/>[].
</summary>
<returns>A <see cref="T:System.Byte"/>[] or a null reference if the next JSON token is null. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsBoolean">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
@@ -2891,7 +2877,19 @@
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.DateTimeOffset"/>. This method will return <c>null</c> at the end of an array.</returns>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsDecimal">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonTextReader.ReadAsDouble">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonTextReader.Close">
<summary>
@@ -2927,9 +2925,9 @@
Represents a writer that provides a fast, non-cached, forward-only way of generating JSON data.
</summary>
</member>
<member name="P:Newtonsoft.Json.JsonTextWriter.BufferPool">
<member name="P:Newtonsoft.Json.JsonTextWriter.ArrayPool">
<summary>
Gets or sets the writer's character buffer pool.
Gets or sets the writer's character array pool.
</summary>
</member>
<member name="P:Newtonsoft.Json.JsonTextWriter.Indentation">
@@ -3369,6 +3367,18 @@
</summary>
<returns>A <see cref="T:System.Nullable`1"/>.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonValidatingReader.ReadAsDouble">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonValidatingReader.ReadAsBoolean">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>.</returns>
</member>
<member name="M:Newtonsoft.Json.JsonValidatingReader.ReadAsString">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.String"/>.
@@ -3840,6 +3850,12 @@
</summary>
<param name="ws">The string of white space characters.</param>
</member>
<member name="M:Newtonsoft.Json.JsonWriter.Dispose(System.Boolean)">
<summary>
Releases unmanaged and - optionally - managed resources
</summary>
<param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
</member>
<member name="M:Newtonsoft.Json.JsonWriter.SetWriteState(Newtonsoft.Json.JsonToken,System.Object)">
<summary>
Sets the state of the JsonWriter,
@@ -4848,6 +4864,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.JToken">
<summary>
Represents an abstract JSON token.
@@ -5792,44 +5814,6 @@
</summary>
<param name="token">The token to read from.</param>
</member>
<member name="M:Newtonsoft.Json.Linq.JTokenReader.ReadAsBytes">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Byte"/>[].
</summary>
<returns>
A <see cref="T:System.Byte"/>[] or a null reference if the next JSON token is null. This method will return <c>null</c> at the end of an array.
</returns>
</member>
<member name="M:Newtonsoft.Json.Linq.JTokenReader.ReadAsDecimal">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.Linq.JTokenReader.ReadAsInt32">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.Linq.JTokenReader.ReadAsString">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.String"/>.
</summary>
<returns>A <see cref="T:System.String"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.Linq.JTokenReader.ReadAsDateTime">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.Linq.JTokenReader.ReadAsDateTimeOffset">
<summary>
Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
</summary>
<returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
</member>
<member name="M:Newtonsoft.Json.Linq.JTokenReader.Read">
<summary>
Reads the next JSON token from the stream.
@@ -6277,9 +6261,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>
@@ -6411,6 +6395,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"/>.
@@ -6424,7 +6423,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>
@@ -7940,6 +7939,11 @@
Gets or sets the extension data getter.
</summary>
</member>
<member name="P:Newtonsoft.Json.Serialization.JsonObjectContract.ExtensionDataValueType">
<summary>
Gets or sets the extension data value type.
</summary>
</member>
<member name="M:Newtonsoft.Json.Serialization.JsonObjectContract.#ctor(System.Type)">
<summary>
Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.JsonObjectContract"/> class.
@@ -7976,9 +7980,9 @@
</member>
<member name="P:Newtonsoft.Json.Serialization.JsonProperty.Order">
<summary>
Gets or sets the order of serialization and deserialization of a member.
Gets or sets the order of serialization of a member.
</summary>
<value>The numeric order of serialization or deserialization.</value>
<value>The numeric order of serialization.</value>
</member>
<member name="P:Newtonsoft.Json.Serialization.JsonProperty.UnderlyingName">
<summary>
@@ -8373,6 +8377,11 @@
<summary>
Specifies type name handling options for the <see cref="T:Newtonsoft.Json.JsonSerializer"/>.
</summary>
<remarks>
<see cref="T:Newtonsoft.Json.TypeNameHandling"/> should be used with caution when your application deserializes JSON from an external source.
Incoming types should be validated with a custom <see cref="T:System.Runtime.Serialization.SerializationBinder"/>
when deserializing with a value other than <c>TypeNameHandling.None</c>.
</remarks>
</member>
<member name="F:Newtonsoft.Json.TypeNameHandling.None">
<summary>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
tools/ILRepack/ILRepack.exe Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
tools/NetHook2.dll Normal file

Binary file not shown.

BIN
tools/NetHookAnalyzer2.exe Normal file

Binary file not shown.

1
tools/hook.bat Normal file
View File

@@ -0,0 +1 @@
rundll32 NetHook2.dll,Inject

1
tools/unhook.bat Normal file
View File

@@ -0,0 +1 @@
rundll32 NetHook2.dll,Eject