├── .gitattributes
├── .gitignore
├── .nuget
├── Microsoft.Build.dll
├── NuGet.Config
├── NuGet.targets
└── packages.config
├── Bin
├── Debug
│ └── settings-template.json
└── Release
│ └── settings-template.json
├── CONTRIBUTING.md
├── Docs
├── Json
│ └── 753_6.txt
└── inventories.htm
├── LICENSE
├── README.md
├── SteamBot.sln
├── SteamBot
├── 3rdparty
│ └── Options.cs
├── AdminUserHandler.cs
├── AssemblyInfo.cs
├── Bot.cs
├── BotInfo.cs
├── BotManager.cs
├── BotManagerInterpreter.cs
├── Configuration.cs
├── ExampleBot.csproj
├── ExampleBot.csproj.DotSettings
├── Log.cs
├── Notifications.cs
├── Program.cs
├── SimpleUserHandler.cs
├── SteamGroups
│ ├── CMsgGroupInviteAction.cs
│ └── CMsgInviteUserToGroup.cs
├── SteamGuardRequiredEventArgs.cs
├── SteamTradeDemoHandler.cs
├── TF2GC
│ ├── Crafting.cs
│ ├── Items.cs
│ ├── MsgCraft.cs
│ └── MsgDelete.cs
├── TradeOfferUserHandler.cs
├── UserHandler.cs
├── app.config
└── packages.config
├── SteamBotUnitTest.sln
├── SteamBotUnitTest
├── Properties
│ └── AssemblyInfo.cs
├── SteamTrade
│ └── Tf2ValueTests.cs
├── SteamTradeUnitTest.csproj
└── packages.config
├── SteamTrade
├── AssemblyInfo.cs
├── Exceptions
│ ├── InventoryFetchException.cs
│ └── TradeException.cs
├── ForeignInventory.cs
├── GenericInventory.cs
├── Inventory.cs
├── Schema.cs
├── SteamTrade.csproj
├── SteamWeb.cs
├── Tf2Value.cs
├── Trade.cs
├── TradeManager.cs
├── TradeOffer
│ ├── OfferSession.cs
│ ├── TradeOffer.cs
│ ├── TradeOfferManager.cs
│ └── TradeOfferWebAPI.cs
├── TradeWebAPI
│ ├── TradeSession.cs
│ └── TradeStatus.cs
├── app.config
└── packages.config
└── triggerappveyor.txt
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 | *.sln merge=union
7 | *.csproj merge=union
8 | *.vbproj merge=union
9 | *.fsproj merge=union
10 | *.dbproj merge=union
11 |
12 | # Standard to msysgit
13 | *.doc diff=astextplain
14 | *.DOC diff=astextplain
15 | *.docx diff=astextplain
16 | *.DOCX diff=astextplain
17 | *.dot diff=astextplain
18 | *.DOT diff=astextplain
19 | *.pdf diff=astextplain
20 | *.PDF diff=astextplain
21 | *.rtf diff=astextplain
22 | *.RTF diff=astextplain
23 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | [Oo]bj
2 | [Rr]elease
3 | [Dd]ebug
4 | packages
5 | *.exe
6 | *.dll
7 | *.user
8 | *.suo
9 | *.[Cc]ache
10 | *.bak
11 | *.ncb
12 | *.log
13 | *.DS_Store
14 | [Tt]humbs.db
15 | _ReSharper.*
16 | *.resharper
17 | Ankh.NoLoad
18 |
19 | #OS junk files
20 | [Tt]humbs.db
21 | *.DS_Store
22 |
23 | *.userprefs
24 | *.pidb
25 |
26 | # NuGet Packages Directory
27 | packages
28 | *.nupkg
29 |
30 | # Allow NuGet specific exes and dlls
31 | !.nuget/Microsoft.Build.dll
32 |
--------------------------------------------------------------------------------
/.nuget/Microsoft.Build.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jessecar96/SteamBot/e8e9e5fcd64ae35b201e2597068849c10a667b60/.nuget/Microsoft.Build.dll
--------------------------------------------------------------------------------
/.nuget/NuGet.Config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.nuget/NuGet.targets:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $(MSBuildProjectDirectory)\..\
5 |
6 |
7 | false
8 |
9 |
10 | false
11 |
12 |
13 | true
14 |
15 |
16 | true
17 |
18 |
19 |
20 |
21 |
22 |
26 |
27 |
28 |
29 |
30 | $([System.IO.Path]::Combine($(SolutionDir), ".nuget"))
31 | $([System.IO.Path]::Combine($(ProjectDir), "packages.config"))
32 |
33 |
34 |
35 |
36 | $(SolutionDir).nuget
37 | packages.config
38 |
39 |
40 |
41 |
42 | $(NuGetToolsPath)\NuGet.exe
43 | @(PackageSource)
44 |
45 | "$(NuGetExePath)"
46 | mono --runtime=v4.0.30319 $(NuGetExePath)
47 |
48 | $(TargetDir.Trim('\\'))
49 |
50 | -RequireConsent
51 | -NonInteractive
52 |
53 |
54 | $(SolutionDir)
55 | $(SolutionDir)
56 | $(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(RequireConsentSwitch) -solutionDir "$(SolutionDirParsed)"
57 | $(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(RequireConsentSwitch) -solutionDir "$(SolutionDirParsed)"
58 |
59 | $(NuGetCommand) pack "$(ProjectPath)" -Properties Configuration=$(Configuration) $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols
60 |
61 |
62 |
63 | RestorePackages;
64 | $(BuildDependsOn);
65 |
66 |
67 |
68 |
69 | $(BuildDependsOn);
70 | BuildPackage;
71 |
72 |
73 |
74 |
75 |
76 |
77 |
82 |
83 |
84 |
85 |
86 |
87 |
89 |
90 |
91 |
92 |
93 |
95 |
96 |
99 |
100 |
101 |
102 |
104 |
105 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
137 |
138 |
139 |
140 |
141 |
--------------------------------------------------------------------------------
/.nuget/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/Bin/Debug/settings-template.json:
--------------------------------------------------------------------------------
1 | {
2 | "Admins":["234567"],
3 | "ApiKey":"Steam API Key goes here!",
4 | "mainLog": "syslog.log",
5 | "UseSeparateProcesses": "false",
6 | "AutoStartAllBots": "true",
7 | "Bots": [
8 | {
9 | "Username":"BOT USERNAME",
10 | "Password":"BOT PASSWORD",
11 | "ApiKey":"Bot specific apiKey",
12 | "DisplayName":"TestBot",
13 | "ChatResponse":"Hi there bro",
14 | "logFile": "TestBot.log",
15 | "BotControlClass": "SteamBot.SimpleUserHandler",
16 | "MaximumTradeTime":180,
17 | "MaximumActionGap":30,
18 | "DisplayNamePrefix":"[AutomatedBot] ",
19 | "TradePollingInterval":800,
20 | "TradeOfferPollingIntervalSecs":30,
21 | "ConsoleLogLevel":"Success",
22 | "FileLogLevel":"Info",
23 | "AutoStart": "true",
24 | "SchemaLang":"en"
25 | }
26 | ]
27 | }
28 |
--------------------------------------------------------------------------------
/Bin/Release/settings-template.json:
--------------------------------------------------------------------------------
1 | {
2 | "Admins":["234567"],
3 | "ApiKey":"Steam API Key goes here!",
4 | "mainLog": "syslog.log",
5 | "UseSeparateProcesses": "false",
6 | "AutoStartAllBots": "true",
7 | "Bots": [
8 | {
9 | "Username":"BOT USERNAME",
10 | "Password":"BOT PASSWORD",
11 | "ApiKey":"Bot specific apiKey",
12 | "DisplayName":"TestBot",
13 | "ChatResponse":"Hi there bro",
14 | "logFile": "TestBot.log",
15 | "BotControlClass": "SteamBot.SimpleUserHandler",
16 | "MaximumTradeTime":180,
17 | "MaximumActionGap":30,
18 | "DisplayNamePrefix":"[AutomatedBot] ",
19 | "TradePollingInterval":800,
20 | "TradeOfferPollingIntervalSecs":30,
21 | "ConsoleLogLevel":"Success",
22 | "FileLogLevel":"Info",
23 | "AutoStart": "true",
24 | "SchemaLang":"en"
25 | }
26 | ]
27 | }
28 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to SteamBot #
2 | When you're contributing to SteamBot, there are a few rules you should follow. First and foremost, SteamBot should be able to compile and run on Linux. SteamBot development works in both Visual Studio and MonoDevelop, but _please_ keep your temporary files (such as `.pidb`, `.*~`, or even `.tmp`) out of the project.
3 |
4 | ## How To Contribute ##
5 | 1. Fork The Repository ([Jessecar96/SteamBot](https://github.com/Jessecar96/SteamBot))
6 | 2. Branch It
7 | - this is because when you do the pull request for it, it includes commits you make after you make the pull request and before the pull request is accepted
8 | 3. Make Your Changes
9 | 4. Commit Your Changes
10 | 5. Do 3 and 4 as Needed
11 | 6. Push Your Changes Back to GitHub
12 | 7. Start a Pull Request on the Repository
13 | - make sure you explain what the pull request does
14 |
15 | ## Indentation ##
16 | With SteamBot, you should use four (4) spaces as an indent; tabs should not be used as indentation ever. This comes from
17 | Microsoft's [C# Coding Conventions](http://msdn.microsoft.com/en-us/library/vstudio/ff926074.aspx) (thank you, Philipp). It gets annoying when you have both in there, and it clogs up commit logs trying to fix it.
18 |
19 | ## Brackets ##
20 | Brackets should be on the next line of a function definition or an if directive. Brackets should always be on their own line.
21 |
22 | ## Issues ##
23 | Make sure you
24 | - Describe the problem
25 | - Show how to reproduce it, if applicable
26 | - Explain what you think is causing it, if applicable
27 | - Give a plausible solution
28 |
29 | ## Commits ##
30 | Commits should be in the present tense, and with Title Capitalization. If needed, a body should be on the next line in normal capitalization.
31 |
32 | ## C# 6 ##
33 | It would be better to stick to C# 5 features to be compatible with #1002.
34 |
--------------------------------------------------------------------------------
/Docs/Json/753_6.txt:
--------------------------------------------------------------------------------
1 | {
2 | "success":true,
3 | "rgInventory":
4 | {
5 | "70968777":
6 | {
7 | "id":"70968777",
8 | "classid":"172884566",
9 | "instanceid":"0",
10 | "amount":"1",
11 | "pos":1
12 | }
13 | },
14 |
15 | "rgCurrency":[],
16 |
17 | "rgDescriptions":
18 | {
19 | "172884566_0":
20 | {
21 | "appid":"753",
22 | "classid":"172884566",
23 | "instanceid":"0",
24 | "icon_url":"LM8bG-H8rsNnAQorXQCek5SD0aqljKCDs8_ZMbnm1jTlxCzguODo0cfYFtV7YNAZmZbIufDO4pu4wsQpuOXXM-jAP6Cm6PvTxoVa02Iqzh7Tw5Xqr4y427WZ1WPxrZMwvZ0_t6vkr9KRlEHfdy2XWMrFlL_7muvX4p-HaafwjDv0zg==","icon_url_large":"LM8bG-H8rsNnAQorXQCek5SD0aqljKCDs8_ZMbnm1jTlxCzguODo0cfYFtV7YNAZmZbIufDO4pu4wsQpuOXXM-jAP6Cm6PvTxoVa02Iqzh7Tw5Xqr4y427WZ1WPxrZMwvZ0_t6vkr9KRlEHfdy2XWMrFlL_7muvX4p-HaafwjDv0zg==",
25 | "icon_drag_url":"",
26 | "name":"GM_Construct",
27 | "market_hash_name":"4000-GM_Construct",
28 | "market_name":"GM_Construct",
29 | "name_color":"",
30 | "background_color":"",
31 | "type":"Garry's Mod Profile Background",
32 | "tradable":1,
33 | "marketable":1,
34 | "market_fee_app":"4000",
35 |
36 | "descriptions":[{"value":"The map that spawned a million melons"}],
37 |
38 | "tags":[{"internal_name":"app_4000","name":"Garry's Mod","category":"Game","category_name":"Game"},
39 | {"internal_name":"droprate_0","name":"Common","category":"droprate","category_name":"Rarity"},
40 | {"internal_name":"item_class_3","name":"Profile Background","category":"item_class","category_name":"Item Type"}]
41 | }
42 | }
43 | }
--------------------------------------------------------------------------------
/Docs/inventories.htm:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Non TF2 Inventories
6 |
16 |
17 |
18 | Version 1.1 (2013/08/18)
19 | Known Inventories
20 |
21 | Base URL: http://steamcommunity.com/id/userName/inventory/json/AppId/ContextId
22 | Game Appids List: http://api.steampowered.com/ISteamApps/GetAppList/v2
23 | NOTE: Inventory Context Id (type) changes according to the AppId item schema, so it's up to you find it out wich ones work :/
24 |
25 |
26 |
38 |
39 |
40 | Steam Inventory (AppId 753)
41 | Context Id | Description | Notes |
42 |
43 |
44 | 1 |
45 | Gifts (Games) |
46 | WARNING: Automatization (buy & sell) may be against Steam TOS |
47 |
48 |
49 |
50 | 3 |
51 | Coupons |
52 | |
53 |
54 |
55 |
56 | 6 |
57 | Steam Trading Cards |
58 | Game Cards, Profile Backgrounds & Emoticons |
59 |
60 |
61 |
62 |
63 | Counter-Strike: Global Offensive (AppId 730)
64 | Context Id | Description | Notes |
65 |
66 | 2 |
67 | Inventory |
68 | Weapons |
69 |
70 |
71 |
72 |
73 | Dota 2 (AppId 570)
74 | Context Id | Description | Notes |
75 |
76 | 2 |
77 | Main Inventory |
78 | Not sure |
79 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) SteamBot Contributors
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of
4 | this software and associated documentation files (the "Software"), to deal in
5 | the Software without restriction, including without limitation the rights to
6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7 | of the Software, and to permit persons to whom the Software is furnished to do
8 | so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://ci.appveyor.com/project/Jessecar96/steambot/branch/master)
2 |
3 | **SteamBot** is a bot written in C# for the purpose of interacting with Steam Chat and Steam Trade. As of right now, about 8 contributors have all added to the bot. The bot is publicly available under the MIT License. Check out [LICENSE] for more details.
4 |
5 | There are several things you must do in order to get SteamBot working:
6 |
7 | 1. Download the source.
8 | 2. Compile the source code.
9 | 3. Configure the bot (username, password, etc.).
10 | 4. *Optionally*, customize the bot by changing the source code.
11 |
12 | ## Getting the Source
13 |
14 | Retrieving the source code should be done by following the [installation guide] on the wiki. The install guide covers the instructions needed to obtain the source code as well as the instructions for compiling the code.
15 |
16 | ## Configuring the Bot
17 |
18 | See the [configuration guide] on the wiki. This guide covers configuring a basic bot as well as creating a custom user handler.
19 |
20 | ## Bot Administration
21 |
22 | While running the bots you may find it necessary to do some basic operations like shutting down and restarting a bot. The console will take some commands to allow you to do some this. See the [usage guide] for more information.
23 |
24 | ## More help?
25 | If it's a bug, open an Issue; if you have a fix, read [CONTRIBUTING.md] and open a Pull Request. If it is a question about how to use SteamBot with your own bots, visit our subreddit at [/r/SteamBot](http://www.reddit.com/r/SteamBot). Please use the issue tracker only for bugs reports and pull requests. The subreddit should be used for all other discussions.
26 |
27 |
28 | A list of contributors (add yourself if you want to):
29 |
30 | - [Jessecar96](http://steamcommunity.com/id/jessecar) (project lead)
31 | - [geel9](http://steamcommunity.com/id/geel9)
32 | - [cwhelchel](http://steamcommunity.com/id/cmw69krinkle)
33 | - [Lagg](http://lagg.me)
34 | - [BlueRaja](http://steamcommunity.com/id/BlueRaja/)
35 |
36 | ## Wanna Contribute?
37 | Please read [CONTRIBUTING.md].
38 |
39 |
40 | [installation guide]: https://github.com/Jessecar96/SteamBot/wiki/Installation-Guide
41 | [CONTRIBUTING.md]: https://github.com/Jessecar96/SteamBot/blob/master/CONTRIBUTING.md
42 | [LICENSE]: https://github.com/Jessecar96/SteamBot/blob/master/LICENSE
43 | [configuration guide]: https://github.com/Jessecar96/SteamBot/wiki/Configuration-Guide
44 | [usage guide]: https://github.com/Jessecar96/SteamBot/wiki/Usage-Guide
45 |
--------------------------------------------------------------------------------
/SteamBot.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Express 2013 for Windows Desktop
4 | VisualStudioVersion = 12.0.30501.0
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExampleBot", "SteamBot\ExampleBot.csproj", "{E81DED36-EDF5-41A5-8666-A3A0C581762F}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SteamTrade", "SteamTrade\SteamTrade.csproj", "{6CEC0333-81EB-40EE-85D1-941363626FC7}"
9 | EndProject
10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{C8129CC9-FBBA-4F80-831B-94BDE64D263C}"
11 | ProjectSection(SolutionItems) = preProject
12 | .nuget\Microsoft.Build.dll = .nuget\Microsoft.Build.dll
13 | .nuget\NuGet.Config = .nuget\NuGet.Config
14 | .nuget\NuGet.targets = .nuget\NuGet.targets
15 | EndProjectSection
16 | EndProject
17 | Global
18 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
19 | Debug|Any CPU = Debug|Any CPU
20 | Release|Any CPU = Release|Any CPU
21 | EndGlobalSection
22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
23 | {E81DED36-EDF5-41A5-8666-A3A0C581762F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
24 | {E81DED36-EDF5-41A5-8666-A3A0C581762F}.Debug|Any CPU.Build.0 = Debug|Any CPU
25 | {E81DED36-EDF5-41A5-8666-A3A0C581762F}.Release|Any CPU.ActiveCfg = Release|Any CPU
26 | {E81DED36-EDF5-41A5-8666-A3A0C581762F}.Release|Any CPU.Build.0 = Release|Any CPU
27 | {6CEC0333-81EB-40EE-85D1-941363626FC7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
28 | {6CEC0333-81EB-40EE-85D1-941363626FC7}.Debug|Any CPU.Build.0 = Debug|Any CPU
29 | {6CEC0333-81EB-40EE-85D1-941363626FC7}.Release|Any CPU.ActiveCfg = Release|Any CPU
30 | {6CEC0333-81EB-40EE-85D1-941363626FC7}.Release|Any CPU.Build.0 = Release|Any CPU
31 | EndGlobalSection
32 | GlobalSection(SolutionProperties) = preSolution
33 | HideSolutionNode = FALSE
34 | EndGlobalSection
35 | GlobalSection(MonoDevelopProperties) = preSolution
36 | StartupItem = SteamBot\ExampleBot.csproj
37 | Policies = $0
38 | $0.DotNetNamingPolicy = $1
39 | $1.DirectoryNamespaceAssociation = None
40 | $1.ResourceNamePolicy = FileFormatDefault
41 | $0.TextStylePolicy = $5
42 | $2.inheritsSet = null
43 | $2.scope = text/x-csharp
44 | $0.CSharpFormattingPolicy = $3
45 | $3.AnonymousMethodBraceStyle = NextLine
46 | $3.PropertyBraceStyle = NextLine
47 | $3.PropertyGetBraceStyle = NextLine
48 | $3.PropertySetBraceStyle = NextLine
49 | $3.EventBraceStyle = NextLine
50 | $3.EventAddBraceStyle = NextLine
51 | $3.EventRemoveBraceStyle = NextLine
52 | $3.StatementBraceStyle = NextLine
53 | $3.ElseNewLinePlacement = NewLine
54 | $3.CatchNewLinePlacement = NewLine
55 | $3.FinallyNewLinePlacement = NewLine
56 | $3.inheritsSet = Mono
57 | $3.inheritsScope = text/x-csharp
58 | $3.scope = text/x-csharp
59 | $4.FileWidth = 120
60 | $4.inheritsSet = VisualStudio
61 | $4.inheritsScope = text/plain
62 | $4.scope = text/plain
63 | $5.inheritsSet = null
64 | $5.scope = application/octet-stream
65 | $0.StandardHeader = $6
66 | $6.Text =
67 | $6.IncludeInNewFiles = True
68 | $0.NameConventionPolicy = $7
69 | $7.Rules = $8
70 | $8.NamingRule = $33
71 | $9.Name = Namespaces
72 | $9.AffectedEntity = Namespace
73 | $9.VisibilityMask = VisibilityMask
74 | $9.NamingStyle = PascalCase
75 | $9.IncludeInstanceMembers = True
76 | $9.IncludeStaticEntities = True
77 | $10.Name = Types
78 | $10.AffectedEntity = Class, Struct, Enum, Delegate
79 | $10.VisibilityMask = VisibilityMask
80 | $10.NamingStyle = PascalCase
81 | $10.IncludeInstanceMembers = True
82 | $10.IncludeStaticEntities = True
83 | $11.Name = Interfaces
84 | $11.RequiredPrefixes = $12
85 | $12.String = I
86 | $11.AffectedEntity = Interface
87 | $11.VisibilityMask = VisibilityMask
88 | $11.NamingStyle = PascalCase
89 | $11.IncludeInstanceMembers = True
90 | $11.IncludeStaticEntities = True
91 | $13.Name = Attributes
92 | $13.RequiredSuffixes = $14
93 | $14.String = Attribute
94 | $13.AffectedEntity = CustomAttributes
95 | $13.VisibilityMask = VisibilityMask
96 | $13.NamingStyle = PascalCase
97 | $13.IncludeInstanceMembers = True
98 | $13.IncludeStaticEntities = True
99 | $15.Name = Event Arguments
100 | $15.RequiredSuffixes = $16
101 | $16.String = EventArgs
102 | $15.AffectedEntity = CustomEventArgs
103 | $15.VisibilityMask = VisibilityMask
104 | $15.NamingStyle = PascalCase
105 | $15.IncludeInstanceMembers = True
106 | $15.IncludeStaticEntities = True
107 | $17.Name = Exceptions
108 | $17.RequiredSuffixes = $18
109 | $18.String = Exception
110 | $17.AffectedEntity = CustomExceptions
111 | $17.VisibilityMask = VisibilityMask
112 | $17.NamingStyle = PascalCase
113 | $17.IncludeInstanceMembers = True
114 | $17.IncludeStaticEntities = True
115 | $19.Name = Methods
116 | $19.AffectedEntity = Methods
117 | $19.VisibilityMask = VisibilityMask
118 | $19.NamingStyle = PascalCase
119 | $19.IncludeInstanceMembers = True
120 | $19.IncludeStaticEntities = True
121 | $20.Name = Static Readonly Fields
122 | $20.AffectedEntity = ReadonlyField
123 | $20.VisibilityMask = Internal, Protected, Public
124 | $20.NamingStyle = PascalCase
125 | $20.IncludeInstanceMembers = False
126 | $20.IncludeStaticEntities = True
127 | $21.Name = Fields (Non Private)
128 | $21.AffectedEntity = Field
129 | $21.VisibilityMask = Internal, Protected, Public
130 | $21.NamingStyle = PascalCase
131 | $21.IncludeInstanceMembers = True
132 | $21.IncludeStaticEntities = True
133 | $22.Name = ReadOnly Fields (Non Private)
134 | $22.AffectedEntity = ReadonlyField
135 | $22.VisibilityMask = Internal, Protected, Public
136 | $22.NamingStyle = PascalCase
137 | $22.IncludeInstanceMembers = True
138 | $22.IncludeStaticEntities = False
139 | $23.Name = Fields (Private)
140 | $23.AllowedPrefixes = $24
141 | $24.String = m_
142 | $23.AffectedEntity = Field, ReadonlyField
143 | $23.VisibilityMask = Private
144 | $23.NamingStyle = CamelCase
145 | $23.IncludeInstanceMembers = True
146 | $23.IncludeStaticEntities = False
147 | $25.Name = Static Fields (Private)
148 | $25.AffectedEntity = Field
149 | $25.VisibilityMask = Private
150 | $25.NamingStyle = CamelCase
151 | $25.IncludeInstanceMembers = False
152 | $25.IncludeStaticEntities = True
153 | $26.Name = ReadOnly Fields (Private)
154 | $26.AllowedPrefixes = $27
155 | $27.String = m_
156 | $26.AffectedEntity = ReadonlyField
157 | $26.VisibilityMask = Private
158 | $26.NamingStyle = CamelCase
159 | $26.IncludeInstanceMembers = True
160 | $26.IncludeStaticEntities = False
161 | $28.Name = Constant Fields
162 | $28.AffectedEntity = ConstantField
163 | $28.VisibilityMask = VisibilityMask
164 | $28.NamingStyle = PascalCase
165 | $28.IncludeInstanceMembers = True
166 | $28.IncludeStaticEntities = True
167 | $29.Name = Properties
168 | $29.AffectedEntity = Property
169 | $29.VisibilityMask = VisibilityMask
170 | $29.NamingStyle = PascalCase
171 | $29.IncludeInstanceMembers = True
172 | $29.IncludeStaticEntities = True
173 | $30.Name = Events
174 | $30.AffectedEntity = Event
175 | $30.VisibilityMask = VisibilityMask
176 | $30.NamingStyle = PascalCase
177 | $30.IncludeInstanceMembers = True
178 | $30.IncludeStaticEntities = True
179 | $31.Name = Enum Members
180 | $31.AffectedEntity = EnumMember
181 | $31.VisibilityMask = VisibilityMask
182 | $31.NamingStyle = PascalCase
183 | $31.IncludeInstanceMembers = True
184 | $31.IncludeStaticEntities = True
185 | $32.Name = Parameters
186 | $32.AffectedEntity = Parameter
187 | $32.VisibilityMask = VisibilityMask
188 | $32.NamingStyle = CamelCase
189 | $32.IncludeInstanceMembers = True
190 | $32.IncludeStaticEntities = True
191 | $33.Name = Type Parameters
192 | $33.RequiredPrefixes = $34
193 | $34.String = T
194 | $33.AffectedEntity = TypeParameter
195 | $33.VisibilityMask = VisibilityMask
196 | $33.NamingStyle = PascalCase
197 | $33.IncludeInstanceMembers = True
198 | $33.IncludeStaticEntities = True
199 | outputpath = ..\Bin
200 | EndGlobalSection
201 | EndGlobal
202 |
--------------------------------------------------------------------------------
/SteamBot/AdminUserHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows.Forms;
3 | using SteamKit2;
4 | using SteamTrade;
5 | using System.Collections.Generic;
6 | using SteamTrade.TradeOffer;
7 |
8 | namespace SteamBot
9 | {
10 | ///
11 | /// A user handler class that implements basic text-based commands entered in
12 | /// chat or trade chat.
13 | ///
14 | public class AdminUserHandler : UserHandler
15 | {
16 | private const string AddCmd = "add";
17 | private const string RemoveCmd = "remove";
18 | private const string AddCratesSubCmd = "crates";
19 | private const string AddWepsSubCmd = "weapons";
20 | private const string AddMetalSubCmd = "metal";
21 | private const string AddAllSubCmd = "all";
22 | private const string HelpCmd = "help";
23 |
24 | public AdminUserHandler(Bot bot, SteamID sid) : base(bot, sid) {}
25 |
26 | #region Overrides of UserHandler
27 |
28 | ///
29 | /// Called when the bot is fully logged in.
30 | ///
31 | public override void OnLoginCompleted()
32 | {
33 | }
34 |
35 | ///
36 | /// Triggered when a clan invites the bot.
37 | ///
38 | ///
39 | /// Whether to accept.
40 | ///
41 | public override bool OnGroupAdd()
42 | {
43 | return false;
44 | }
45 |
46 | ///
47 | /// Called when a the user adds the bot as a friend.
48 | ///
49 | ///
50 | /// Whether to accept.
51 | ///
52 | public override bool OnFriendAdd()
53 | {
54 | // if the other is an admin then accept add
55 | if (IsAdmin)
56 | {
57 | return true;
58 | }
59 |
60 | Log.Warn("Random SteamID: " + OtherSID + " tried to add the bot as a friend");
61 | return false;
62 | }
63 |
64 | public override void OnFriendRemove()
65 | {
66 | }
67 |
68 | ///
69 | /// Called whenever a message is sent to the bot.
70 | /// This is limited to regular and emote messages.
71 | ///
72 | public override void OnMessage(string message, EChatEntryType type)
73 | {
74 | // TODO: magic command system
75 | }
76 |
77 | ///
78 | /// Called whenever a user requests a trade.
79 | ///
80 | ///
81 | /// Whether to accept the request.
82 | ///
83 | public override bool OnTradeRequest()
84 | {
85 | if (IsAdmin)
86 | return true;
87 |
88 | return false;
89 | }
90 |
91 | public override void OnTradeError(string error)
92 | {
93 | Log.Error(error);
94 | }
95 |
96 | public override void OnTradeTimeout()
97 | {
98 | Log.Warn("Trade timed out.");
99 | }
100 |
101 | public override void OnTradeInit()
102 | {
103 | SendTradeMessage("Success. (Type {0} for commands)", HelpCmd);
104 | }
105 |
106 | public override void OnTradeAddItem(Schema.Item schemaItem, Inventory.Item inventoryItem)
107 | {
108 | // whatever.
109 | }
110 |
111 | public override void OnTradeRemoveItem(Schema.Item schemaItem, Inventory.Item inventoryItem)
112 | {
113 | // whatever.
114 | }
115 |
116 | public override void OnTradeMessage(string message)
117 | {
118 | ProcessTradeMessage(message);
119 | }
120 |
121 | public override void OnTradeReady(bool ready)
122 | {
123 | if (!IsAdmin)
124 | {
125 | SendTradeMessage("You are not my master.");
126 | Trade.SetReady(false);
127 | return;
128 | }
129 |
130 | Trade.SetReady(true);
131 | }
132 |
133 | public override void OnTradeOfferUpdated(TradeOffer offer)
134 | {
135 | if(offer.OfferState == TradeOfferState.TradeOfferStateAccepted)
136 | {
137 | Log.Success("Trade Complete.");
138 | }
139 | }
140 |
141 | public override void OnTradeAwaitingConfirmation(long tradeOfferID)
142 | {
143 | Log.Warn("Trade ended awaiting confirmation");
144 | SendChatMessage("Please complete the confirmation to finish the trade");
145 | }
146 |
147 | public override void OnTradeAccept()
148 | {
149 | if (IsAdmin)
150 | {
151 | //Even if it is successful, AcceptTrade can fail on
152 | //trades with a lot of items so we use a try-catch
153 | try
154 | {
155 | if (Trade.AcceptTrade())
156 | Log.Success("Trade Accepted!");
157 | }
158 | catch
159 | {
160 | Log.Warn("The trade might have failed, but we can't be sure.");
161 | }
162 | }
163 | }
164 |
165 | #endregion
166 |
167 | private void ProcessTradeMessage(string message)
168 | {
169 | if (message.Equals(HelpCmd))
170 | {
171 | PrintHelpMessage();
172 | return;
173 | }
174 |
175 | if (message.StartsWith(AddCmd))
176 | {
177 | HandleAddCommand(message);
178 | SendTradeMessage("done adding.");
179 | }
180 | else if (message.StartsWith(RemoveCmd))
181 | {
182 | HandleRemoveCommand(message);
183 | SendTradeMessage("done removing.");
184 | }
185 | }
186 |
187 | private void PrintHelpMessage()
188 | {
189 | SendTradeMessage("{0} {1} [amount] [series] - adds all crates (optionally by series number, use 0 for amount to add all)", AddCmd, AddCratesSubCmd);
190 | SendTradeMessage("{0} {1} [amount] - adds metal", AddCmd, AddMetalSubCmd);
191 | SendTradeMessage("{0} {1} [amount] - adds weapons", AddCmd, AddWepsSubCmd);
192 | SendTradeMessage("{0} {1} [amount] - adds items", AddCmd, AddAllSubCmd);
193 | SendTradeMessage(@"{0} [amount] - adds all or a given amount of items of a given crafting type.", AddCmd);
194 | SendTradeMessage(@"{0} [amount] - adds all or a given amount of items of a given defindex.", AddCmd);
195 |
196 | SendTradeMessage(@"See http://wiki.teamfortress.com/wiki/WebAPI/GetSchema for info about craft_material_type or defindex.");
197 | }
198 |
199 | private void HandleAddCommand(string command)
200 | {
201 | var data = command.Split(' ');
202 | string typeToAdd;
203 |
204 | bool subCmdOk = GetSubCommand (data, out typeToAdd);
205 |
206 | if (!subCmdOk)
207 | return;
208 |
209 | uint amount = GetAddAmount (data);
210 |
211 | // if user supplies the defindex directly use it to add.
212 | int defindex;
213 | if (int.TryParse(typeToAdd, out defindex))
214 | {
215 | Trade.AddAllItemsByDefindex(defindex, amount);
216 | return;
217 | }
218 |
219 | switch (typeToAdd)
220 | {
221 | case AddMetalSubCmd:
222 | AddItemsByCraftType("craft_bar", amount);
223 | break;
224 | case AddWepsSubCmd:
225 | AddItemsByCraftType("weapon", amount);
226 | break;
227 | case AddCratesSubCmd:
228 | // data[3] is the optional series number
229 | if (!String.IsNullOrEmpty(data[3]))
230 | AddCrateBySeries(data[3], amount);
231 | else
232 | AddItemsByCraftType("supply_crate", amount);
233 | break;
234 | case AddAllSubCmd:
235 | AddAllItems();
236 | break;
237 | default:
238 | AddItemsByCraftType(typeToAdd, amount);
239 | break;
240 | }
241 | }
242 |
243 |
244 |
245 | private void HandleRemoveCommand(string command)
246 | {
247 | var data = command.Split(' ');
248 |
249 | string subCommand;
250 |
251 | bool subCmdOk = GetSubCommand(data, out subCommand);
252 |
253 | // were dumb right now... just remove everything.
254 | Trade.RemoveAllItems();
255 |
256 | if (!subCmdOk)
257 | return;
258 | }
259 |
260 |
261 | private void AddItemsByCraftType(string typeToAdd, uint amount)
262 | {
263 | var items = Trade.CurrentSchema.GetItemsByCraftingMaterial(typeToAdd);
264 |
265 | uint added = 0;
266 |
267 | foreach (var item in items)
268 | {
269 | added += Trade.AddAllItemsByDefindex(item.Defindex, amount);
270 |
271 | // if bulk adding something that has a lot of unique
272 | // defindex (weapons) we may over add so limit here also
273 | if (amount > 0 && added >= amount)
274 | return;
275 | }
276 | }
277 |
278 | private void AddAllItems()
279 | {
280 | var items = Trade.CurrentSchema.GetItems();
281 |
282 | foreach (var item in items)
283 | {
284 | Trade.AddAllItemsByDefindex(item.Defindex, 0);
285 | }
286 | }
287 |
288 | private void AddCrateBySeries(string series, uint amount)
289 | {
290 | int ser;
291 | bool parsed = int.TryParse(series, out ser);
292 |
293 | if (!parsed)
294 | return;
295 |
296 | var l = Trade.CurrentSchema.GetItemsByCraftingMaterial("supply_crate");
297 |
298 |
299 | List invItems = new List();
300 |
301 | foreach (var schemaItem in l)
302 | {
303 | ushort defindex = schemaItem.Defindex;
304 | invItems.AddRange(Trade.MyInventory.GetItemsByDefindex(defindex));
305 | }
306 |
307 | uint added = 0;
308 |
309 | foreach (var item in invItems)
310 | {
311 | int crateNum = 0;
312 | for (int count = 0; count < item.Attributes.Length; count++)
313 | {
314 | // FloatValue will give you the crate's series number
315 | crateNum = (int) item.Attributes[count].FloatValue;
316 |
317 | if (crateNum == ser)
318 | {
319 | bool ok = Trade.AddItem(item.Id);
320 |
321 | if (ok)
322 | added++;
323 |
324 | // if bulk adding something that has a lot of unique
325 | // defindex (weapons) we may over add so limit here also
326 | if (amount > 0 && added >= amount)
327 | return;
328 | }
329 | }
330 | }
331 | }
332 |
333 | bool GetSubCommand (string[] data, out string subCommand)
334 | {
335 | if (data.Length < 2)
336 | {
337 | SendTradeMessage("No parameter for cmd");
338 | subCommand = null;
339 | return false;
340 | }
341 |
342 | if (String.IsNullOrEmpty (data [1]))
343 | {
344 | SendTradeMessage("No parameter for cmd");
345 | subCommand = null;
346 | return false;
347 | }
348 |
349 | subCommand = data [1];
350 |
351 | return true;
352 | }
353 |
354 | static uint GetAddAmount (string[] data)
355 | {
356 | uint amount = 0;
357 |
358 | if (data.Length > 2)
359 | {
360 | // get the optional amount parameter
361 | if (!String.IsNullOrEmpty (data [2]))
362 | {
363 | uint.TryParse (data [2], out amount);
364 | }
365 | }
366 |
367 | return amount;
368 | }
369 | }
370 | }
--------------------------------------------------------------------------------
/SteamBot/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 |
3 | [assembly: AssemblyTitle("SteamBot")]
4 | [assembly: AssemblyDescription("SteamBot is a bot for the purpose of interacting with Steam Chat and Steam Trade.")]
5 | [assembly: AssemblyConfiguration("")]
6 | [assembly: AssemblyProduct("SteamBot")]
7 | [assembly: AssemblyCopyright("(C) 2012 SteamBot Contributors")]
8 | [assembly: AssemblyTrademark("")]
9 | [assembly: AssemblyCulture("")]
10 |
11 |
12 | // "{Major}.{Minor}.{Build}.*" will automatically update the revision.
13 | // SteamBot uses Semantic Versioning (http://semver.org/)
14 | [assembly: AssemblyVersion("0.1.1.*")]
15 |
16 |
--------------------------------------------------------------------------------
/SteamBot/BotInfo.cs:
--------------------------------------------------------------------------------
1 | namespace SteamBot
2 | {
3 | public class BotFile
4 | {
5 | public ulong[] Admins { get; set; }
6 | public BotInfo[] Bots { get; set; }
7 | public string ApiKey { get; set; }
8 | }
9 |
10 | public class BotInfo
11 | {
12 | public string Username { get; set; }
13 | public string Password { get; set; }
14 | public string DisplayName { get; set; }
15 | public string ChatResponse { get; set; }
16 | public ulong[] Admins;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/SteamBot/BotManagerInterpreter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Collections.ObjectModel;
4 |
5 | namespace SteamBot
6 | {
7 | ///
8 | /// A interpreter for the bot manager so a user can control bots.
9 | ///
10 | ///
11 | /// There is currently a small set of commands this can interpret. They
12 | /// are limited by the functionality the class
13 | /// exposes.
14 | ///
15 | public class BotManagerInterpreter
16 | {
17 | private readonly BotManager manager;
18 | private CommandSet p;
19 | private string start = String.Empty;
20 | private string stop = String.Empty;
21 | private bool showHelp;
22 | private bool clearConsole;
23 |
24 | public BotManagerInterpreter(BotManager manager)
25 | {
26 | this.manager = manager;
27 | p = new CommandSet
28 | {
29 | new BotManagerOption("stop", "stop (X) where X = the username or index of the configured bot",
30 | s => stop = s),
31 | new BotManagerOption("start", "start (X) where X = the username or index of the configured bot",
32 | s => start = s),
33 | new BotManagerOption("help", "shows this help text", s => showHelp = s != null),
34 | new BotManagerOption("show",
35 | "show (x) where x is one of the following: index, \"bots\", or empty",
36 | param => ShowCommand(param)),
37 | new BotManagerOption("clear", "clears this console", s => clearConsole = s != null),
38 | new BotManagerOption("auth", "auth (X)=(Y) where X = the username or index of the configured bot and Y = the steamguard code",
39 | AuthSet),
40 | new BotManagerOption("exec",
41 | "exec (X) (Y) where X = the username or index of the bot and Y = your custom command to execute",
42 | ExecCommand),
43 | new BotManagerOption("input",
44 | "input (X) (Y) where X = the username or index of the bot and Y = your input",
45 | InputCommand)
46 | };
47 | }
48 |
49 | void AuthSet(string auth)
50 | {
51 | string[] xy = auth.Split('=');
52 |
53 | if (xy.Length == 2)
54 | {
55 | int index;
56 | string code = xy[1].Trim();
57 |
58 | if (int.TryParse(xy[0], out index) && (index < manager.ConfigObject.Bots.Length))
59 | {
60 | Console.WriteLine("Authing bot with '" + code + "'");
61 | manager.AuthBot(index, code);
62 | }
63 | else if (!String.IsNullOrEmpty(xy[0]))
64 | {
65 | for (index = 0; index < manager.ConfigObject.Bots.Length; index++)
66 | {
67 | if (manager.ConfigObject.Bots[index].Username.Equals(xy[0], StringComparison.CurrentCultureIgnoreCase))
68 | {
69 | Console.WriteLine("Authing bot with '" + code + "'");
70 | manager.AuthBot(index, code);
71 | }
72 | }
73 | }
74 | }
75 | }
76 |
77 | ///
78 | /// This interprets the given command string.
79 | ///
80 | /// The entire command string.
81 | public void CommandInterpreter(string command)
82 | {
83 | showHelp = false;
84 | start = null;
85 | stop = null;
86 |
87 | p.Parse(command);
88 |
89 | if (showHelp)
90 | {
91 | Console.WriteLine("");
92 | p.WriteOptionDescriptions(Console.Out);
93 | }
94 |
95 | if (!String.IsNullOrEmpty(stop))
96 | {
97 | int index;
98 | if (int.TryParse(stop, out index) && (index < manager.ConfigObject.Bots.Length))
99 | {
100 | manager.StopBot(index);
101 | }
102 | else
103 | {
104 | manager.StopBot(stop);
105 | }
106 | }
107 |
108 | if (!String.IsNullOrEmpty(start))
109 | {
110 | int index;
111 | if (int.TryParse(start, out index) && (index < manager.ConfigObject.Bots.Length))
112 | {
113 | manager.StartBot(index);
114 | }
115 | else
116 | {
117 | manager.StartBot(start);
118 | }
119 | }
120 |
121 | if (clearConsole)
122 | {
123 | clearConsole = false;
124 | Console.Clear();
125 | }
126 | }
127 |
128 | private void ShowCommand(string param)
129 | {
130 | param = param.Trim();
131 |
132 | int i;
133 | if (int.TryParse(param, out i))
134 | {
135 | // spit out the bots config at index.
136 | if (manager.ConfigObject.Bots.Length > i)
137 | {
138 | Console.WriteLine();
139 | Console.WriteLine(manager.ConfigObject.Bots[i].ToString());
140 | }
141 | }
142 | else if (!String.IsNullOrEmpty(param))
143 | {
144 | if (param.Equals("bots"))
145 | {
146 | // print out the config.Bots array
147 | foreach (var b in manager.ConfigObject.Bots)
148 | {
149 | Console.WriteLine();
150 | Console.WriteLine(b.ToString());
151 | Console.WriteLine();
152 | }
153 | }
154 | }
155 | else
156 | {
157 | // print out entire config.
158 | // the bots array does not get printed.
159 | Console.WriteLine();
160 | Console.WriteLine(manager.ConfigObject.ToString());
161 | }
162 | }
163 |
164 | private void ExecCommand(string cmd)
165 | {
166 | cmd = cmd.Trim();
167 |
168 | var cs = cmd.Split(' ');
169 |
170 | if (cs.Length < 2)
171 | {
172 | Console.WriteLine("Error: No command given to be executed.");
173 | return;
174 | }
175 |
176 | // Take the rest of the input as is
177 | var command = cmd.Remove(0, cs[0].Length + 1);
178 |
179 | int index;
180 | // Try index first then search usernames
181 | if (int.TryParse(cs[0], out index) && (index < manager.ConfigObject.Bots.Length))
182 | {
183 | if (manager.ConfigObject.Bots.Length > index)
184 | {
185 | manager.SendCommand(index, command);
186 | return;
187 | }
188 | }
189 | else if (!String.IsNullOrEmpty(cs[0]))
190 | {
191 | for (index = 0; index < manager.ConfigObject.Bots.Length; index++)
192 | {
193 | if (manager.ConfigObject.Bots[index].Username.Equals(cs[0], StringComparison.CurrentCultureIgnoreCase))
194 | {
195 | manager.SendCommand(index, command);
196 | return;
197 | }
198 | }
199 | }
200 | // Print error
201 | Console.WriteLine("Error: Bot " + cs[0] + " not found.");
202 | }
203 |
204 | private void InputCommand(string inpt)
205 | {
206 | inpt = inpt.Trim();
207 |
208 | var cs = inpt.Split(' ');
209 |
210 | if (cs.Length < 2)
211 | {
212 | Console.WriteLine("Error: No input given.");
213 | return;
214 | }
215 |
216 | // Take the rest of the input as is
217 | var input = inpt.Remove(0, cs[0].Length + 1);
218 |
219 | int index;
220 | // Try index first then search usernames
221 | if (int.TryParse(cs[0], out index) && (index < manager.ConfigObject.Bots.Length))
222 | {
223 | if (manager.ConfigObject.Bots.Length > index)
224 | {
225 | manager.SendInput(index, input);
226 | return;
227 | }
228 | }
229 | else if (!String.IsNullOrEmpty(cs[0]))
230 | {
231 | for (index = 0; index < manager.ConfigObject.Bots.Length; index++)
232 | {
233 | if (manager.ConfigObject.Bots[index].Username.Equals(cs[0], StringComparison.CurrentCultureIgnoreCase))
234 | {
235 | manager.SendInput(index, input);
236 | return;
237 | }
238 | }
239 | }
240 | // Print error
241 | Console.WriteLine("Error: Bot " + cs[0] + " not found.");
242 | }
243 |
244 | #region Nested Options classes
245 | // these are very much like the NDesk.Options but without the
246 | // maturity, features or need for command seprators like "-" or "/"
247 |
248 | private class BotManagerOption
249 | {
250 | public string Name { get; set; }
251 | public string Help { get; set; }
252 | public Action Func { get; set; }
253 |
254 | public BotManagerOption(string name, string help, Action func)
255 | {
256 | Name = name;
257 | Help = help;
258 | Func = func;
259 | }
260 | }
261 |
262 | private class CommandSet : KeyedCollection
263 | {
264 | protected override string GetKeyForItem(BotManagerOption item)
265 | {
266 | return item.Name;
267 | }
268 |
269 | public void Parse(string commandLine)
270 | {
271 | var c = commandLine.Trim();
272 |
273 | var cs = c.Split(' ');
274 |
275 | foreach (var option in this)
276 | {
277 | if (cs[0].Equals(option.Name, StringComparison.CurrentCultureIgnoreCase))
278 | {
279 | if (cs.Length > 2)
280 | {
281 | option.Func(c.Remove(0, cs[0].Length + 1));
282 | }
283 | else if (cs.Length > 1)
284 | {
285 | option.Func(cs[1]);
286 | }
287 | else
288 | {
289 | option.Func(String.Empty);
290 | }
291 | }
292 | }
293 | }
294 |
295 | public void WriteOptionDescriptions(TextWriter o)
296 | {
297 | foreach (BotManagerOption p in this)
298 | {
299 | o.Write('\t');
300 | o.WriteLine(p.Name + '\t' + p.Help);
301 | }
302 | }
303 | }
304 |
305 | #endregion Nested Options classes
306 | }
307 | }
308 |
--------------------------------------------------------------------------------
/SteamBot/Configuration.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Text.RegularExpressions;
8 | using Newtonsoft.Json;
9 | using Newtonsoft.Json.Linq;
10 |
11 | namespace SteamBot
12 | {
13 | public class Configuration
14 | {
15 | private class JsonToSteamID : JsonConverter
16 | {
17 | static Regex Steam2Regex = new Regex(
18 | @"STEAM_(?[0-5]):(?[0-1]):(?\d+)",
19 | RegexOptions.Compiled | RegexOptions.IgnoreCase);
20 | public override bool CanConvert(Type objectType)
21 | {
22 | return objectType == typeof(IEnumerable);
23 | }
24 |
25 | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
26 | {
27 | JArray array = JArray.Load(reader);
28 | List ret = new List();
29 | foreach (JToken id in array)
30 | {
31 | string sID = (string)id;
32 | if (Steam2Regex.IsMatch(sID))
33 | ret.Add(new SteamKit2.SteamID(sID));
34 | else
35 | ret.Add(new SteamKit2.SteamID((ulong)id));
36 | }
37 | return ret;
38 | }
39 |
40 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
41 | {
42 | throw new NotImplementedException();
43 | }
44 | }
45 |
46 | public static Configuration LoadConfiguration (string filename)
47 | {
48 | TextReader reader = new StreamReader(filename);
49 | string json = reader.ReadToEnd();
50 | reader.Close();
51 |
52 | Configuration config = JsonConvert.DeserializeObject(json);
53 |
54 | config.Admins = config.Admins ?? new SteamKit2.SteamID[0];
55 |
56 | // merge bot-specific admins with global admins
57 | foreach (BotInfo bot in config.Bots)
58 | {
59 | if (bot.Admins == null)
60 | bot.Admins = config.Admins;
61 | else
62 | bot.Admins = bot.Admins.Concat(config.Admins);
63 | }
64 |
65 | return config;
66 | }
67 |
68 | #region Top-level config properties
69 |
70 | ///
71 | /// Gets or sets the admins.
72 | ///
73 | ///
74 | /// An array of Steam Profile IDs (64 bit IDs) of the users that are an
75 | /// Admin of your bot(s). Each Profile ID should be a string in quotes
76 | /// and separated by a comma. These admins are global to all bots
77 | /// listed in the Bots array.
78 | ///
79 | [JsonConverter(typeof(JsonToSteamID))]
80 | public IEnumerable Admins { get; set; }
81 |
82 | ///
83 | /// Gets or sets the bots array.
84 | ///
85 | ///
86 | /// The Bots object is an array of BotInfo objects containing
87 | /// information about each individual bot you will be running.
88 | ///
89 | public BotInfo[] Bots { get; set; }
90 |
91 | ///
92 | /// Gets or sets YOUR API key.
93 | ///
94 | ///
95 | /// The API key you have been assigned by Valve. If you do not have
96 | /// one, it can be requested from Value at their Web API Key page. This
97 | /// is required and the bot(s) will not work without an API Key.
98 | ///
99 | public string ApiKey { get; set; }
100 |
101 | ///
102 | /// Gets or sets the main log file name.
103 | ///
104 | public string MainLog { get; set; }
105 |
106 | ///
107 | /// Gets or sets a value indicating whether to use separate processes.
108 | ///
109 | ///
110 | /// true if bot manager is to open each bot in it's own process;
111 | /// otherwise, false to open each bot in a separate thread.
112 | /// Default is false.
113 | ///
114 | public bool UseSeparateProcesses { get; set; }
115 |
116 | ///
117 | /// Gets or sets a value indicating whether to auto start all bots.
118 | ///
119 | ///
120 | /// true to make the bots start on program load; otherwise,
121 | /// false to not start them.
122 | ///
123 | public bool AutoStartAllBots { get; set; }
124 |
125 | #endregion Top-level config properties
126 |
127 | ///
128 | /// Returns a that represents this instance.
129 | ///
130 | ///
131 | /// A that represents this instance.
132 | ///
133 | public override string ToString()
134 | {
135 | StringBuilder sb = new StringBuilder();
136 | var fields = this.GetType().GetProperties();
137 |
138 | foreach (var propInfo in fields)
139 | {
140 | sb.AppendFormat("{0} = {1}" + Environment.NewLine,
141 | propInfo.Name,
142 | propInfo.GetValue(this, null));
143 | }
144 |
145 | return sb.ToString();
146 | }
147 |
148 | public class BotInfo
149 | {
150 | public string Username { get; set; }
151 | public string Password { get; set; }
152 | public string ApiKey { get; set; }
153 | public string DisplayName { get; set; }
154 | public string ChatResponse { get; set; }
155 | public string LogFile { get; set; }
156 | public string BotControlClass { get; set; }
157 | public int MaximumTradeTime { get; set; }
158 | public int MaximumActionGap { get; set; }
159 | public string DisplayNamePrefix { get; set; }
160 | public int TradePollingInterval { get; set; }
161 | public int TradeOfferPollingIntervalSecs { get; set; }
162 | public string ConsoleLogLevel { get; set; }
163 | public string FileLogLevel { get; set; }
164 | [JsonConverter(typeof(JsonToSteamID))]
165 | public IEnumerable Admins { get; set; }
166 | public string SchemaLang { get; set; }
167 |
168 | // Depreciated configuration options
169 | public string LogLevel { get; set; }
170 |
171 | ///
172 | /// Gets or sets a value indicating whether to auto start this bot.
173 | ///
174 | ///
175 | /// true to make the bot start on program load.
176 | ///
177 | ///
178 | /// If is true,
179 | /// then this property has no effect and is ignored.
180 | ///
181 | [JsonProperty (Required = Required.Default, DefaultValueHandling = DefaultValueHandling.Populate)]
182 | [DefaultValue (true)]
183 | public bool AutoStart { get; set; }
184 |
185 | public override string ToString()
186 | {
187 | StringBuilder sb = new StringBuilder();
188 | var fields = this.GetType().GetProperties();
189 |
190 | foreach (var propInfo in fields)
191 | {
192 | sb.AppendFormat("{0} = {1}" + Environment.NewLine,
193 | propInfo.Name,
194 | propInfo.GetValue(this, null));
195 | }
196 |
197 | return sb.ToString();
198 | }
199 | }
200 | }
201 | }
202 |
--------------------------------------------------------------------------------
/SteamBot/ExampleBot.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | x86
6 | 10.0.0
7 | 2.0
8 | {E81DED36-EDF5-41A5-8666-A3A0C581762F}
9 | Exe
10 | SteamBot
11 | SteamBot
12 | ..\
13 | true
14 | v4.5
15 |
16 |
17 |
18 | True
19 | full
20 | false
21 | ..\Bin\Debug
22 | DEBUG;
23 | prompt
24 | 4
25 | AnyCPU
26 | True
27 | false
28 | 5
29 |
30 |
31 | full
32 | true
33 | ..\Bin\Release
34 | prompt
35 | 4
36 | AnyCPU
37 | True
38 | false
39 | true
40 |
41 |
42 |
43 | ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll
44 | True
45 |
46 |
47 | False
48 | ..\packages\protobuf-net.2.0.0.668\lib\net40\protobuf-net.dll
49 |
50 |
51 | ..\packages\SteamAuth.2.0.0\lib\net45\SteamAuth.dll
52 | True
53 |
54 |
55 | ..\packages\SteamKit2.1.8.1\lib\net45\SteamKit2.dll
56 | True
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | False
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 | {6CEC0333-81EB-40EE-85D1-941363626FC7}
102 | SteamTrade
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
--------------------------------------------------------------------------------
/SteamBot/ExampleBot.csproj.DotSettings:
--------------------------------------------------------------------------------
1 |
2 | CSharp50
--------------------------------------------------------------------------------
/SteamBot/Log.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Linq;
4 |
5 | namespace SteamBot
6 | {
7 | public class Log : IDisposable
8 | {
9 | public enum LogLevel
10 | {
11 | Debug,
12 | Info,
13 | Success,
14 | Warn,
15 | Error,
16 | Interface, // if the user needs to input something
17 | Nothing // not recommended; it basically silences
18 | // the console output because nothing is
19 | // greater than it. even if the bot needs
20 | // input, it won't be shown in the console.
21 | }
22 |
23 |
24 | protected StreamWriter _FileStream;
25 | protected string _botName;
26 | private bool disposed;
27 | public LogLevel OutputLevel;
28 | public LogLevel FileLogLevel;
29 | public ConsoleColor DefaultConsoleColor = ConsoleColor.White;
30 | public bool ShowBotName { get; set; }
31 |
32 | public Log(string logFile, string botName = "", LogLevel consoleLogLevel = LogLevel.Info, LogLevel fileLogLevel = LogLevel.Info)
33 | {
34 | Directory.CreateDirectory(Path.Combine(System.Windows.Forms.Application.StartupPath, "logs"));
35 | _FileStream = File.AppendText (Path.Combine("logs",logFile));
36 | _FileStream.AutoFlush = true;
37 | _botName = botName;
38 | OutputLevel = consoleLogLevel;
39 | FileLogLevel = fileLogLevel;
40 | Console.ForegroundColor = DefaultConsoleColor;
41 | ShowBotName = true;
42 | }
43 |
44 | ~Log()
45 | {
46 | Dispose(false);
47 | }
48 |
49 | // This outputs a log entry of the level info.
50 | public void Info(string data, params object[] formatParams)
51 | {
52 | _OutputLine(LogLevel.Info, data, formatParams);
53 | }
54 |
55 | // This outputs a log entry of the level debug.
56 | public void Debug(string data, params object[] formatParams)
57 | {
58 | _OutputLine(LogLevel.Debug, data, formatParams);
59 | }
60 |
61 | // This outputs a log entry of the level success.
62 | public void Success(string data, params object[] formatParams)
63 | {
64 | _OutputLine(LogLevel.Success, data, formatParams);
65 | }
66 |
67 | // This outputs a log entry of the level warn.
68 | public void Warn(string data, params object[] formatParams)
69 | {
70 | _OutputLine(LogLevel.Warn, data, formatParams);
71 | }
72 |
73 | // This outputs a log entry of the level error.
74 | public void Error(string data, params object[] formatParams)
75 | {
76 | _OutputLine(LogLevel.Error, data, formatParams);
77 | }
78 |
79 | // This outputs a log entry of the level interface;
80 | // normally, this means that some sort of user interaction
81 | // is required.
82 | public void Interface(string data, params object[] formatParams)
83 | {
84 | _OutputLine(LogLevel.Interface, data, formatParams);
85 | }
86 |
87 | // Outputs a line to both the log and the console, if
88 | // applicable.
89 | protected void _OutputLine(LogLevel level, string line, params object[] formatParams)
90 | {
91 | if (disposed)
92 | return;
93 | string formattedString = String.Format(
94 | "[{0}{1}] {2}: {3}",
95 | GetLogBotName(),
96 | DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
97 | _LogLevel(level).ToUpper(), (formatParams != null && formatParams.Any() ? String.Format(line, formatParams) : line)
98 | );
99 |
100 | if(level >= FileLogLevel)
101 | {
102 | _FileStream.WriteLine(formattedString);
103 | }
104 | if(level >= OutputLevel)
105 | {
106 | _OutputLineToConsole(level, formattedString);
107 | }
108 | }
109 |
110 | private string GetLogBotName()
111 | {
112 | if(_botName == null)
113 | {
114 | return "(System) ";
115 | }
116 | else if(ShowBotName)
117 | {
118 | return _botName + " ";
119 | }
120 | return "";
121 | }
122 |
123 | // Outputs a line to the console, with the correct color
124 | // formatting.
125 | protected void _OutputLineToConsole (LogLevel level, string line)
126 | {
127 | Console.ForegroundColor = _LogColor (level);
128 | Console.WriteLine (line);
129 | Console.ForegroundColor = DefaultConsoleColor;
130 | }
131 |
132 | // Determine the string equivalent of the LogLevel.
133 | protected string _LogLevel (LogLevel level)
134 | {
135 | switch (level)
136 | {
137 | case LogLevel.Info:
138 | return "info";
139 | case LogLevel.Debug:
140 | return "debug";
141 | case LogLevel.Success:
142 | return "success";
143 | case LogLevel.Warn:
144 | return "warn";
145 | case LogLevel.Error:
146 | return "error";
147 | case LogLevel.Interface:
148 | return "interface";
149 | case LogLevel.Nothing:
150 | return "nothing";
151 | default:
152 | return "undef";
153 | }
154 | }
155 |
156 | // Determine the color to be used when outputting to the
157 | // console.
158 | protected ConsoleColor _LogColor (LogLevel level)
159 | {
160 | switch (level)
161 | {
162 | case LogLevel.Info:
163 | case LogLevel.Debug:
164 | return ConsoleColor.White;
165 | case LogLevel.Success:
166 | return ConsoleColor.Green;
167 | case LogLevel.Warn:
168 | return ConsoleColor.Yellow;
169 | case LogLevel.Error:
170 | return ConsoleColor.Red;
171 | case LogLevel.Interface:
172 | return ConsoleColor.DarkCyan;
173 | default:
174 | return DefaultConsoleColor;
175 | }
176 | }
177 |
178 | private void Dispose(bool disposing)
179 | {
180 | if (disposed)
181 | return;
182 | if (disposing)
183 | _FileStream.Dispose();
184 | disposed = true;
185 | }
186 |
187 | public void Dispose()
188 | {
189 | Dispose(true);
190 | GC.SuppressFinalize(this);
191 | }
192 | }
193 | }
194 |
195 |
--------------------------------------------------------------------------------
/SteamBot/Notifications.cs:
--------------------------------------------------------------------------------
1 | using SteamKit2;
2 | using SteamKit2.Internal;
3 | using System.Collections.ObjectModel;
4 | using System.Linq;
5 |
6 | namespace SteamBot
7 | {
8 | public class SteamNotifications : ClientMsgHandler
9 | {
10 | public class NotificationCallback : CallbackMsg
11 | {
12 | public ReadOnlyCollection Notifications { get; private set; }
13 |
14 | internal NotificationCallback(CMsgClientUserNotifications msg)
15 | {
16 | var list = msg.notifications
17 | .Select(n => new Notification(n))
18 | .ToList();
19 |
20 | this.Notifications = new ReadOnlyCollection(list);
21 | }
22 |
23 | public sealed class Notification
24 | {
25 | internal Notification(CMsgClientUserNotifications.Notification notification)
26 | {
27 | Count = notification.count;
28 | UserNotificationType = (UserNotificationType)notification.user_notification_type;
29 | }
30 |
31 | public uint Count { get; private set; }
32 |
33 | public UserNotificationType UserNotificationType { get; private set; }
34 | }
35 |
36 | public enum UserNotificationType
37 | {
38 | TradeOffer = 1,
39 | Unknown
40 | }
41 | }
42 |
43 | public class CommentNotificationCallback : CallbackMsg
44 | {
45 | public CommentNotification CommentNotifications { get; private set; }
46 |
47 | internal CommentNotificationCallback(CMsgClientCommentNotifications msg)
48 | {
49 | CommentNotifications = new CommentNotification(msg);
50 | }
51 |
52 | public sealed class CommentNotification
53 | {
54 | internal CommentNotification(CMsgClientCommentNotifications msg)
55 | {
56 | CountNewComments = msg.count_new_comments;
57 | CountNewCommentsOwner = msg.count_new_comments_owner;
58 | CountNewCommentsSubscriptions = msg.count_new_comments_subscriptions;
59 | }
60 |
61 | public uint CountNewComments { get; private set; }
62 |
63 | public uint CountNewCommentsOwner { get; private set; }
64 |
65 | public uint CountNewCommentsSubscriptions { get; private set; }
66 | }
67 | }
68 |
69 | ///
70 | /// Request to see if the client user has any comment notifications
71 | ///
72 | public void RequestCommentNotifications()
73 | {
74 | var clientRequestCommentNotifications =
75 | new ClientMsgProtobuf(EMsg.ClientRequestCommentNotifications);
76 |
77 | Client.Send(clientRequestCommentNotifications);
78 | }
79 |
80 | ///
81 | /// Request to see if the client user has any notifications.
82 | ///
83 | public void RequestNotifications()
84 | {
85 | var requestItemAnnouncements =
86 | new ClientMsgProtobuf(EMsg.ClientRequestItemAnnouncements);
87 | Client.Send(requestItemAnnouncements);
88 | }
89 |
90 | public override void HandleMsg(IPacketMsg packetMsg)
91 | {
92 | switch (packetMsg.MsgType)
93 | {
94 | case EMsg.ClientNewLoginKey:
95 | HandleClientNewLoginKey(packetMsg);
96 | break;
97 |
98 | case EMsg.ClientUserNotifications:
99 | HandleClientUserNotifications(packetMsg);
100 | break;
101 |
102 | case EMsg.ClientCommentNotifications:
103 | HandleClientCommentNotifications(packetMsg);
104 | break;
105 | }
106 | }
107 |
108 | private void HandleClientUserNotifications(IPacketMsg packetMsg)
109 | {
110 | var clientUserNotificationResponse = new ClientMsgProtobuf(packetMsg);
111 |
112 | CMsgClientUserNotifications result = clientUserNotificationResponse.Body;
113 |
114 | Client.PostCallback(new NotificationCallback(result));
115 | }
116 |
117 | private void HandleClientCommentNotifications(IPacketMsg packetMsg)
118 | {
119 | var clientCommentNotifications = new ClientMsgProtobuf(packetMsg);
120 |
121 | CMsgClientCommentNotifications result = clientCommentNotifications.Body;
122 |
123 | Client.PostCallback(new CommentNotificationCallback(result));
124 | }
125 |
126 | private void HandleClientNewLoginKey(IPacketMsg packetMsg)
127 | {
128 | this.RequestCommentNotifications();
129 | this.RequestNotifications();
130 | }
131 | }
132 | }
--------------------------------------------------------------------------------
/SteamBot/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Runtime.InteropServices;
4 | using NDesk.Options;
5 |
6 | namespace SteamBot
7 | {
8 | public class Program
9 | {
10 | private static OptionSet opts = new OptionSet()
11 | {
12 | {"bot=", "launch a configured bot given that bots index in the configuration array.",
13 | b => botIndex = Convert.ToInt32(b) } ,
14 | { "help", "shows this help text", p => showHelp = (p != null) }
15 | };
16 |
17 | private static bool showHelp;
18 |
19 | private static int botIndex = -1;
20 | private static BotManager manager;
21 | private static bool isclosing = false;
22 |
23 | [STAThread]
24 | public static void Main(string[] args)
25 | {
26 | opts.Parse(args);
27 |
28 | if (showHelp)
29 | {
30 | Console.ForegroundColor = ConsoleColor.White;
31 | Console.WriteLine("If no options are given SteamBot defaults to Bot Manager mode.");
32 | opts.WriteOptionDescriptions(Console.Out);
33 | Console.Write("Press Enter to exit...");
34 | Console.ReadLine();
35 | return;
36 | }
37 |
38 | if (args.Length == 0)
39 | {
40 | BotManagerMode();
41 | }
42 | else if (botIndex > -1)
43 | {
44 | BotMode(botIndex);
45 | }
46 | }
47 |
48 | #region SteamBot Operational Modes
49 |
50 | // This mode is to run a single Bot until it's terminated.
51 | private static void BotMode(int botIndex)
52 | {
53 | if (!File.Exists("settings.json"))
54 | {
55 | Console.WriteLine("No settings.json file found.");
56 | return;
57 | }
58 |
59 | Configuration configObject;
60 | try
61 | {
62 | configObject = Configuration.LoadConfiguration("settings.json");
63 | }
64 | catch (Newtonsoft.Json.JsonReaderException)
65 | {
66 | // handle basic json formatting screwups
67 | Console.WriteLine("settings.json file is corrupt or improperly formatted.");
68 | return;
69 | }
70 |
71 | if (botIndex >= configObject.Bots.Length)
72 | {
73 | Console.WriteLine("Invalid bot index.");
74 | return;
75 | }
76 |
77 | Bot b = new Bot(configObject.Bots[botIndex], configObject.ApiKey, BotManager.UserHandlerCreator, true, true);
78 | Console.Title = "Bot Manager";
79 | b.StartBot();
80 |
81 | string AuthSet = "auth";
82 | string ExecCommand = "exec";
83 | string InputCommand = "input";
84 |
85 | // this loop is needed to keep the botmode console alive.
86 | // instead of just sleeping, this loop will handle console input
87 | while (true)
88 | {
89 | string inputText = Console.ReadLine();
90 |
91 | if (String.IsNullOrEmpty(inputText))
92 | continue;
93 |
94 | // Small parse for console input
95 | var c = inputText.Trim();
96 |
97 | var cs = c.Split(' ');
98 |
99 | if (cs.Length > 1)
100 | {
101 | if (cs[0].Equals(AuthSet, StringComparison.CurrentCultureIgnoreCase))
102 | {
103 | b.AuthCode = cs[1].Trim();
104 | }
105 | else if (cs[0].Equals(ExecCommand, StringComparison.CurrentCultureIgnoreCase))
106 | {
107 | b.HandleBotCommand(c.Remove(0, cs[0].Length + 1));
108 | }
109 | else if (cs[0].Equals(InputCommand, StringComparison.CurrentCultureIgnoreCase))
110 | {
111 | b.HandleInput(c.Remove(0, cs[0].Length + 1));
112 | }
113 | }
114 | }
115 | }
116 |
117 | // This mode is to manage child bot processes and take use command line inputs
118 | private static void BotManagerMode()
119 | {
120 | Console.Title = "Bot Manager";
121 |
122 | manager = new BotManager();
123 |
124 | var loadedOk = manager.LoadConfiguration("settings.json");
125 |
126 | if (!loadedOk)
127 | {
128 | Console.WriteLine(
129 | "Configuration file Does not exist or is corrupt. Please rename 'settings-template.json' to 'settings.json' and modify the settings to match your environment");
130 | Console.Write("Press Enter to exit...");
131 | Console.ReadLine();
132 | }
133 | else
134 | {
135 | if (manager.ConfigObject.UseSeparateProcesses)
136 | SetConsoleCtrlHandler(ConsoleCtrlCheck, true);
137 |
138 | if (manager.ConfigObject.AutoStartAllBots)
139 | {
140 | var startedOk = manager.StartBots();
141 |
142 | if (!startedOk)
143 | {
144 | Console.WriteLine(
145 | "Error starting the bots because either the configuration was bad or because the log file was not opened.");
146 | Console.Write("Press Enter to exit...");
147 | Console.ReadLine();
148 | }
149 | }
150 | else
151 | {
152 | foreach (var botInfo in manager.ConfigObject.Bots)
153 | {
154 | if (botInfo.AutoStart)
155 | {
156 | // auto start this particual bot...
157 | manager.StartBot(botInfo.Username);
158 | }
159 | }
160 | }
161 |
162 | Console.WriteLine("Type help for bot manager commands. ");
163 | Console.Write("botmgr > ");
164 |
165 | var bmi = new BotManagerInterpreter(manager);
166 |
167 | // command interpreter loop.
168 | do
169 | {
170 | Console.Write("botmgr > ");
171 | string inputText = Console.ReadLine();
172 |
173 | if (!String.IsNullOrEmpty(inputText))
174 | bmi.CommandInterpreter(inputText);
175 |
176 | } while (!isclosing);
177 | }
178 | }
179 |
180 | #endregion Bot Modes
181 |
182 | private static bool ConsoleCtrlCheck(CtrlTypes ctrlType)
183 | {
184 | // Put your own handler here
185 | switch (ctrlType)
186 | {
187 | case CtrlTypes.CTRL_C_EVENT:
188 | case CtrlTypes.CTRL_BREAK_EVENT:
189 | case CtrlTypes.CTRL_CLOSE_EVENT:
190 | case CtrlTypes.CTRL_LOGOFF_EVENT:
191 | case CtrlTypes.CTRL_SHUTDOWN_EVENT:
192 | if (manager != null)
193 | {
194 | manager.StopBots();
195 | }
196 | isclosing = true;
197 | break;
198 | }
199 |
200 | return true;
201 | }
202 |
203 | #region Console Control Handler Imports
204 |
205 | // Declare the SetConsoleCtrlHandler function
206 | // as external and receiving a delegate.
207 | [DllImport("Kernel32")]
208 | public static extern bool SetConsoleCtrlHandler(HandlerRoutine Handler, bool Add);
209 |
210 | // A delegate type to be used as the handler routine
211 | // for SetConsoleCtrlHandler.
212 | public delegate bool HandlerRoutine(CtrlTypes CtrlType);
213 |
214 | // An enumerated type for the control messages
215 | // sent to the handler routine.
216 | public enum CtrlTypes
217 | {
218 | CTRL_C_EVENT = 0,
219 | CTRL_BREAK_EVENT,
220 | CTRL_CLOSE_EVENT,
221 | CTRL_LOGOFF_EVENT = 5,
222 | CTRL_SHUTDOWN_EVENT
223 | }
224 |
225 | #endregion
226 | }
227 | }
228 |
--------------------------------------------------------------------------------
/SteamBot/SimpleUserHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using SteamKit2;
3 | using System.Collections.Generic;
4 | using SteamTrade;
5 | using SteamTrade.TradeOffer;
6 | using SteamTrade.TradeWebAPI;
7 |
8 | namespace SteamBot
9 | {
10 | public class SimpleUserHandler : UserHandler
11 | {
12 | public TF2Value AmountAdded;
13 |
14 | public SimpleUserHandler (Bot bot, SteamID sid) : base(bot, sid) {}
15 |
16 | public override bool OnGroupAdd()
17 | {
18 | return false;
19 | }
20 |
21 | public override bool OnFriendAdd ()
22 | {
23 | return true;
24 | }
25 |
26 | public override void OnLoginCompleted()
27 | {
28 | }
29 |
30 | public override void OnChatRoomMessage(SteamID chatID, SteamID sender, string message)
31 | {
32 | Log.Info(Bot.SteamFriends.GetFriendPersonaName(sender) + ": " + message);
33 | base.OnChatRoomMessage(chatID, sender, message);
34 | }
35 |
36 | public override void OnFriendRemove () {}
37 |
38 | public override void OnMessage (string message, EChatEntryType type)
39 | {
40 | SendChatMessage(Bot.ChatResponse);
41 | }
42 |
43 | public override bool OnTradeRequest()
44 | {
45 | return true;
46 | }
47 |
48 | public override void OnTradeError (string error)
49 | {
50 | SendChatMessage("Oh, there was an error: {0}.", error);
51 | Log.Warn (error);
52 | }
53 |
54 | public override void OnTradeTimeout ()
55 | {
56 | SendChatMessage("Sorry, but you were AFK and the trade was canceled.");
57 | Log.Info ("User was kicked because he was AFK.");
58 | }
59 |
60 | public override void OnTradeInit()
61 | {
62 | SendTradeMessage("Success. Please put up your items.");
63 | }
64 |
65 | public override void OnTradeAddItem (Schema.Item schemaItem, Inventory.Item inventoryItem) {}
66 |
67 | public override void OnTradeRemoveItem (Schema.Item schemaItem, Inventory.Item inventoryItem) {}
68 |
69 | public override void OnTradeMessage (string message) {}
70 |
71 | public override void OnTradeReady (bool ready)
72 | {
73 | if (!ready)
74 | {
75 | Trade.SetReady (false);
76 | }
77 | else
78 | {
79 | if(Validate ())
80 | {
81 | Trade.SetReady (true);
82 | }
83 | SendTradeMessage("Scrap: {0}", AmountAdded.ScrapTotal);
84 | }
85 | }
86 |
87 | public override void OnTradeAwaitingConfirmation(long tradeOfferID)
88 | {
89 | Log.Warn("Trade ended awaiting confirmation");
90 | SendChatMessage("Please complete the confirmation to finish the trade");
91 | }
92 |
93 | public override void OnTradeOfferUpdated(TradeOffer offer)
94 | {
95 | switch (offer.OfferState)
96 | {
97 | case TradeOfferState.TradeOfferStateAccepted:
98 | Log.Info(String.Format("Trade offer {0} has been completed!", offer.TradeOfferId));
99 | SendChatMessage("Trade completed, thank you!");
100 | break;
101 | case TradeOfferState.TradeOfferStateActive:
102 | case TradeOfferState.TradeOfferStateNeedsConfirmation:
103 | case TradeOfferState.TradeOfferStateInEscrow:
104 | //Trade is still active but incomplete
105 | break;
106 | case TradeOfferState.TradeOfferStateCountered:
107 | Log.Info(String.Format("Trade offer {0} was countered", offer.TradeOfferId));
108 | break;
109 | default:
110 | Log.Info(String.Format("Trade offer {0} failed", offer.TradeOfferId));
111 | break;
112 | }
113 | }
114 |
115 | public override void OnTradeAccept()
116 | {
117 | if (Validate() || IsAdmin)
118 | {
119 | //Even if it is successful, AcceptTrade can fail on
120 | //trades with a lot of items so we use a try-catch
121 | try {
122 | if (Trade.AcceptTrade())
123 | Log.Success("Trade Accepted!");
124 | }
125 | catch {
126 | Log.Warn ("The trade might have failed, but we can't be sure.");
127 | }
128 | }
129 | }
130 |
131 | public bool Validate ()
132 | {
133 | AmountAdded = TF2Value.Zero;
134 |
135 | List errors = new List ();
136 |
137 | foreach (TradeUserAssets asset in Trade.OtherOfferedItems)
138 | {
139 | var item = Trade.OtherInventory.GetItem(asset.assetid);
140 | if (item.Defindex == 5000)
141 | AmountAdded += TF2Value.Scrap;
142 | else if (item.Defindex == 5001)
143 | AmountAdded += TF2Value.Reclaimed;
144 | else if (item.Defindex == 5002)
145 | AmountAdded += TF2Value.Refined;
146 | else
147 | {
148 | var schemaItem = Trade.CurrentSchema.GetItem (item.Defindex);
149 | errors.Add ("Item " + schemaItem.Name + " is not a metal.");
150 | }
151 | }
152 |
153 | if (AmountAdded == TF2Value.Zero)
154 | {
155 | errors.Add ("You must put up at least 1 scrap.");
156 | }
157 |
158 | // send the errors
159 | if (errors.Count != 0)
160 | SendTradeMessage("There were errors in your trade: ");
161 | foreach (string error in errors)
162 | {
163 | SendTradeMessage(error);
164 | }
165 |
166 | return errors.Count == 0;
167 | }
168 |
169 | }
170 |
171 | }
172 |
173 |
--------------------------------------------------------------------------------
/SteamBot/SteamGroups/CMsgGroupInviteAction.cs:
--------------------------------------------------------------------------------
1 | ///SteamKit2.ClanMessages
2 | ///Created by Jacob Douglas (gamemaster1494)
3 | ///Created on 4/17/2013 at 2:19 AM CST
4 | ///(c) Copyright 2013, Nebula Programs
5 |
6 | /// Purpose: This class contains the messages used to Invite friends to clans
7 | /// As well as Decline or Accept clan invites.
8 |
9 | using System.IO;
10 | using SteamKit2;
11 | using SteamKit2.Internal;
12 |
13 | namespace SteamBot.SteamGroups
14 | {
15 | //CMsgInviteUserToClan
16 |
17 | ///
18 | /// Message used to Accept or Decline a group(clan) invite.
19 | ///
20 | public class CMsgGroupInviteAction : ISteamSerializableMessage, ISteamSerializable
21 | {
22 | EMsg ISteamSerializableMessage.GetEMsg()
23 | {
24 | return EMsg.ClientAcknowledgeClanInvite;
25 | }
26 |
27 | public CMsgGroupInviteAction()
28 | {
29 |
30 | }
31 |
32 | ///
33 | /// Group invited to.
34 | ///
35 | public ulong GroupID = 0;
36 |
37 | ///
38 | /// To accept or decline the invite.
39 | ///
40 | public bool AcceptInvite = true;
41 |
42 | void ISteamSerializable.Serialize(Stream stream)
43 | {
44 | try
45 | {
46 | BinaryWriter bw = new BinaryWriter(stream);
47 | bw.Write(GroupID);
48 | bw.Write(AcceptInvite);
49 | }//try
50 | catch
51 | {
52 | throw new IOException();
53 | }//catch
54 | }//Serialize()
55 |
56 | void ISteamSerializable.Deserialize(Stream stream)
57 | {
58 | try
59 | {
60 | BinaryReader br = new BinaryReader(stream);
61 | GroupID = br.ReadUInt64();
62 | AcceptInvite = br.ReadBoolean();
63 | }//try
64 | catch
65 | {
66 | throw new IOException();
67 | }//catch
68 | }//Deserialize()
69 | }//CMsgClanInviteAction
70 |
71 | }
72 |
--------------------------------------------------------------------------------
/SteamBot/SteamGroups/CMsgInviteUserToGroup.cs:
--------------------------------------------------------------------------------
1 | ///SteamKit2.ClanMessages
2 | ///Created by Jacob Douglas (gamemaster1494)
3 | ///Created on 4/17/2013 at 2:19 AM CST
4 | ///(c) Copyright 2013, Nebula Programs
5 |
6 | /// Purpose: This class contains the messages used to Invite friends to clans
7 | /// As well as Decline or Accept clan invites.
8 |
9 | using System.IO;
10 | using SteamKit2;
11 | using SteamKit2.Internal;
12 |
13 | namespace SteamBot.SteamGroups
14 | {
15 | ///
16 | /// Message used to invite a user to a group(clan).
17 | ///
18 | public class CMsgInviteUserToGroup : ISteamSerializableMessage, ISteamSerializable
19 | {
20 | EMsg ISteamSerializableMessage.GetEMsg()
21 | {
22 | return EMsg.ClientInviteUserToClan;
23 | }
24 |
25 | public CMsgInviteUserToGroup()
26 | {
27 |
28 | }
29 |
30 | ///
31 | /// Who is being invited.
32 | ///
33 | public ulong Invitee = 0;
34 |
35 | ///
36 | /// Group to invite to
37 | ///
38 | public ulong GroupID = 0;
39 |
40 | ///
41 | /// Not known yet. All data seen shows this as being true.
42 | /// See what happens if its false?
43 | ///
44 | public bool UnknownInfo = true;
45 |
46 | void ISteamSerializable.Serialize(Stream stream)
47 | {
48 | try
49 | {
50 | BinaryWriter bw = new BinaryWriter(stream);
51 | bw.Write(Invitee);
52 | bw.Write(GroupID);
53 | bw.Write(UnknownInfo);
54 | }//try
55 | catch
56 | {
57 | throw new IOException();
58 | }//catch
59 | }//Serialize()
60 |
61 | void ISteamSerializable.Deserialize(Stream stream)
62 | {
63 | try
64 | {
65 | BinaryReader br = new BinaryReader(stream);
66 | Invitee = br.ReadUInt64();
67 | GroupID = br.ReadUInt64();
68 | UnknownInfo = br.ReadBoolean();
69 | }//try
70 | catch
71 | {
72 | throw new IOException();
73 | }//catch
74 | }//Deserialize()
75 | }
76 | }
--------------------------------------------------------------------------------
/SteamBot/SteamGuardRequiredEventArgs.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace SteamBot
4 | {
5 | public class SteamGuardRequiredEventArgs : EventArgs
6 | {
7 | ///
8 | /// Set this to return the Steam Guard code to the bot.
9 | ///
10 | public string SteamGuard { get; set; }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/SteamBot/SteamTradeDemoHandler.cs:
--------------------------------------------------------------------------------
1 | using SteamKit2;
2 | using System.Collections.Generic;
3 | using SteamTrade;
4 | using SteamTrade.TradeOffer;
5 |
6 | namespace SteamBot
7 | {
8 | public class SteamTradeDemoHandler : UserHandler
9 | {
10 | // NEW ------------------------------------------------------------------
11 | private readonly GenericInventory mySteamInventory;
12 | private readonly GenericInventory OtherSteamInventory;
13 |
14 | private bool tested;
15 | // ----------------------------------------------------------------------
16 |
17 | public SteamTradeDemoHandler(Bot bot, SteamID sid) : base(bot, sid)
18 | {
19 | mySteamInventory = new GenericInventory(SteamWeb);
20 | OtherSteamInventory = new GenericInventory(SteamWeb);
21 | }
22 |
23 | public override bool OnGroupAdd()
24 | {
25 | return false;
26 | }
27 |
28 | public override bool OnFriendAdd ()
29 | {
30 | return true;
31 | }
32 |
33 | public override void OnLoginCompleted() {}
34 |
35 | public override void OnChatRoomMessage(SteamID chatID, SteamID sender, string message)
36 | {
37 | Log.Info(Bot.SteamFriends.GetFriendPersonaName(sender) + ": " + message);
38 | base.OnChatRoomMessage(chatID, sender, message);
39 | }
40 |
41 | public override void OnFriendRemove () {}
42 |
43 | public override void OnMessage (string message, EChatEntryType type)
44 | {
45 | SendChatMessage(Bot.ChatResponse);
46 | }
47 |
48 | public override bool OnTradeRequest()
49 | {
50 | return true;
51 | }
52 |
53 | public override void OnTradeError (string error)
54 | {
55 | SendChatMessage("Oh, there was an error: {0}.", error);
56 | Log.Warn (error);
57 | }
58 |
59 | public override void OnTradeTimeout ()
60 | {
61 | SendChatMessage("Sorry, but you were AFK and the trade was canceled.");
62 | Log.Info ("User was kicked because he was AFK.");
63 | }
64 |
65 | public override void OnTradeInit()
66 | {
67 | // NEW -------------------------------------------------------------------------------
68 | List contextId = new List();
69 | tested = false;
70 |
71 | /*************************************************************************************
72 | *
73 | * SteamInventory AppId = 753
74 | *
75 | * Context Id Description
76 | * 1 Gifts (Games), must be public on steam profile in order to work.
77 | * 6 Trading Cards, Emoticons & Backgrounds.
78 | *
79 | ************************************************************************************/
80 |
81 | contextId.Add(1);
82 | contextId.Add(6);
83 |
84 | mySteamInventory.load(753, contextId, Bot.SteamClient.SteamID);
85 | OtherSteamInventory.load(753, contextId, OtherSID);
86 |
87 | if (!mySteamInventory.isLoaded | !OtherSteamInventory.isLoaded)
88 | {
89 | SendTradeMessage("Couldn't open an inventory, type 'errors' for more info.");
90 | }
91 |
92 | SendTradeMessage("Type 'test' to start.");
93 | // -----------------------------------------------------------------------------------
94 | }
95 |
96 | public override void OnTradeAddItem (Schema.Item schemaItem, Inventory.Item inventoryItem) {
97 | // USELESS DEBUG MESSAGES -------------------------------------------------------------------------------
98 | SendTradeMessage("Object AppID: {0}", inventoryItem.AppId);
99 | SendTradeMessage("Object ContextId: {0}", inventoryItem.ContextId);
100 |
101 | switch (inventoryItem.AppId)
102 | {
103 | case 440:
104 | SendTradeMessage("TF2 Item Added.");
105 | SendTradeMessage("Name: {0}", schemaItem.Name);
106 | SendTradeMessage("Quality: {0}", inventoryItem.Quality);
107 | SendTradeMessage("Level: {0}", inventoryItem.Level);
108 | SendTradeMessage("Craftable: {0}", (inventoryItem.IsNotCraftable ? "No" : "Yes"));
109 | break;
110 |
111 | case 753:
112 | GenericInventory.ItemDescription tmpDescription = OtherSteamInventory.getDescription(inventoryItem.Id);
113 | SendTradeMessage("Steam Inventory Item Added.");
114 | SendTradeMessage("Type: {0}", tmpDescription.type);
115 | SendTradeMessage("Marketable: {0}", (tmpDescription.marketable ? "Yes" : "No"));
116 | break;
117 |
118 | default:
119 | SendTradeMessage("Unknown item");
120 | break;
121 | }
122 | // ------------------------------------------------------------------------------------------------------
123 | }
124 |
125 | public override void OnTradeRemoveItem (Schema.Item schemaItem, Inventory.Item inventoryItem) {}
126 |
127 | public override void OnTradeMessage (string message) {
128 | switch (message.ToLower())
129 | {
130 | case "errors":
131 | if (OtherSteamInventory.errors.Count > 0)
132 | {
133 | SendTradeMessage("User Errors:");
134 | foreach (string error in OtherSteamInventory.errors)
135 | {
136 | SendTradeMessage(" * {0}", error);
137 | }
138 | }
139 |
140 | if (mySteamInventory.errors.Count > 0)
141 | {
142 | SendTradeMessage("Bot Errors:");
143 | foreach (string error in mySteamInventory.errors)
144 | {
145 | SendTradeMessage(" * {0}", error);
146 | }
147 | }
148 | break;
149 |
150 | case "test":
151 | if (tested)
152 | {
153 | foreach (GenericInventory.Item item in mySteamInventory.items.Values)
154 | {
155 | Trade.RemoveItem(item);
156 | }
157 | }
158 | else
159 | {
160 | SendTradeMessage("Items on my bp: {0}", mySteamInventory.items.Count);
161 | foreach (GenericInventory.Item item in mySteamInventory.items.Values)
162 | {
163 | Trade.AddItem(item);
164 | }
165 | }
166 |
167 | tested = !tested;
168 |
169 | break;
170 |
171 | case "remove":
172 | foreach (var item in mySteamInventory.items)
173 | {
174 | Trade.RemoveItem(item.Value.assetid, item.Value.appid, item.Value.contextid);
175 | }
176 | break;
177 | }
178 | }
179 |
180 | public override void OnTradeReady (bool ready)
181 | {
182 | //Because SetReady must use its own version, it's important
183 | //we poll the trade to make sure everything is up-to-date.
184 | Trade.Poll();
185 | if (!ready)
186 | {
187 | Trade.SetReady (false);
188 | }
189 | else
190 | {
191 | if(Validate () | IsAdmin)
192 | {
193 | Trade.SetReady (true);
194 | }
195 | }
196 | }
197 |
198 | public override void OnTradeOfferUpdated(TradeOffer offer)
199 | {
200 | if(offer.OfferState == TradeOfferState.TradeOfferStateAccepted)
201 | {
202 | Log.Success("Trade Complete.");
203 | }
204 | }
205 |
206 | public override void OnTradeAwaitingConfirmation(long tradeOfferID)
207 | {
208 | Log.Warn("Trade ended awaiting confirmation");
209 | SendChatMessage("Please complete the confirmation to finish the trade");
210 | }
211 |
212 | public override void OnTradeAccept()
213 | {
214 | if (Validate() | IsAdmin)
215 | {
216 | //Even if it is successful, AcceptTrade can fail on
217 | //trades with a lot of items so we use a try-catch
218 | try {
219 | Trade.AcceptTrade();
220 | }
221 | catch {
222 | Log.Warn ("The trade might have failed, but we can't be sure.");
223 | }
224 |
225 | Log.Success ("Trade Complete!");
226 | }
227 | }
228 |
229 | public bool Validate ()
230 | {
231 | List errors = new List ();
232 | errors.Add("This demo is meant to show you how to handle SteamInventory Items. Trade cannot be completed, unless you're an Admin.");
233 |
234 | // send the errors
235 | if (errors.Count != 0)
236 | SendTradeMessage("There were errors in your trade: ");
237 |
238 | foreach (string error in errors)
239 | {
240 | SendTradeMessage(error);
241 | }
242 |
243 | return errors.Count == 0;
244 | }
245 |
246 | }
247 |
248 | }
249 |
250 |
--------------------------------------------------------------------------------
/SteamBot/TF2GC/Crafting.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using SteamKit2.GC;
6 |
7 | namespace SteamBot.TF2GC
8 | {
9 | public enum ECraftingRecipe : short
10 | {
11 | SmeltClassWeapons = 3,
12 | CombineScrap = 4,
13 | CombineReclaimed = 5,
14 | SmeltReclaimed = 22,
15 | SmeltRefined = 23
16 | }
17 |
18 | public static class Crafting
19 | {
20 | ///
21 | /// Crafts the specified items using the best fit recipe.
22 | ///
23 | /// The current Bot
24 | /// A list of ulong Item IDs to craft
25 | ///
26 | /// You must have set the current game to 440 for this to do anything.
27 | ///
28 | public static void CraftItems(Bot bot, params ulong[] items)
29 | {
30 | CraftItems(bot, -2, items);
31 | }
32 | ///
33 | /// Crafts the specified items using the specified recipe.
34 | ///
35 | /// The current Bot
36 | /// The recipe number (unknown atm; -2 is "best fit")
37 | /// A list of ulong Item IDs to craft
38 | ///
39 | /// You must have set the current game to 440 for this to do anything.
40 | ///
41 | public static void CraftItems(Bot bot, ECraftingRecipe recipe, params ulong[] items)
42 | {
43 | CraftItems(bot, (short)recipe, items);
44 | }
45 | public static void CraftItems(Bot bot, short recipe, params ulong[] items)
46 | {
47 | if (bot.CurrentGame != 440)
48 | throw new Exception("SteamBot is not ingame with AppID 440; current AppID is " + bot.CurrentGame);
49 |
50 | var craftMsg = new ClientGCMsg();
51 |
52 | craftMsg.Body.NumItems = (short)items.Length;
53 | craftMsg.Body.Recipe = recipe;
54 |
55 | foreach (ulong id in items)
56 | craftMsg.Write(id);
57 |
58 | bot.SteamGameCoordinator.Send(craftMsg, 440);
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/SteamBot/TF2GC/Items.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using SteamKit2.GC;
6 |
7 | namespace SteamBot.TF2GC
8 | {
9 | public static class Items
10 | {
11 | ///
12 | /// Permanently deletes the specified item
13 | ///
14 | /// The current Bot
15 | /// The 64-bit Item ID to delete
16 | ///
17 | /// You must have set the current game to 440 for this to do anything.
18 | ///
19 | public static void DeleteItem(Bot bot, ulong item)
20 | {
21 | if (bot.CurrentGame != 440)
22 | throw new Exception("SteamBot is not ingame with AppID 440; current AppID is " + bot.CurrentGame);
23 |
24 | var deleteMsg = new ClientGCMsg();
25 |
26 | deleteMsg.Write((ulong)item);
27 |
28 | bot.SteamGameCoordinator.Send(deleteMsg, 440);
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/SteamBot/TF2GC/MsgCraft.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | using SteamKit2;
7 | using SteamKit2.GC;
8 | using SteamKit2.GC.TF2;
9 | using SteamKit2.Internal;
10 |
11 | namespace SteamBot.TF2GC
12 | {
13 | public class MsgCraft : IGCSerializableMessage
14 | {
15 | public ulong[] IdsToSend;
16 | public short Recipe = -2;
17 | public short NumItems = 2;
18 |
19 | public MsgCraft()
20 | {
21 | }
22 |
23 | public bool IsProto
24 | {
25 | get { return false; }
26 | }
27 |
28 | //1002 is the EMsg code for crafting
29 | public uint MsgType
30 | {
31 | get { return 1002; }
32 | }
33 |
34 | public JobID TargetJobID
35 | {
36 | get { return new JobID(); }
37 | set { throw new NotImplementedException(); }
38 | }
39 |
40 | public JobID SourceJobID
41 | {
42 | get { return new JobID(); }
43 | set { throw new NotImplementedException(); }
44 | }
45 |
46 | public byte[] Serialize()
47 | {
48 | List ret = new List();
49 | ret.AddRange(BitConverter.GetBytes((short)Recipe));
50 | ret.AddRange(BitConverter.GetBytes((short)NumItems));
51 | return ret.ToArray();
52 | }
53 |
54 | public void Serialize(Stream stream)
55 | {
56 | byte[] buf = Serialize();
57 | stream.Write(buf, 0, buf.Length);
58 | }
59 |
60 | public void Deserialize(Stream stream)
61 | {
62 |
63 | }
64 |
65 |
66 | //1002 is the EMsg code for crafting
67 | public uint GetEMsg()
68 | {
69 | return 1002;
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/SteamBot/TF2GC/MsgDelete.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | using SteamKit2;
7 | using SteamKit2.GC;
8 | using SteamKit2.GC.TF2;
9 | using SteamKit2.Internal;
10 |
11 | namespace SteamBot.TF2GC
12 | {
13 | public class MsgDelete : IGCSerializableMessage
14 | {
15 |
16 | public MsgDelete()
17 | {
18 |
19 | }
20 |
21 | public bool IsProto
22 | {
23 | get { return false; }
24 | }
25 |
26 | public uint MsgType
27 | {
28 | get { return 1004; }
29 | }
30 |
31 | public JobID TargetJobID
32 | {
33 | get { return new JobID(); }
34 | set { throw new NotImplementedException(); }
35 | }
36 |
37 | public JobID SourceJobID
38 | {
39 | get { return new JobID(); }
40 | set { throw new NotImplementedException(); }
41 | }
42 |
43 | public byte[] Serialize()
44 | {
45 | List ret = new List();
46 | return ret.ToArray();
47 | }
48 |
49 | public void Serialize(Stream stream)
50 | {
51 | byte[] buf = Serialize();
52 | stream.Write(buf, 0, buf.Length);
53 | }
54 |
55 | public void Deserialize(Stream stream)
56 | {
57 |
58 | }
59 |
60 | public uint GetEMsg()
61 | {
62 | //1004 is the EMsg for item deletion
63 | return 1004;
64 | }
65 | }
66 | }
--------------------------------------------------------------------------------
/SteamBot/TradeOfferUserHandler.cs:
--------------------------------------------------------------------------------
1 | using SteamKit2;
2 | using SteamTrade;
3 | using SteamTrade.TradeOffer;
4 | using System;
5 | using System.Collections.Generic;
6 | using TradeAsset = SteamTrade.TradeOffer.TradeOffer.TradeStatusUser.TradeAsset;
7 |
8 | namespace SteamBot
9 | {
10 | public class TradeOfferUserHandler : UserHandler
11 | {
12 | public TradeOfferUserHandler(Bot bot, SteamID sid) : base(bot, sid) { }
13 |
14 | public override void OnTradeOfferUpdated(TradeOffer offer)
15 | {
16 | if(offer.OfferState == TradeOfferState.TradeOfferStateActive && !offer.IsOurOffer)
17 | {
18 | OnNewTradeOffer(offer);
19 | }
20 | }
21 |
22 | private void OnNewTradeOffer(TradeOffer offer)
23 | {
24 | //receiving a trade offer
25 | if (IsAdmin)
26 | {
27 | //parse inventories of bot and other partner
28 | //either with webapi or generic inventory
29 | //Bot.GetInventory();
30 | //Bot.GetOtherInventory(OtherSID);
31 |
32 | var myItems = offer.Items.GetMyItems();
33 | var theirItems = offer.Items.GetTheirItems();
34 | Log.Info("They want " + myItems.Count + " of my items.");
35 | Log.Info("And I will get " + theirItems.Count + " of their items.");
36 |
37 | //do validation logic etc
38 | if (DummyValidation(myItems, theirItems))
39 | {
40 | TradeOfferAcceptResponse acceptResp = offer.Accept();
41 | if (acceptResp.Accepted)
42 | {
43 | Bot.AcceptAllMobileTradeConfirmations();
44 | Log.Success("Accepted trade offer successfully : Trade ID: " + acceptResp.TradeId);
45 | }
46 | }
47 | else
48 | {
49 | // maybe we want different items or something
50 |
51 | //offer.Items.AddMyItem(0, 0, 0);
52 | //offer.Items.RemoveTheirItem(0, 0, 0);
53 | if (offer.Items.NewVersion)
54 | {
55 | string newOfferId;
56 | if (offer.CounterOffer(out newOfferId))
57 | {
58 | Bot.AcceptAllMobileTradeConfirmations();
59 | Log.Success("Counter offered successfully : New Offer ID: " + newOfferId);
60 | }
61 | }
62 | }
63 | }
64 | else
65 | {
66 | //we don't know this user so we can decline
67 | if (offer.Decline())
68 | {
69 | Log.Info("Declined trade offer : " + offer.TradeOfferId + " from untrusted user " + OtherSID.ConvertToUInt64());
70 | }
71 | }
72 | }
73 |
74 | public override void OnMessage(string message, EChatEntryType type)
75 | {
76 | if (IsAdmin)
77 | {
78 | //creating a new trade offer
79 | var offer = Bot.NewTradeOffer(OtherSID);
80 |
81 | //offer.Items.AddMyItem(0, 0, 0);
82 | if (offer.Items.NewVersion)
83 | {
84 | string newOfferId;
85 | if (offer.Send(out newOfferId))
86 | {
87 | Bot.AcceptAllMobileTradeConfirmations();
88 | Log.Success("Trade offer sent : Offer ID " + newOfferId);
89 | }
90 | }
91 |
92 | //creating a new trade offer with token
93 | var offerWithToken = Bot.NewTradeOffer(OtherSID);
94 |
95 | //offer.Items.AddMyItem(0, 0, 0);
96 | if (offerWithToken.Items.NewVersion)
97 | {
98 | string newOfferId;
99 | // "token" should be replaced with the actual token from the other user
100 | if (offerWithToken.SendWithToken(out newOfferId, "token"))
101 | {
102 | Bot.AcceptAllMobileTradeConfirmations();
103 | Log.Success("Trade offer sent : Offer ID " + newOfferId);
104 | }
105 | }
106 | }
107 | }
108 |
109 | public override bool OnGroupAdd() { return false; }
110 |
111 | public override bool OnFriendAdd() { return IsAdmin; }
112 |
113 | public override void OnFriendRemove() { }
114 |
115 | public override void OnLoginCompleted() { }
116 |
117 | public override bool OnTradeRequest() { return false; }
118 |
119 | public override void OnTradeError(string error) { }
120 |
121 | public override void OnTradeTimeout() { }
122 |
123 | public override void OnTradeAwaitingConfirmation(long tradeOfferID) { }
124 |
125 | public override void OnTradeInit() { }
126 |
127 | public override void OnTradeAddItem(Schema.Item schemaItem, Inventory.Item inventoryItem) { }
128 |
129 | public override void OnTradeRemoveItem(Schema.Item schemaItem, Inventory.Item inventoryItem) { }
130 |
131 | public override void OnTradeMessage(string message) { }
132 |
133 | public override void OnTradeReady(bool ready) { }
134 |
135 | public override void OnTradeAccept() { }
136 |
137 | private bool DummyValidation(List myAssets, List theirAssets)
138 | {
139 | //compare items etc
140 | if (myAssets.Count == theirAssets.Count)
141 | {
142 | return true;
143 | }
144 | return false;
145 | }
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/SteamBot/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/SteamBot/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/SteamBotUnitTest/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("SteamBotUnitTest")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("SteamBotUnitTest")]
13 | [assembly: AssemblyCopyright("Copyright © 2015")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("54039ab1-e82a-4d9f-93f2-1a96e42ec6de")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/SteamBotUnitTest/SteamTradeUnitTest.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {DD770440-43F0-4440-BD36-DD91A0EB6842}
8 | Library
9 | Properties
10 | SteamBotUnitTest
11 | SteamBotUnitTest
12 | v4.5.1
13 | 512
14 | ..\
15 | true
16 |
17 |
18 |
19 | true
20 | full
21 | false
22 | bin\Debug\
23 | DEBUG;TRACE
24 | prompt
25 | 4
26 |
27 |
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 |
35 |
36 |
37 | ..\packages\NUnit.2.6.4\lib\nunit.framework.dll
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | {6cec0333-81eb-40ee-85d1-941363626fc7}
55 | SteamTrade
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
66 |
67 |
68 |
69 |
76 |
--------------------------------------------------------------------------------
/SteamBotUnitTest/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/SteamTrade/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 |
4 | // Information about this assembly is defined by the following attributes.
5 | // Change them to the values specific to your project.
6 |
7 | [assembly: AssemblyTitle("SteamTrade")]
8 | [assembly: AssemblyDescription("")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("")]
11 | [assembly: AssemblyProduct("SteamTrade")]
12 | [assembly: AssemblyCopyright("C) 2012 SteamBot Contributors")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
17 | // The form "{Major}.{Minor}.*" will automatically update the build and revision,
18 | // and "{Major}.{Minor}.{Build}.*" will update just the revision.
19 |
20 | // SteamTrade uses Semantic Versioning (http://semver.org/)
21 | [assembly: AssemblyVersion("0.1.0.*")]
22 |
23 | // The following attributes are used to specify the signing key for the assembly,
24 | // if desired. See the Mono documentation for more information about signing.
25 |
26 | //[assembly: AssemblyDelaySign(false)]
27 | //[assembly: AssemblyKeyFile("")]
28 |
29 |
--------------------------------------------------------------------------------
/SteamTrade/Exceptions/InventoryFetchException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using SteamKit2;
3 |
4 | namespace SteamTrade.Exceptions
5 | {
6 | public class InventoryFetchException : TradeException
7 | {
8 | public InventoryFetchException ()
9 | {
10 | }
11 |
12 | ///
13 | /// Gets the Steam identifier that caused the fetch exception.
14 | ///
15 | ///
16 | /// The failing steam identifier.
17 | ///
18 | public SteamID FailingSteamId { get; private set; }
19 |
20 | ///
21 | /// Initializes a new instance of the class.
22 | ///
23 | ///
24 | /// Steam identifier that caused the fetch exception.
25 | ///
26 | public InventoryFetchException (SteamID steamId)
27 | : base(String.Format("Failed to fetch inventory for: {0}", steamId.ToString()))
28 | {
29 | FailingSteamId = steamId;
30 | }
31 | }
32 | }
33 |
34 |
--------------------------------------------------------------------------------
/SteamTrade/Exceptions/TradeException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace SteamTrade.Exceptions
7 | {
8 | ///
9 | /// A basic exception that occurs in the trading library.
10 | ///
11 | public class TradeException : Exception
12 | {
13 | public TradeException()
14 | {
15 | }
16 |
17 | public TradeException(string message)
18 | : base(message)
19 | {
20 | }
21 |
22 | public TradeException(string message, Exception inner)
23 | : base(message, inner)
24 | {
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/SteamTrade/ForeignInventory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 |
4 | namespace SteamTrade
5 | {
6 | ///
7 | /// This represents an inventory as decoded from the Steam Trade API
8 | /// function of the same name.
9 | ///
10 | ///
11 | /// This class takes the results of the following Trade API call:
12 | /// POST /trade/(steamid)/foreigninventory/sessionid=(trade_session_id)&steamid=(steamid)&appid=(appid)&contextid=(trade contextid)
13 | ///
14 | /// The trade context id is important and only obtainable from being in
15 | /// a trade.
16 | ///
17 | public class ForeignInventory
18 | {
19 | private readonly dynamic rawJson;
20 |
21 | ///
22 | /// Initializes a new instance of the class.
23 | ///
24 | ///
25 | /// The json returned from the foreigninventory Web API call.
26 | ///
27 | public ForeignInventory (dynamic rawJson)
28 | {
29 | this.rawJson = rawJson;
30 |
31 | if (rawJson.success == "true")
32 | {
33 | InventoryValid = true;
34 | }
35 | }
36 |
37 | ///
38 | /// Gets a value indicating whether the inventory is valid.
39 | ///
40 | ///
41 | /// true if the inventory is valid; otherwise, false.
42 | ///
43 | public bool InventoryValid
44 | {
45 | get; private set;
46 | }
47 |
48 | ///
49 | /// Gets the class id for the given item.
50 | ///
51 | /// The item id.
52 | /// A class ID or 0 if there is an error.
53 | public uint GetClassIdForItemId(ulong itemId)
54 | {
55 | string i = itemId.ToString(CultureInfo.InvariantCulture);
56 |
57 | try
58 | {
59 | return rawJson.rgInventory[i].classid;
60 | }
61 | catch
62 | {
63 | return 0;
64 | }
65 | }
66 |
67 | ///
68 | /// Gets the instance id for given item.
69 | ///
70 | /// The item id.
71 | /// A instance ID or 0 if there is an error.
72 | public ulong GetInstanceIdForItemId(ulong itemId)
73 | {
74 | string i = itemId.ToString(CultureInfo.InvariantCulture);
75 |
76 | try
77 | {
78 | return rawJson.rgInventory[i].instanceid;
79 | }
80 | catch
81 | {
82 | return 0;
83 | }
84 | }
85 |
86 |
87 | ///
88 | /// Gets the defindex for a given Item ID.
89 | ///
90 | /// The item id.
91 | /// A defindex or -1 if there is an error.
92 | public int GetDefIndex(ulong itemId)
93 | {
94 | try
95 | {
96 | uint classId = GetClassIdForItemId(itemId);
97 | ulong iid = GetInstanceIdForItemId(itemId);
98 |
99 | if (classId == 0 || iid == 0)
100 | {
101 | return -1;
102 | }
103 |
104 | // for tf2 the def index is in the app_data section in the
105 | // descriptions object. this may not be the case for all
106 | // games and therefore this may be non-portable.
107 | string index = classId + "_" + iid;
108 | string defIndex = rawJson.rgDescriptions[index].app_data.def_index;
109 | return int.Parse(defIndex);
110 | }
111 | catch
112 | {
113 | return -1;
114 | }
115 | }
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/SteamTrade/GenericInventory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Collections.Specialized;
4 | using System.Linq;
5 | using System.Threading.Tasks;
6 | using Newtonsoft.Json;
7 | using SteamKit2;
8 | using SteamTrade.TradeWebAPI;
9 |
10 | namespace SteamTrade
11 | {
12 |
13 | ///
14 | /// Generic Steam Backpack Interface
15 | ///
16 | public class GenericInventory
17 | {
18 | private readonly SteamWeb SteamWeb;
19 |
20 | public GenericInventory(SteamWeb steamWeb)
21 | {
22 | SteamWeb = steamWeb;
23 | }
24 |
25 | public Dictionary items
26 | {
27 | get
28 | {
29 | if (_loadTask == null)
30 | return null;
31 | _loadTask.Wait();
32 | return _items;
33 | }
34 | }
35 |
36 | public Dictionary descriptions
37 | {
38 | get
39 | {
40 | if (_loadTask == null)
41 | return null;
42 | _loadTask.Wait();
43 | return _descriptions;
44 | }
45 | }
46 |
47 | public List errors
48 | {
49 | get
50 | {
51 | if (_loadTask == null)
52 | return null;
53 | _loadTask.Wait();
54 | return _errors;
55 | }
56 | }
57 |
58 | public bool isLoaded = false;
59 |
60 | private Task _loadTask;
61 | private Dictionary _descriptions = new Dictionary();
62 | private Dictionary _items = new Dictionary();
63 | private List _errors = new List();
64 |
65 | public class Item : TradeUserAssets
66 | {
67 | public Item(int appid, long contextid, ulong assetid, string descriptionid, int amount = 1) : base(appid, contextid, assetid, amount)
68 | {
69 | this.descriptionid = descriptionid;
70 | }
71 |
72 | public string descriptionid { get; private set; }
73 |
74 | public override string ToString()
75 | {
76 | return string.Format("id:{0}, appid:{1}, contextid:{2}, amount:{3}, descriptionid:{4}",
77 | assetid, appid, contextid, amount, descriptionid);
78 | }
79 | }
80 |
81 | public class ItemDescription
82 | {
83 | public string name { get; set; }
84 | public string type { get; set; }
85 | public bool tradable { get; set; }
86 | public bool marketable { get; set; }
87 | public string url { get; set; }
88 | public long classid { get; set; }
89 | public long market_fee_app_id { get; set; }
90 |
91 | public Dictionary app_data { get; set; }
92 |
93 | public void debug_app_data()
94 | {
95 | Console.WriteLine("\n\"" + name + "\"");
96 | if (app_data == null)
97 | {
98 | Console.WriteLine("Doesn't have app_data");
99 | return;
100 | }
101 |
102 | foreach (var value in app_data)
103 | {
104 | Console.WriteLine(string.Format("{0} = {1}", value.Key, value.Value));
105 | }
106 | Console.WriteLine("");
107 | }
108 | }
109 |
110 | ///
111 | /// Returns information (such as item name, etc) about the given item.
112 | /// This call can fail, usually when the user's inventory is private.
113 | ///
114 | public ItemDescription getDescription(ulong id)
115 | {
116 | if (_loadTask == null)
117 | return null;
118 | _loadTask.Wait();
119 |
120 | try
121 | {
122 | return _descriptions[_items[id].descriptionid];
123 | }
124 | catch
125 | {
126 | return null;
127 | }
128 | }
129 |
130 | public void load(int appid, IEnumerable contextIds, SteamID steamid)
131 | {
132 | List contextIdsCopy = contextIds.ToList();
133 | _loadTask = Task.Factory.StartNew(() => loadImplementation(appid, contextIdsCopy, steamid));
134 | }
135 |
136 | public void loadImplementation(int appid, IEnumerable contextIds, SteamID steamid)
137 | {
138 | dynamic invResponse;
139 | isLoaded = false;
140 | Dictionary tmpAppData;
141 |
142 | _items.Clear();
143 | _descriptions.Clear();
144 | _errors.Clear();
145 |
146 | try
147 | {
148 | foreach (long contextId in contextIds)
149 | {
150 | string moreStart = null;
151 | do
152 | {
153 | var data = String.IsNullOrEmpty(moreStart) ? null : new NameValueCollection {{"start", moreStart}};
154 | string response = SteamWeb.Fetch(
155 | String.Format("http://steamcommunity.com/profiles/{0}/inventory/json/{1}/{2}/", steamid.ConvertToUInt64(), appid, contextId),
156 | "GET", data);
157 | invResponse = JsonConvert.DeserializeObject(response);
158 |
159 | if (invResponse.success == false)
160 | {
161 | _errors.Add("Fail to open backpack: " + invResponse.Error);
162 | continue;
163 | }
164 |
165 | //rgInventory = Items on Steam Inventory
166 | foreach (var item in invResponse.rgInventory)
167 | {
168 | foreach (var itemId in item)
169 | {
170 | ulong id = (ulong) itemId.id;
171 | if (!_items.ContainsKey(id))
172 | {
173 | string descriptionid = itemId.classid + "_" + itemId.instanceid;
174 | _items.Add((ulong)itemId.id, new Item(appid, contextId, (ulong)itemId.id, descriptionid));
175 | break;
176 | }
177 | }
178 | }
179 |
180 | // rgDescriptions = Item Schema (sort of)
181 | foreach (var description in invResponse.rgDescriptions)
182 | {
183 | foreach (var class_instance in description) // classid + '_' + instenceid
184 | {
185 | string key = "" + (class_instance.classid ?? '0') + "_" + (class_instance.instanceid ?? '0');
186 | if (!_descriptions.ContainsKey(key))
187 | {
188 | if(class_instance.app_data != null)
189 | {
190 | tmpAppData = new Dictionary();
191 | foreach(var value in class_instance.app_data)
192 | {
193 | tmpAppData.Add("" + value.Name, "" + value.Value);
194 | }
195 | }
196 | else
197 | {
198 | tmpAppData = null;
199 | }
200 |
201 | _descriptions.Add(key,
202 | new ItemDescription()
203 | {
204 | name = class_instance.name,
205 | type = class_instance.type,
206 | marketable = (bool)class_instance.marketable,
207 | tradable = (bool)class_instance.tradable,
208 | classid = long.Parse((string)class_instance.classid),
209 | url = (class_instance.actions != null && class_instance.actions.First["link"] != null ? class_instance.actions.First["link"] : ""),
210 | app_data = tmpAppData,
211 | market_fee_app_id = (class_instance.market_fee_app != null ? class_instance.market_fee_app : 0),
212 | }
213 | );
214 | break;
215 | }
216 |
217 | }
218 | }
219 |
220 | try
221 | {
222 | moreStart = invResponse.more_start;
223 | }
224 | catch (Exception e)
225 | {
226 | moreStart = null;
227 | }
228 | } while (!String.IsNullOrEmpty(moreStart) && moreStart.ToLower() != "false");
229 | }//end for (contextId)
230 | }//end try
231 | catch (Exception e)
232 | {
233 | Console.WriteLine(e);
234 | _errors.Add("Exception: " + e.Message);
235 | }
236 | isLoaded = true;
237 | }
238 | }
239 | }
240 |
--------------------------------------------------------------------------------
/SteamTrade/Inventory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Newtonsoft.Json;
5 | using SteamKit2;
6 |
7 | namespace SteamTrade
8 | {
9 | public class Inventory
10 | {
11 | ///
12 | /// Fetches the inventory for the given Steam ID using the Steam API.
13 | ///
14 | /// The give users inventory.
15 | /// Steam identifier.
16 | /// The needed Steam API key.
17 | /// The SteamWeb instance for this Bot
18 | public static Inventory FetchInventory(ulong steamId, string apiKey, SteamWeb steamWeb)
19 | {
20 | int attempts = 1;
21 | InventoryResponse result = null;
22 | while ((result == null || result.result.items == null) && attempts <= 3)
23 | {
24 | var url = "http://api.steampowered.com/IEconItems_440/GetPlayerItems/v0001/?key=" + apiKey + "&steamid=" + steamId;
25 | string response = steamWeb.Fetch(url, "GET", null, false);
26 | result = JsonConvert.DeserializeObject(response);
27 | attempts++;
28 | }
29 | return new Inventory(result.result);
30 | }
31 |
32 | ///
33 | /// Gets the inventory for the given Steam ID using the Steam Community website.
34 | ///
35 | /// The inventory for the given user.
36 | /// The Steam identifier.
37 | /// The SteamWeb instance for this Bot
38 | public static dynamic GetInventory(SteamID steamid, SteamWeb steamWeb)
39 | {
40 | string url = String.Format (
41 | "http://steamcommunity.com/profiles/{0}/inventory/json/440/2/?trading=1",
42 | steamid.ConvertToUInt64 ()
43 | );
44 |
45 | try
46 | {
47 | string response = steamWeb.Fetch (url, "GET");
48 | return JsonConvert.DeserializeObject (response);
49 | }
50 | catch (Exception)
51 | {
52 | return JsonConvert.DeserializeObject ("{\"success\":\"false\"}");
53 | }
54 | }
55 |
56 | public uint NumSlots { get; set; }
57 | public Item[] Items { get; set; }
58 | public bool IsPrivate { get; private set; }
59 | public bool IsGood { get; private set; }
60 |
61 | protected Inventory (InventoryResult apiInventory)
62 | {
63 | NumSlots = apiInventory.num_backpack_slots;
64 | Items = apiInventory.items;
65 | IsPrivate = (apiInventory.status == "15");
66 | IsGood = (apiInventory.status == "1");
67 | }
68 |
69 | ///
70 | /// Check to see if user is Free to play
71 | ///
72 | /// Yes or no
73 | public bool IsFreeToPlay()
74 | {
75 | return this.NumSlots % 100 == 50;
76 | }
77 |
78 | public Item GetItem (ulong id)
79 | {
80 | // Check for Private Inventory
81 | if( this.IsPrivate )
82 | throw new Exceptions.TradeException("Unable to access Inventory: Inventory is Private!");
83 |
84 | return (Items == null ? null : Items.FirstOrDefault(item => item.Id == id));
85 | }
86 |
87 | public List- GetItemsByDefindex (int defindex)
88 | {
89 | // Check for Private Inventory
90 | if( this.IsPrivate )
91 | throw new Exceptions.TradeException("Unable to access Inventory: Inventory is Private!");
92 |
93 | return Items.Where(item => item.Defindex == defindex).ToList();
94 | }
95 |
96 | public class Item
97 | {
98 | public int AppId = 440;
99 | public long ContextId = 2;
100 |
101 | [JsonProperty("id")]
102 | public ulong Id { get; set; }
103 |
104 | [JsonProperty("original_id")]
105 | public ulong OriginalId { get; set; }
106 |
107 | [JsonProperty("defindex")]
108 | public ushort Defindex { get; set; }
109 |
110 | [JsonProperty("level")]
111 | public byte Level { get; set; }
112 |
113 | [JsonProperty("quality")]
114 | public int Quality { get; set; }
115 |
116 | [JsonProperty("quantity")]
117 | public int RemainingUses { get; set; }
118 |
119 | [JsonProperty("origin")]
120 | public int Origin { get; set; }
121 |
122 | [JsonProperty("custom_name")]
123 | public string CustomName { get; set; }
124 |
125 | [JsonProperty("custom_desc")]
126 | public string CustomDescription { get; set; }
127 |
128 | [JsonProperty("flag_cannot_craft")]
129 | public bool IsNotCraftable { get; set; }
130 |
131 | [JsonProperty("flag_cannot_trade")]
132 | public bool IsNotTradeable { get; set; }
133 |
134 | [JsonProperty("attributes")]
135 | public ItemAttribute[] Attributes { get; set; }
136 |
137 | [JsonProperty("contained_item")]
138 | public Item ContainedItem { get; set; }
139 | }
140 |
141 | public class ItemAttribute
142 | {
143 | [JsonProperty("defindex")]
144 | public ushort Defindex { get; set; }
145 |
146 | [JsonProperty("value")]
147 | public string Value { get; set; }
148 |
149 | [JsonProperty("float_value")]
150 | public float FloatValue { get; set; }
151 |
152 | [JsonProperty("account_info")]
153 | public AccountInfo AccountInfo { get; set; }
154 | }
155 |
156 | public class AccountInfo
157 | {
158 | [JsonProperty("steamid")]
159 | public ulong SteamID { get; set; }
160 |
161 | [JsonProperty("personaname")]
162 | public string PersonaName { get; set; }
163 | }
164 |
165 | protected class InventoryResult
166 | {
167 | public string status { get; set; }
168 |
169 | public uint num_backpack_slots { get; set; }
170 |
171 | public Item[] items { get; set; }
172 | }
173 |
174 | protected class InventoryResponse
175 | {
176 | public InventoryResult result;
177 | }
178 | }
179 | }
180 |
181 |
--------------------------------------------------------------------------------
/SteamTrade/Schema.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Newtonsoft.Json;
5 | using System.Net;
6 | using System.IO;
7 | using System.Threading;
8 |
9 | namespace SteamTrade
10 | {
11 | ///
12 | /// This class represents the TF2 Item schema as deserialized from its
13 | /// JSON representation.
14 | ///
15 | public class Schema
16 | {
17 | private const string SchemaMutexName = "steam_bot_cache_file_mutex";
18 | private const string SchemaApiUrlBase = "https://api.steampowered.com/IEconItems_440/GetSchemaItems/v1/?key=";
19 | private const string SchemaApiItemOriginNamesUrlBase = "https://api.steampowered.com/IEconItems_440/GetSchemaOverview/v1/?key=";
20 |
21 | ///
22 | /// Full file name for schema cache file. This value is only used when calling . If the time modified of the local copy is later than that of the server, local copy is used without downloading. Default value is %TEMP%\tf_schema.cache.
23 | ///
24 | public static string CacheFileFullName = Path.GetTempPath() + "\\tf_schema.cache";
25 |
26 | ///
27 | /// Fetches the Tf2 Item schema.
28 | ///
29 | /// The API key.
30 | /// A deserialized instance of the Item Schema.
31 | ///
32 | /// The schema will be cached for future use if it is updated.
33 | ///
34 |
35 | public static Schema FetchSchema(string apiKey, string schemaLang = null)
36 | {
37 | var url = SchemaApiUrlBase + apiKey;
38 | if (schemaLang != null)
39 | url += "&format=json&language=" + schemaLang;
40 |
41 | // just let one thread/proc do the initial check/possible update.
42 | bool wasCreated;
43 | var mre = new EventWaitHandle(false,
44 | EventResetMode.ManualReset, SchemaMutexName, out wasCreated);
45 |
46 | // the thread that create the wait handle will be the one to
47 | // write the cache file. The others will wait patiently.
48 | if (!wasCreated)
49 | {
50 | bool signaled = mre.WaitOne(10000);
51 |
52 | if (!signaled)
53 | {
54 | return null;
55 | }
56 | }
57 |
58 | bool keepUpdating = true;
59 | SchemaResult schemaResult = new SchemaResult();
60 | string tmpUrl = url;
61 |
62 | do
63 | {
64 | if(schemaResult.result != null)
65 | tmpUrl = url + "&start=" + schemaResult.result.Next;
66 |
67 | string result = new SteamWeb().Fetch(tmpUrl, "GET");
68 |
69 | if (schemaResult.result == null || schemaResult.result.Items == null)
70 | {
71 | schemaResult = JsonConvert.DeserializeObject(result);
72 | }
73 | else
74 | {
75 | SchemaResult tempResult = JsonConvert.DeserializeObject(result);
76 | var items = schemaResult.result.Items.Concat(tempResult.result.Items);
77 | schemaResult.result.Items = items.ToArray();
78 | schemaResult.result.Next = tempResult.result.Next;
79 | }
80 |
81 | if (schemaResult.result.Next <= schemaResult.result.Items.Count())
82 | keepUpdating = false;
83 |
84 | } while (keepUpdating);
85 |
86 |
87 | //Get origin names
88 | string itemOriginUrl = SchemaApiItemOriginNamesUrlBase + apiKey;
89 |
90 | if (schemaLang != null)
91 | itemOriginUrl += "&format=json&language=" + schemaLang;
92 |
93 | string resp = new SteamWeb().Fetch(itemOriginUrl, "GET");
94 |
95 | var itemOriginResult = JsonConvert.DeserializeObject(resp);
96 |
97 | schemaResult.result.OriginNames = itemOriginResult.result.OriginNames;
98 |
99 | // were done here. let others read.
100 | mre.Set();
101 | DateTime schemaLastModified = DateTime.Now;
102 |
103 | return schemaResult.result ?? null;
104 |
105 | }
106 |
107 | // Gets the schema from the web or from the cached file.
108 | private static string GetSchemaString(HttpWebResponse response, DateTime schemaLastModified)
109 | {
110 | string result;
111 | bool mustUpdateCache = !File.Exists(CacheFileFullName) || schemaLastModified > File.GetCreationTime(CacheFileFullName);
112 |
113 | if (mustUpdateCache)
114 | {
115 | using(var reader = new StreamReader(response.GetResponseStream()))
116 | {
117 | result = reader.ReadToEnd();
118 |
119 | File.WriteAllText(CacheFileFullName, result);
120 | File.SetCreationTime(CacheFileFullName, schemaLastModified);
121 | }
122 | }
123 | else
124 | {
125 | // read previously cached file.
126 | using(TextReader reader = new StreamReader(CacheFileFullName))
127 | {
128 | result = reader.ReadToEnd();
129 | }
130 | }
131 |
132 | return result;
133 | }
134 |
135 |
136 | [JsonProperty("status")]
137 | public int Status { get; set; }
138 |
139 | [JsonProperty("items_game_url")]
140 | public string ItemsGameUrl { get; set; }
141 |
142 | [JsonProperty("items")]
143 | public Item[] Items { get; set; }
144 |
145 | [JsonProperty("originNames")]
146 | public ItemOrigin[] OriginNames { get; set; }
147 |
148 | [JsonProperty("next")]
149 | public int Next { get; set; }
150 |
151 | ///
152 | /// Find an SchemaItem by it's defindex.
153 | ///
154 | public Item GetItem (int defindex)
155 | {
156 | foreach (Item item in Items)
157 | {
158 | if (item.Defindex == defindex)
159 | return item;
160 | }
161 | return null;
162 | }
163 |
164 | ///
165 | /// Returns all Items of the given crafting material.
166 | ///
167 | /// Item's craft_material_type JSON property.
168 | ///
169 | public List
- GetItemsByCraftingMaterial(string material)
170 | {
171 | return Items.Where(item => item.CraftMaterialType == material).ToList();
172 | }
173 |
174 | public List
- GetItems()
175 | {
176 | return Items.ToList();
177 | }
178 |
179 | public class ItemOrigin
180 | {
181 | [JsonProperty("origin")]
182 | public int Origin { get; set; }
183 |
184 | [JsonProperty("name")]
185 | public string Name { get; set; }
186 | }
187 |
188 | public class Item
189 | {
190 | [JsonProperty("name")]
191 | public string Name { get; set; }
192 |
193 | [JsonProperty("defindex")]
194 | public ushort Defindex { get; set; }
195 |
196 | [JsonProperty("item_class")]
197 | public string ItemClass { get; set; }
198 |
199 | [JsonProperty("item_type_name")]
200 | public string ItemTypeName { get; set; }
201 |
202 | [JsonProperty("item_name")]
203 | public string ItemName { get; set; }
204 |
205 | [JsonProperty("proper_name")]
206 | public bool ProperName { get; set; }
207 |
208 | [JsonProperty("craft_material_type")]
209 | public string CraftMaterialType { get; set; }
210 |
211 | [JsonProperty("used_by_classes")]
212 | public string[] UsableByClasses { get; set; }
213 |
214 | [JsonProperty("item_slot")]
215 | public string ItemSlot { get; set; }
216 |
217 | [JsonProperty("craft_class")]
218 | public string CraftClass { get; set; }
219 |
220 | [JsonProperty("item_quality")]
221 | public int ItemQuality { get; set; }
222 | }
223 |
224 | protected class SchemaResult
225 | {
226 | public Schema result { get; set; }
227 | }
228 | }
229 | }
230 |
231 |
--------------------------------------------------------------------------------
/SteamTrade/SteamTrade.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | AnyCPU
6 | 10.0.0
7 | 2.0
8 | {6CEC0333-81EB-40EE-85D1-941363626FC7}
9 | Library
10 | SteamTrade
11 | SteamTrade
12 | ..\
13 | true
14 | v4.5
15 |
16 |
17 |
18 | True
19 | full
20 | False
21 | bin\Debug
22 | DEBUG;
23 | prompt
24 | 4
25 | False
26 | false
27 | 5
28 |
29 |
30 | full
31 | True
32 | bin\Release
33 | prompt
34 | 4
35 | False
36 | true
37 | false
38 |
39 |
40 |
41 | ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll
42 | True
43 |
44 |
45 | ..\packages\protobuf-net.2.0.0.668\lib\net40\protobuf-net.dll
46 |
47 |
48 | ..\packages\SteamAuth.2.0.0\lib\net45\SteamAuth.dll
49 | True
50 |
51 |
52 | ..\packages\SteamKit2.1.8.1\lib\net45\SteamKit2.dll
53 | True
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/SteamTrade/TradeOffer/OfferSession.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using SteamKit2;
3 | using System;
4 | using System.Collections.Specialized;
5 | using System.Diagnostics;
6 | using System.IO;
7 | using System.Net;
8 |
9 | namespace SteamTrade.TradeOffer
10 | {
11 | public class OfferSession
12 | {
13 | private readonly TradeOfferWebAPI webApi;
14 | private readonly SteamWeb steamWeb;
15 |
16 | internal JsonSerializerSettings JsonSerializerSettings { get; set; }
17 |
18 | internal const string SendUrl = "https://steamcommunity.com/tradeoffer/new/send";
19 |
20 | public OfferSession(TradeOfferWebAPI webApi, SteamWeb steamWeb)
21 | {
22 | this.webApi = webApi;
23 | this.steamWeb = steamWeb;
24 |
25 | JsonSerializerSettings = new JsonSerializerSettings();
26 | JsonSerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.None;
27 | JsonSerializerSettings.Formatting = Formatting.None;
28 | }
29 |
30 | public TradeOfferAcceptResponse Accept(string tradeOfferId)
31 | {
32 | var data = new NameValueCollection();
33 | data.Add("sessionid", steamWeb.SessionId);
34 | data.Add("serverid", "1");
35 | data.Add("tradeofferid", tradeOfferId);
36 |
37 | string url = string.Format("https://steamcommunity.com/tradeoffer/{0}/accept", tradeOfferId);
38 | string referer = string.Format("https://steamcommunity.com/tradeoffer/{0}/", tradeOfferId);
39 |
40 | string resp = steamWeb.Fetch(url, "POST", data, false, referer, true);
41 |
42 | if (!String.IsNullOrEmpty(resp))
43 | {
44 | try
45 | {
46 | var res = JsonConvert.DeserializeObject(resp);
47 | //steam can return 'null' response
48 | if (res != null) {
49 | res.Accepted = string.IsNullOrEmpty(res.TradeError);
50 | return res;
51 | }
52 | }
53 | catch (JsonException)
54 | {
55 | return new TradeOfferAcceptResponse { TradeError = "Error parsing server response: " + resp };
56 | }
57 | }
58 | //if it didn't work as expected, check the state, maybe it was accepted after all
59 | var state = webApi.GetOfferState(tradeOfferId);
60 | return new TradeOfferAcceptResponse { Accepted = state == TradeOfferState.TradeOfferStateAccepted };
61 | }
62 |
63 | public bool Decline(string tradeOfferId)
64 | {
65 | var data = new NameValueCollection();
66 | data.Add("sessionid", steamWeb.SessionId);
67 | data.Add("serverid", "1");
68 | data.Add("tradeofferid", tradeOfferId);
69 |
70 | string url = string.Format("https://steamcommunity.com/tradeoffer/{0}/decline", tradeOfferId);
71 | //should be http://steamcommunity.com/{0}/{1}/tradeoffers - id/profile persona/id64 ideally
72 | string referer = string.Format("https://steamcommunity.com/tradeoffer/{0}/", tradeOfferId);
73 |
74 | var resp = steamWeb.Fetch(url, "POST", data, false, referer);
75 |
76 | if (!String.IsNullOrEmpty(resp))
77 | {
78 | try
79 | {
80 | var json = JsonConvert.DeserializeObject(resp);
81 | if (json.TradeOfferId != null && json.TradeOfferId == tradeOfferId)
82 | {
83 | return true;
84 | }
85 | }
86 | catch (JsonException jsex)
87 | {
88 | Debug.WriteLine(jsex);
89 | }
90 | }
91 | else
92 | {
93 | var state = webApi.GetOfferState(tradeOfferId);
94 | if (state == TradeOfferState.TradeOfferStateDeclined)
95 | {
96 | return true;
97 | }
98 | }
99 | return false;
100 | }
101 |
102 | public bool Cancel(string tradeOfferId)
103 | {
104 | var data = new NameValueCollection();
105 | data.Add("sessionid", steamWeb.SessionId);
106 | data.Add("tradeofferid", tradeOfferId);
107 | data.Add("serverid", "1");
108 | string url = string.Format("https://steamcommunity.com/tradeoffer/{0}/cancel", tradeOfferId);
109 | //should be http://steamcommunity.com/{0}/{1}/tradeoffers/sent/ - id/profile persona/id64 ideally
110 | string referer = string.Format("https://steamcommunity.com/tradeoffer/{0}/", tradeOfferId);
111 |
112 | var resp = steamWeb.Fetch(url, "POST", data, false, referer);
113 |
114 | if (!String.IsNullOrEmpty(resp))
115 | {
116 | try
117 | {
118 | var json = JsonConvert.DeserializeObject(resp);
119 | if (json.TradeOfferId != null && json.TradeOfferId == tradeOfferId)
120 | {
121 | return true;
122 | }
123 | }
124 | catch (JsonException jsex)
125 | {
126 | Debug.WriteLine(jsex);
127 | }
128 | }
129 | else
130 | {
131 | var state = webApi.GetOfferState(tradeOfferId);
132 | if (state == TradeOfferState.TradeOfferStateCanceled)
133 | {
134 | return true;
135 | }
136 | }
137 | return false;
138 | }
139 |
140 | ///
141 | /// Creates a new counter offer
142 | ///
143 | /// A message to include with the trade offer
144 | /// The SteamID of the partner we are trading with
145 | /// The list of items we and they are going to trade
146 | /// The trade offer Id that will be created if successful
147 | /// The trade offer Id of the offer being countered
148 | ///
149 | public bool CounterOffer(string message, SteamID otherSteamId, TradeOffer.TradeStatus status, out string newTradeOfferId, string tradeOfferId)
150 | {
151 | if (String.IsNullOrEmpty(tradeOfferId))
152 | {
153 | throw new ArgumentNullException("tradeOfferId", "Trade Offer Id must be set for counter offers.");
154 | }
155 |
156 | var data = new NameValueCollection();
157 | data.Add("sessionid", steamWeb.SessionId);
158 | data.Add("serverid", "1");
159 | data.Add("partner", otherSteamId.ConvertToUInt64().ToString());
160 | data.Add("tradeoffermessage", message);
161 | data.Add("json_tradeoffer", JsonConvert.SerializeObject(status, JsonSerializerSettings));
162 | data.Add("tradeofferid_countered", tradeOfferId);
163 | data.Add("trade_offer_create_params", "{}");
164 |
165 | string referer = string.Format("https://steamcommunity.com/tradeoffer/{0}/", tradeOfferId);
166 |
167 | if (!Request(SendUrl, data, referer, tradeOfferId, out newTradeOfferId))
168 | {
169 | var state = webApi.GetOfferState(tradeOfferId);
170 | if (state == TradeOfferState.TradeOfferStateCountered)
171 | {
172 | return true;
173 | }
174 | return false;
175 | }
176 | return true;
177 | }
178 |
179 | ///
180 | /// Creates a new trade offer
181 | ///
182 | /// A message to include with the trade offer
183 | /// The SteamID of the partner we are trading with
184 | /// The list of items we and they are going to trade
185 | /// The trade offer Id that will be created if successful
186 | /// True if successfully returns a newTradeOfferId, else false
187 | public bool SendTradeOffer(string message, SteamID otherSteamId, TradeOffer.TradeStatus status, out string newTradeOfferId)
188 | {
189 | var data = new NameValueCollection();
190 | data.Add("sessionid", steamWeb.SessionId);
191 | data.Add("serverid", "1");
192 | data.Add("partner", otherSteamId.ConvertToUInt64().ToString());
193 | data.Add("tradeoffermessage", message);
194 | data.Add("json_tradeoffer", JsonConvert.SerializeObject(status, JsonSerializerSettings));
195 | data.Add("trade_offer_create_params", "{}");
196 |
197 | string referer = string.Format("https://steamcommunity.com/tradeoffer/new/?partner={0}",
198 | otherSteamId.AccountID);
199 |
200 | return Request(SendUrl, data, referer, null, out newTradeOfferId);
201 | }
202 |
203 | ///
204 | /// Creates a new trade offer with a token
205 | ///
206 | /// A message to include with the trade offer
207 | /// The SteamID of the partner we are trading with
208 | /// The list of items we and they are going to trade
209 | /// The token of the partner we are trading with
210 | /// The trade offer Id that will be created if successful
211 | /// True if successfully returns a newTradeOfferId, else false
212 | public bool SendTradeOfferWithToken(string message, SteamID otherSteamId, TradeOffer.TradeStatus status,
213 | string token, out string newTradeOfferId)
214 | {
215 | if (String.IsNullOrEmpty(token))
216 | {
217 | throw new ArgumentNullException("token", "Partner trade offer token is missing");
218 | }
219 | var offerToken = new OfferAccessToken() { TradeOfferAccessToken = token };
220 |
221 | var data = new NameValueCollection();
222 | data.Add("sessionid", steamWeb.SessionId);
223 | data.Add("serverid", "1");
224 | data.Add("partner", otherSteamId.ConvertToUInt64().ToString());
225 | data.Add("tradeoffermessage", message);
226 | data.Add("json_tradeoffer", JsonConvert.SerializeObject(status, JsonSerializerSettings));
227 | data.Add("trade_offer_create_params", JsonConvert.SerializeObject(offerToken, JsonSerializerSettings));
228 |
229 | string referer = string.Format("https://steamcommunity.com/tradeoffer/new/?partner={0}&token={1}",
230 | otherSteamId.AccountID, token);
231 | return Request(SendUrl, data, referer, null, out newTradeOfferId);
232 | }
233 |
234 | internal bool Request(string url, NameValueCollection data, string referer, string tradeOfferId, out string newTradeOfferId)
235 | {
236 | newTradeOfferId = "";
237 |
238 | string resp = steamWeb.Fetch(url, "POST", data, false, referer);
239 | if (!String.IsNullOrEmpty(resp))
240 | {
241 | try
242 | {
243 | var offerResponse = JsonConvert.DeserializeObject(resp);
244 | if (!String.IsNullOrEmpty(offerResponse.TradeOfferId))
245 | {
246 | newTradeOfferId = offerResponse.TradeOfferId;
247 | return true;
248 | }
249 | else
250 | {
251 | //todo: log possible error
252 | Debug.WriteLine(offerResponse.TradeError);
253 | }
254 | }
255 | catch (JsonException jsex)
256 | {
257 | Debug.WriteLine(jsex);
258 | }
259 | }
260 | return false;
261 | }
262 | }
263 |
264 | public class NewTradeOfferResponse
265 | {
266 | [JsonProperty("tradeofferid")]
267 | public string TradeOfferId { get; set; }
268 |
269 | [JsonProperty("strError")]
270 | public string TradeError { get; set; }
271 | }
272 |
273 | public class OfferAccessToken
274 | {
275 | [JsonProperty("trade_offer_access_token")]
276 | public string TradeOfferAccessToken { get; set; }
277 | }
278 |
279 | public class TradeOfferAcceptResponse
280 | {
281 | public bool Accepted { get; set; }
282 |
283 | [JsonProperty("tradeid")]
284 | public string TradeId { get; set; }
285 |
286 | [JsonProperty("strError")]
287 | public string TradeError { get; set; }
288 |
289 | public TradeOfferAcceptResponse()
290 | {
291 | TradeId = String.Empty;
292 | TradeError = String.Empty;
293 | }
294 | }
295 | }
296 |
--------------------------------------------------------------------------------
/SteamTrade/TradeOffer/TradeOfferManager.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Diagnostics;
3 | using SteamKit2;
4 | using System;
5 | using System.Linq;
6 | using System.Threading.Tasks;
7 |
8 | namespace SteamTrade.TradeOffer
9 | {
10 | public class TradeOfferManager
11 | {
12 | private readonly Dictionary knownTradeOffers = new Dictionary();
13 | private readonly OfferSession session;
14 | private readonly TradeOfferWebAPI webApi;
15 | private readonly Queue unhandledTradeOfferUpdates;
16 |
17 | public DateTime LastTimeCheckedOffers { get; private set; }
18 |
19 | public TradeOfferManager(string apiKey, SteamWeb steamWeb)
20 | {
21 | if (apiKey == null)
22 | throw new ArgumentNullException("apiKey");
23 |
24 | LastTimeCheckedOffers = DateTime.MinValue;
25 | webApi = new TradeOfferWebAPI(apiKey, steamWeb);
26 | session = new OfferSession(webApi, steamWeb);
27 | unhandledTradeOfferUpdates = new Queue();
28 | }
29 |
30 | public delegate void TradeOfferUpdatedHandler(TradeOffer offer);
31 |
32 | ///
33 | /// Occurs when a new trade offer has been made by the other user
34 | ///
35 | public event TradeOfferUpdatedHandler OnTradeOfferUpdated;
36 |
37 | public void EnqueueUpdatedOffers()
38 | {
39 | DateTime startTime = DateTime.Now;
40 |
41 | var offersResponse = (LastTimeCheckedOffers == DateTime.MinValue
42 | ? webApi.GetAllTradeOffers()
43 | : webApi.GetAllTradeOffers(GetUnixTimeStamp(LastTimeCheckedOffers).ToString()));
44 | AddTradeOffersToQueue(offersResponse);
45 |
46 | LastTimeCheckedOffers = startTime - TimeSpan.FromMinutes(5); //Lazy way to make sure we don't miss any trade offers due to slightly differing clocks
47 | }
48 |
49 | private void AddTradeOffersToQueue(OffersResponse offers)
50 | {
51 | if (offers == null || offers.AllOffers == null)
52 | return;
53 |
54 | lock(unhandledTradeOfferUpdates)
55 | {
56 | foreach(var offer in offers.AllOffers)
57 | {
58 | unhandledTradeOfferUpdates.Enqueue(offer);
59 | }
60 | }
61 | }
62 |
63 | public bool HandleNextPendingTradeOfferUpdate()
64 | {
65 | Offer nextOffer;
66 | lock (unhandledTradeOfferUpdates)
67 | {
68 | if (!unhandledTradeOfferUpdates.Any())
69 | {
70 | return false;
71 | }
72 | nextOffer = unhandledTradeOfferUpdates.Dequeue();
73 | }
74 |
75 | return HandleTradeOfferUpdate(nextOffer);
76 | }
77 |
78 | private bool HandleTradeOfferUpdate(Offer offer)
79 | {
80 | if(knownTradeOffers.ContainsKey(offer.TradeOfferId) && knownTradeOffers[offer.TradeOfferId] == offer.TradeOfferState)
81 | {
82 | return false;
83 | }
84 |
85 | //make sure the api loaded correctly sometimes the items are missing
86 | if(IsOfferValid(offer))
87 | {
88 | SendOfferToHandler(offer);
89 | }
90 | else
91 | {
92 | var resp = webApi.GetTradeOffer(offer.TradeOfferId);
93 | if(IsOfferValid(resp.Offer))
94 | {
95 | SendOfferToHandler(resp.Offer);
96 | }
97 | else
98 | {
99 | Debug.WriteLine("Offer returned from steam api is not valid : " + resp.Offer.TradeOfferId);
100 | return false;
101 | }
102 | }
103 | return true;
104 | }
105 |
106 | private bool IsOfferValid(Offer offer)
107 | {
108 | bool hasItemsToGive = offer.ItemsToGive != null && offer.ItemsToGive.Count != 0;
109 | bool hasItemsToReceive = offer.ItemsToReceive != null && offer.ItemsToReceive.Count != 0;
110 | return hasItemsToGive || hasItemsToReceive;
111 | }
112 |
113 | private void SendOfferToHandler(Offer offer)
114 | {
115 | knownTradeOffers[offer.TradeOfferId] = offer.TradeOfferState;
116 | OnTradeOfferUpdated(new TradeOffer(session, offer));
117 | }
118 |
119 | private uint GetUnixTimeStamp(DateTime dateTime)
120 | {
121 | var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
122 | return (uint)((dateTime.ToUniversalTime() - epoch).TotalSeconds);
123 | }
124 |
125 | public TradeOffer NewOffer(SteamID other)
126 | {
127 | return new TradeOffer(session, other);
128 | }
129 |
130 | public bool TryGetOffer(string offerId, out TradeOffer tradeOffer)
131 | {
132 | tradeOffer = null;
133 | var resp = webApi.GetTradeOffer(offerId);
134 | if (resp != null)
135 | {
136 | if (IsOfferValid(resp.Offer))
137 | {
138 | tradeOffer = new TradeOffer(session, resp.Offer);
139 | return true;
140 | }
141 | else
142 | {
143 | Debug.WriteLine("Offer returned from steam api is not valid : " + resp.Offer.TradeOfferId);
144 | }
145 | }
146 | return false;
147 | }
148 | }
149 | }
--------------------------------------------------------------------------------
/SteamTrade/TradeOffer/TradeOfferWebAPI.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Diagnostics;
5 | using System.Linq;
6 |
7 | namespace SteamTrade.TradeOffer
8 | {
9 | public class TradeOfferWebAPI
10 | {
11 | private readonly SteamWeb steamWeb;
12 | private readonly string apiKey;
13 |
14 | public TradeOfferWebAPI(string apiKey, SteamWeb steamWeb)
15 | {
16 | this.steamWeb = steamWeb;
17 | this.apiKey = apiKey;
18 |
19 | if (apiKey == null)
20 | {
21 | throw new ArgumentNullException("apiKey");
22 | }
23 | }
24 |
25 | private const string BaseUrl = "http://api.steampowered.com/IEconService/{0}/{1}/{2}";
26 |
27 | public OfferResponse GetTradeOffer(string tradeofferid)
28 | {
29 | string options = string.Format("?key={0}&tradeofferid={1}&language={2}&get_descriptions=1", apiKey, tradeofferid, "en_us");
30 | string url = String.Format(BaseUrl, "GetTradeOffer", "v1", options);
31 | try
32 | {
33 | string response = steamWeb.Fetch(url, "GET", null, false);
34 | var result = JsonConvert.DeserializeObject>(response);
35 | return result.Response;
36 | }
37 | catch (Exception ex)
38 | {
39 | //todo log
40 | Debug.WriteLine(ex);
41 | }
42 | return new OfferResponse();
43 | }
44 |
45 | public TradeOfferState GetOfferState(string tradeofferid)
46 | {
47 | var resp = GetTradeOffer(tradeofferid);
48 | if (resp != null && resp.Offer != null)
49 | {
50 | return resp.Offer.TradeOfferState;
51 | }
52 | return TradeOfferState.TradeOfferStateUnknown;
53 | }
54 |
55 | public OffersResponse GetAllTradeOffers(string timeHistoricalCutoff = "1389106496", string language = "en_us")
56 | {
57 | return GetTradeOffers(true, true, false, true, false, timeHistoricalCutoff, language);
58 | }
59 |
60 | public OffersResponse GetActiveTradeOffers(bool getSentOffers, bool getReceivedOffers, bool getDescriptions, string language = "en_us")
61 | {
62 | return GetTradeOffers(getSentOffers, getReceivedOffers, getDescriptions, true, false, "1389106496", language);
63 | }
64 |
65 | public OffersResponse GetTradeOffers(bool getSentOffers, bool getReceivedOffers, bool getDescriptions, bool activeOnly, bool historicalOnly, string timeHistoricalCutoff = "1389106496", string language = "en_us")
66 | {
67 | if (!getSentOffers && !getReceivedOffers)
68 | {
69 | throw new ArgumentException("getSentOffers and getReceivedOffers can't be both false");
70 | }
71 |
72 | string options = string.Format("?key={0}&get_sent_offers={1}&get_received_offers={2}&get_descriptions={3}&language={4}&active_only={5}&historical_only={6}&time_historical_cutoff={7}",
73 | apiKey, BoolConverter(getSentOffers), BoolConverter(getReceivedOffers), BoolConverter(getDescriptions), language, BoolConverter(activeOnly), BoolConverter(historicalOnly), timeHistoricalCutoff);
74 | string url = String.Format(BaseUrl, "GetTradeOffers", "v1", options);
75 | string response = steamWeb.Fetch(url, "GET", null, false);
76 | try
77 | {
78 | var result = JsonConvert.DeserializeObject>(response);
79 | return result.Response;
80 | }
81 | catch (Exception ex)
82 | {
83 | //todo log
84 | Debug.WriteLine(ex);
85 | }
86 | return new OffersResponse();
87 | }
88 |
89 | private static string BoolConverter(bool value)
90 | {
91 | return value ? "1" : "0";
92 | }
93 |
94 | public TradeOffersSummary GetTradeOffersSummary(UInt32 timeLastVisit)
95 | {
96 | string options = string.Format("?key={0}&time_last_visit={1}", apiKey, timeLastVisit);
97 | string url = String.Format(BaseUrl, "GetTradeOffersSummary", "v1", options);
98 |
99 | try
100 | {
101 | string response = steamWeb.Fetch(url, "GET", null, false);
102 | var resp = JsonConvert.DeserializeObject>(response);
103 |
104 | return resp.Response;
105 | }
106 | catch (Exception ex)
107 | {
108 | //todo log
109 | Debug.WriteLine(ex);
110 | }
111 | return new TradeOffersSummary();
112 | }
113 |
114 | private bool DeclineTradeOffer(ulong tradeofferid)
115 | {
116 | string options = string.Format("?key={0}&tradeofferid={1}", apiKey, tradeofferid);
117 | string url = String.Format(BaseUrl, "DeclineTradeOffer", "v1", options);
118 | Debug.WriteLine(url);
119 | string response = steamWeb.Fetch(url, "POST", null, false);
120 | dynamic json = JsonConvert.DeserializeObject(response);
121 |
122 | if (json == null || json.success != "1")
123 | {
124 | return false;
125 | }
126 | return true;
127 | }
128 |
129 | private bool CancelTradeOffer(ulong tradeofferid)
130 | {
131 | string options = string.Format("?key={0}&tradeofferid={1}", apiKey, tradeofferid);
132 | string url = String.Format(BaseUrl, "CancelTradeOffer", "v1", options);
133 | Debug.WriteLine(url);
134 | string response = steamWeb.Fetch(url, "POST", null, false);
135 | dynamic json = JsonConvert.DeserializeObject(response);
136 |
137 | if (json == null || json.success != "1")
138 | {
139 | return false;
140 | }
141 | return true;
142 | }
143 | }
144 |
145 | public class TradeOffersSummary
146 | {
147 | [JsonProperty("pending_received_count")]
148 | public int PendingReceivedCount { get; set; }
149 |
150 | [JsonProperty("new_received_count")]
151 | public int NewReceivedCount { get; set; }
152 |
153 | [JsonProperty("updated_received_count")]
154 | public int UpdatedReceivedCount { get; set; }
155 |
156 | [JsonProperty("historical_received_count")]
157 | public int HistoricalReceivedCount { get; set; }
158 |
159 | [JsonProperty("pending_sent_count")]
160 | public int PendingSentCount { get; set; }
161 |
162 | [JsonProperty("newly_accepted_sent_count")]
163 | public int NewlyAcceptedSentCount { get; set; }
164 |
165 | [JsonProperty("updated_sent_count")]
166 | public int UpdatedSentCount { get; set; }
167 |
168 | [JsonProperty("historical_sent_count")]
169 | public int HistoricalSentCount { get; set; }
170 | }
171 |
172 | public class ApiResponse
173 | {
174 | [JsonProperty("response")]
175 | public T Response { get; set; }
176 | }
177 |
178 | public class OfferResponse
179 | {
180 | [JsonProperty("offer")]
181 | public Offer Offer { get; set; }
182 |
183 | [JsonProperty("descriptions")]
184 | public List Descriptions { get; set; }
185 | }
186 |
187 | public class OffersResponse
188 | {
189 | [JsonProperty("trade_offers_sent")]
190 | public List TradeOffersSent { get; set; }
191 |
192 | [JsonProperty("trade_offers_received")]
193 | public List TradeOffersReceived { get; set; }
194 |
195 | [JsonProperty("descriptions")]
196 | public List Descriptions { get; set; }
197 |
198 | public IEnumerable AllOffers
199 | {
200 | get
201 | {
202 | if (TradeOffersSent == null)
203 | {
204 | return TradeOffersReceived;
205 | }
206 |
207 | return (TradeOffersReceived == null ? TradeOffersSent : TradeOffersSent.Union(TradeOffersReceived));
208 | }
209 | }
210 | }
211 |
212 | public class Offer
213 | {
214 | [JsonProperty("tradeofferid")]
215 | public string TradeOfferId { get; set; }
216 |
217 | [JsonProperty("accountid_other")]
218 | public int AccountIdOther { get; set; }
219 |
220 | [JsonProperty("message")]
221 | public string Message { get; set; }
222 |
223 | [JsonProperty("expiration_time")]
224 | public int ExpirationTime { get; set; }
225 |
226 | [JsonProperty("trade_offer_state")]
227 | public TradeOfferState TradeOfferState { get; set; }
228 |
229 | [JsonProperty("items_to_give")]
230 | public List ItemsToGive { get; set; }
231 |
232 | [JsonProperty("items_to_receive")]
233 | public List ItemsToReceive { get; set; }
234 |
235 | [JsonProperty("is_our_offer")]
236 | public bool IsOurOffer { get; set; }
237 |
238 | [JsonProperty("time_created")]
239 | public int TimeCreated { get; set; }
240 |
241 | [JsonProperty("time_updated")]
242 | public int TimeUpdated { get; set; }
243 |
244 | [JsonProperty("from_real_time_trade")]
245 | public bool FromRealTimeTrade { get; set; }
246 |
247 | [JsonProperty("escrow_end_date")]
248 | public int EscrowEndDate { get; set; }
249 |
250 | [JsonProperty("confirmation_method")]
251 | private int confirmationMethod { get; set; }
252 | public TradeOfferConfirmationMethod ConfirmationMethod { get { return (TradeOfferConfirmationMethod)confirmationMethod; } set { confirmationMethod = (int)value; } }
253 | }
254 |
255 | public enum TradeOfferState
256 | {
257 | TradeOfferStateInvalid = 1,
258 | TradeOfferStateActive = 2,
259 | TradeOfferStateAccepted = 3,
260 | TradeOfferStateCountered = 4,
261 | TradeOfferStateExpired = 5,
262 | TradeOfferStateCanceled = 6,
263 | TradeOfferStateDeclined = 7,
264 | TradeOfferStateInvalidItems = 8,
265 | TradeOfferStateNeedsConfirmation = 9,
266 | TradeOfferStateCanceledBySecondFactor = 10,
267 | TradeOfferStateInEscrow = 11,
268 | TradeOfferStateUnknown
269 | }
270 |
271 | public enum TradeOfferConfirmationMethod
272 | {
273 | TradeOfferConfirmationMethodInvalid = 0,
274 | TradeOfferConfirmationMethodEmail = 1,
275 | TradeOfferConfirmationMethodMobileApp = 2
276 | }
277 |
278 | public class CEconAsset
279 | {
280 | [JsonProperty("appid")]
281 | public string AppId { get; set; }
282 |
283 | [JsonProperty("contextid")]
284 | public string ContextId { get; set; }
285 |
286 | [JsonProperty("assetid")]
287 | public string AssetId { get; set; }
288 |
289 | [JsonProperty("classid")]
290 | public string ClassId { get; set; }
291 |
292 | [JsonProperty("instanceid")]
293 | public string InstanceId { get; set; }
294 |
295 | [JsonProperty("amount")]
296 | public string Amount { get; set; }
297 |
298 | [JsonProperty("missing")]
299 | public bool IsMissing { get; set; }
300 | }
301 |
302 | public class AssetDescription
303 | {
304 | [JsonProperty("appid")]
305 | public int AppId { get; set; }
306 |
307 | [JsonProperty("classid")]
308 | public string ClassId { get; set; }
309 |
310 | [JsonProperty("instanceid")]
311 | public string InstanceId { get; set; }
312 |
313 | [JsonProperty("currency")]
314 | public bool IsCurrency { get; set; }
315 |
316 | [JsonProperty("background_color")]
317 | public string BackgroundColor { get; set; }
318 |
319 | [JsonProperty("icon_url")]
320 | public string IconUrl { get; set; }
321 |
322 | [JsonProperty("icon_url_large")]
323 | public string IconUrlLarge { get; set; }
324 |
325 | [JsonProperty("descriptions")]
326 | public List Descriptions { get; set; }
327 |
328 | [JsonProperty("tradable")]
329 | public bool IsTradable { get; set; }
330 |
331 | [JsonProperty("owner_actions")]
332 | public List OwnerActions { get; set; }
333 |
334 | [JsonProperty("name")]
335 | public string Name { get; set; }
336 |
337 | [JsonProperty("name_color")]
338 | public string NameColor { get; set; }
339 |
340 | [JsonProperty("type")]
341 | public string Type { get; set; }
342 |
343 | [JsonProperty("market_name")]
344 | public string MarketName { get; set; }
345 |
346 | [JsonProperty("market_hash_name")]
347 | public string MarketHashName { get; set; }
348 | }
349 |
350 | public class Description
351 | {
352 | [JsonProperty("type")]
353 | public string Type { get; set; }
354 |
355 | [JsonProperty("value")]
356 | public string Value { get; set; }
357 | }
358 |
359 | public class OwnerAction
360 | {
361 | [JsonProperty("link")]
362 | public string Link { get; set; }
363 |
364 | [JsonProperty("name")]
365 | public string Name { get; set; }
366 | }
367 | }
--------------------------------------------------------------------------------
/SteamTrade/TradeWebAPI/TradeSession.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Specialized;
3 | using System.Net;
4 | using Newtonsoft.Json;
5 | using SteamKit2;
6 |
7 | namespace SteamTrade.TradeWebAPI
8 | {
9 | ///
10 | /// This class provides the interface into the Web API for trading on the
11 | /// Steam network.
12 | ///
13 | public class TradeSession
14 | {
15 | private const string SteamTradeUrl = "http://steamcommunity.com/trade/{0}/";
16 |
17 | private string sessionIdEsc;
18 | private string baseTradeURL;
19 |
20 | private readonly SteamWeb SteamWeb;
21 | private readonly SteamID OtherSID;
22 |
23 | ///
24 | /// Initializes a new instance of the class.
25 | ///
26 | /// The Steam id of the other trading partner.
27 | /// The SteamWeb instance for this bot
28 | public TradeSession(SteamID otherSid, SteamWeb steamWeb)
29 | {
30 | OtherSID = otherSid;
31 | SteamWeb = steamWeb;
32 |
33 | Init();
34 | }
35 |
36 | #region Trade status properties
37 |
38 | ///
39 | /// Gets the LogPos number of the current trade.
40 | ///
41 | /// This is not automatically updated by this class.
42 | internal int LogPos { get; set; }
43 |
44 | ///
45 | /// Gets the version number of the current trade. This increments on
46 | /// every item added or removed from a trade.
47 | ///
48 | /// This is not automatically updated by this class.
49 | internal int Version { get; set; }
50 |
51 | #endregion Trade status properties
52 |
53 | #region Trade Web API command methods
54 |
55 | ///
56 | /// Gets the trade status.
57 | ///
58 | /// A deserialized JSON object into
59 | ///
60 | /// This is the main polling method for trading and must be done at a
61 | /// periodic rate (probably around 1 second).
62 | ///
63 | internal TradeStatus GetStatus()
64 | {
65 | var data = new NameValueCollection ();
66 |
67 | data.Add ("sessionid", sessionIdEsc);
68 | data.Add ("logpos", "" + LogPos);
69 | data.Add ("version", "" + Version);
70 |
71 | string response = Fetch (baseTradeURL + "tradestatus", "POST", data);
72 |
73 | return JsonConvert.DeserializeObject (response);
74 | }
75 |
76 |
77 | ///
78 | /// Gets the foreign inventory.
79 | ///
80 | /// The other id.
81 | /// A dynamic JSON object.
82 | internal dynamic GetForeignInventory(SteamID otherId)
83 | {
84 | return GetForeignInventory(otherId, 440, 2);
85 | }
86 | internal dynamic GetForeignInventory(SteamID otherId, long contextId, int appid)
87 | {
88 | var data = new NameValueCollection();
89 |
90 | data.Add ("sessionid", sessionIdEsc);
91 | data.Add ("steamid", "" + otherId.ConvertToUInt64());
92 | data.Add ("appid", "" + appid);
93 | data.Add ("contextid", "" + contextId);
94 |
95 | try
96 | {
97 | string response = Fetch(baseTradeURL + "foreigninventory", "GET", data);
98 | return JsonConvert.DeserializeObject(response);
99 | }
100 | catch (Exception)
101 | {
102 | return JsonConvert.DeserializeObject("{\"success\":\"false\"}");
103 | }
104 | }
105 |
106 | ///
107 | /// Sends a message to the user over the trade chat.
108 | ///
109 | internal bool SendMessageWebCmd(string msg)
110 | {
111 | var data = new NameValueCollection ();
112 | data.Add ("sessionid", sessionIdEsc);
113 | data.Add ("message", msg);
114 | data.Add ("logpos", "" + LogPos);
115 | data.Add ("version", "" + Version);
116 |
117 | string result = Fetch (baseTradeURL + "chat", "POST", data);
118 |
119 | dynamic json = JsonConvert.DeserializeObject(result);
120 | return IsSuccess(json);
121 | }
122 |
123 | ///
124 | /// Adds a specified item by its itemid. Since each itemid is
125 | /// unique to each item, you'd first have to find the item, or
126 | /// use AddItemByDefindex instead.
127 | ///
128 | ///
129 | /// Returns false if the item doesn't exist in the Bot's inventory,
130 | /// and returns true if it appears the item was added.
131 | ///
132 | internal bool AddItemWebCmd(ulong itemid, int slot,int appid,long contextid)
133 | {
134 | var data = new NameValueCollection ();
135 |
136 | data.Add ("sessionid", sessionIdEsc);
137 | data.Add ("appid", "" + appid);
138 | data.Add ("contextid", "" + contextid);
139 | data.Add ("itemid", "" + itemid);
140 | data.Add ("slot", "" + slot);
141 |
142 | string result = Fetch(baseTradeURL + "additem", "POST", data);
143 |
144 | dynamic json = JsonConvert.DeserializeObject(result);
145 | return IsSuccess(json);
146 | }
147 |
148 | ///
149 | /// Removes an item by its itemid. Read AddItem about itemids.
150 | /// Returns false if the item isn't in the offered items, or
151 | /// true if it appears it succeeded.
152 | ///
153 | internal bool RemoveItemWebCmd(ulong itemid, int slot, int appid, long contextid)
154 | {
155 | var data = new NameValueCollection ();
156 |
157 | data.Add ("sessionid", sessionIdEsc);
158 | data.Add ("appid", "" + appid);
159 | data.Add ("contextid", "" + contextid);
160 | data.Add ("itemid", "" + itemid);
161 | data.Add ("slot", "" + slot);
162 |
163 | string result = Fetch (baseTradeURL + "removeitem", "POST", data);
164 |
165 | dynamic json = JsonConvert.DeserializeObject(result);
166 | return IsSuccess(json);
167 | }
168 |
169 | ///
170 | /// Sets the bot to a ready status.
171 | ///
172 | internal bool SetReadyWebCmd(bool ready)
173 | {
174 | var data = new NameValueCollection ();
175 | data.Add ("sessionid", sessionIdEsc);
176 | data.Add ("ready", ready ? "true" : "false");
177 | data.Add ("version", "" + Version);
178 |
179 | string result = Fetch (baseTradeURL + "toggleready", "POST", data);
180 |
181 | dynamic json = JsonConvert.DeserializeObject(result);
182 | return IsSuccess(json);
183 | }
184 |
185 | ///
186 | /// Accepts the trade from the user. Returns a deserialized
187 | /// JSON object.
188 | ///
189 | internal bool AcceptTradeWebCmd()
190 | {
191 | var data = new NameValueCollection ();
192 |
193 | data.Add ("sessionid", sessionIdEsc);
194 | data.Add ("version", "" + Version);
195 |
196 | string response = Fetch (baseTradeURL + "confirm", "POST", data);
197 |
198 | dynamic json = JsonConvert.DeserializeObject(response);
199 | return IsSuccess(json);
200 | }
201 |
202 | ///
203 | /// Cancel the trade. This calls the OnClose handler, as well.
204 | ///
205 | internal bool CancelTradeWebCmd ()
206 | {
207 | var data = new NameValueCollection ();
208 |
209 | data.Add ("sessionid", sessionIdEsc);
210 |
211 | string result = Fetch (baseTradeURL + "cancel", "POST", data);
212 |
213 | dynamic json = JsonConvert.DeserializeObject(result);
214 | return IsSuccess(json);
215 | }
216 |
217 | private bool IsSuccess(dynamic json)
218 | {
219 | if(json == null)
220 | return false;
221 | try
222 | {
223 | //Sometimes, the response looks like this: {"success":false,"results":{"success":11}}
224 | //I believe this is Steam's way of asking the trade window (which is actually a webpage) to refresh, following a large successful update
225 | return (json.success == "true" || (json.results != null && json.results.success == "11"));
226 | }
227 | catch(Exception)
228 | {
229 | return false;
230 | }
231 | }
232 |
233 | #endregion Trade Web API command methods
234 |
235 | string Fetch (string url, string method, NameValueCollection data = null)
236 | {
237 | return SteamWeb.Fetch (url, method, data);
238 | }
239 |
240 | private void Init()
241 | {
242 | sessionIdEsc = Uri.UnescapeDataString(SteamWeb.SessionId);
243 |
244 | Version = 1;
245 |
246 | baseTradeURL = String.Format (SteamTradeUrl, OtherSID.ConvertToUInt64());
247 | }
248 | }
249 | }
250 |
251 |
--------------------------------------------------------------------------------
/SteamTrade/TradeWebAPI/TradeStatus.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Newtonsoft.Json.Linq;
4 |
5 | namespace SteamTrade.TradeWebAPI
6 | {
7 | public class TradeStatus
8 | {
9 | public string error { get; set; }
10 |
11 | public bool newversion { get; set; }
12 |
13 | public bool success { get; set; }
14 |
15 | public string tradeid { get; set; }
16 |
17 | public long trade_status { get; set; }
18 |
19 | public int version { get; set; }
20 |
21 | public int logpos { get; set; }
22 |
23 | public TradeUserObj me { get; set; }
24 |
25 | public TradeUserObj them { get; set; }
26 |
27 | public TradeEvent[] events { get; set; }
28 |
29 | public TradeEvent GetLastEvent()
30 | {
31 | if (events == null || events.Length == 0)
32 | return null;
33 |
34 | return events[events.Length - 1];
35 | }
36 |
37 | public TradeEvent[] GetAllEvents()
38 | {
39 | if (events == null)
40 | return new TradeEvent[0];
41 |
42 | return events;
43 | }
44 | }
45 |
46 | public class TradeEvent : IEquatable
47 | {
48 | public string steamid { get; set; }
49 |
50 | public int action { get; set; }
51 |
52 | public ulong timestamp { get; set; }
53 |
54 | public int appid { get; set; }
55 |
56 | public string text { get; set; }
57 |
58 | public long contextid { get; set; }
59 |
60 | public ulong assetid { get; set; }
61 |
62 | ///
63 | /// Determins if the TradeEvent is equal to another.
64 | ///
65 | /// TradeEvent to compare to
66 | /// True if equal, false if not
67 | public bool Equals(TradeEvent other)
68 | {
69 | return this.steamid == other.steamid && this.action == other.action
70 | && this.timestamp == other.timestamp && this.appid == other.appid
71 | && this.text == other.text && this.contextid == other.contextid
72 | && this.assetid == other.assetid;
73 | }
74 | }
75 |
76 | public class TradeUserObj
77 | {
78 | public int ready { get; set; }
79 |
80 | public int confirmed { get; set; }
81 |
82 | public int sec_since_touch { get; set; }
83 |
84 | public bool connection_pending { get; set; }
85 |
86 | public dynamic assets { get; set; }
87 |
88 | ///
89 | /// Gets the array from the TradeUserObj. Use this instead
90 | /// of the property directly.
91 | ///
92 | /// An array of
93 | public TradeUserAssets[] GetAssets()
94 | {
95 | var tradeUserAssetses = new List();
96 |
97 | // if items were added in trade the type is an array like so:
98 | // a normal JSON array
99 | // "assets": [
100 | // {
101 | // "assetid": "1693638354",
102 | // }
103 | //],
104 | if (assets.GetType() == typeof(JArray))
105 | {
106 | foreach (var asset in assets)
107 | {
108 | tradeUserAssetses.Add(new TradeUserAssets((int)asset.appid, (long)asset.contextid, (ulong)asset.assetid, (int)asset.amount));
109 | }
110 | }
111 | else if (assets.GetType() == typeof(JObject))
112 | {
113 | // when items are removed from trade they look like this:
114 | // a JSON object like a "list"
115 | // (item in trade slot 1 was removed)
116 | // "assets": {
117 | // "2": {
118 | // "assetid": "1745718856",
119 | // },
120 | // "3": {
121 | // "assetid": "1690644335",
122 | // }
123 | //},
124 | foreach (JProperty obj in assets)
125 | {
126 | dynamic value = obj.Value;
127 | tradeUserAssetses.Add(new TradeUserAssets((int)value.appid, (long)value.contextid, (ulong)value.assetid, (int)value.amount));
128 | }
129 | }
130 |
131 | return tradeUserAssetses.ToArray();
132 | }
133 | }
134 |
135 | public class TradeUserAssets : IEquatable, IComparable
136 | {
137 | /// Iventory type
138 | public long contextid { get; private set; }
139 | /// itemid
140 | public ulong assetid { get; private set; }
141 | public int appid { get; private set; }
142 | public int amount { get; private set; }
143 |
144 | public TradeUserAssets(int appid, long contextid, ulong assetid, int amount = 1)
145 | {
146 | this.appid = appid;
147 | this.contextid = contextid;
148 | this.assetid = assetid;
149 | this.amount = amount;
150 | }
151 |
152 | public bool Equals(TradeUserAssets other)
153 | {
154 | return (CompareTo(other) == 0);
155 | }
156 |
157 | public override bool Equals(object other)
158 | {
159 | TradeUserAssets otherCasted = other as TradeUserAssets;
160 | return (otherCasted != null && Equals(otherCasted));
161 | }
162 |
163 | public override int GetHashCode()
164 | {
165 | return contextid.GetHashCode() ^ assetid.GetHashCode() ^ appid.GetHashCode() ^ amount.GetHashCode();
166 | }
167 |
168 | public int CompareTo(TradeUserAssets other)
169 | {
170 | if(appid != other.appid)
171 | return (appid < other.appid ? -1 : 1);
172 | if(contextid != other.contextid)
173 | return (contextid < other.contextid ? -1 : 1);
174 | if(assetid != other.assetid)
175 | return (assetid < other.assetid ? -1 : 1);
176 | if(amount != other.amount)
177 | return (amount < other.amount ? -1 : 1);
178 | return 0;
179 | }
180 |
181 | public override string ToString()
182 | {
183 | return string.Format("id:{0}, appid:{1}, contextid:{2}, amount:{3}",assetid,appid,contextid,amount);
184 | }
185 | }
186 |
187 | public enum TradeEventType
188 | {
189 | ItemAdded = 0, //itemid = "assetid"
190 | ItemRemoved = 1, //itemid = "assetid"
191 | UserSetReady = 2,
192 | UserSetUnReady = 3,
193 | UserAccept = 4,
194 | //5 = ?? Maybe some sort of cancel?
195 | ModifiedCurrency = 6,
196 | UserChat = 7 //message = "text"
197 | }
198 | }
--------------------------------------------------------------------------------
/SteamTrade/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/SteamTrade/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/triggerappveyor.txt:
--------------------------------------------------------------------------------
1 | yo appveyor, build me now.
2 | Trigger tests!
--------------------------------------------------------------------------------