mirror of
https://github.com/JustArchiNET/ArchiSteamFarm.git
synced 2025-12-31 13:40:46 +00:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d6c9fe3cde | ||
|
|
4b782bd10d | ||
|
|
1a1e48a33d | ||
|
|
f7d7c559b8 | ||
|
|
602cda73b7 | ||
|
|
f579462f60 | ||
|
|
8066f1a7c0 | ||
|
|
f8409e1be6 | ||
|
|
c36eeb8c28 | ||
|
|
d0344a7ab9 | ||
|
|
2c767bfe85 | ||
|
|
2816ecaa90 | ||
|
|
214746bca2 |
@@ -75,10 +75,7 @@ namespace ArchiSteamFarm {
|
||||
return;
|
||||
}
|
||||
|
||||
Notifications = new HashSet<ENotification>();
|
||||
foreach (CMsgClientUserNotifications.Notification notification in msg.notifications) {
|
||||
Notifications.Add((ENotification) notification.user_notification_type);
|
||||
}
|
||||
Notifications = new HashSet<ENotification>(msg.notifications.Select(notification => (ENotification) notification.user_notification_type));
|
||||
}
|
||||
|
||||
internal NotificationsCallback(JobID jobID, CMsgClientItemAnnouncements msg) {
|
||||
|
||||
@@ -345,14 +345,48 @@ namespace ArchiSteamFarm {
|
||||
return response;
|
||||
}
|
||||
|
||||
internal async Task<bool> HandleConfirmations(string deviceID, string confirmationHash, uint time, HashSet<MobileAuthenticator.Confirmation> confirmations, bool accept) {
|
||||
if (string.IsNullOrEmpty(deviceID) || string.IsNullOrEmpty(confirmationHash) || (time == 0) || (confirmations == null) || (confirmations.Count == 0)) {
|
||||
Logging.LogNullError(nameof(deviceID) + " || " + nameof(confirmationHash) + " || " + nameof(time) + " || " + nameof(confirmations), Bot.BotName);
|
||||
return false;
|
||||
internal async Task<bool?> HandleConfirmation(string deviceID, string confirmationHash, uint time, uint confirmationID, ulong confirmationKey, bool accept) {
|
||||
if (string.IsNullOrEmpty(deviceID) || string.IsNullOrEmpty(confirmationHash) || (time == 0) || (confirmationID == 0) || (confirmationKey == 0)) {
|
||||
Logging.LogNullError(nameof(deviceID) + " || " + nameof(confirmationHash) + " || " + nameof(time) + " || " + nameof(confirmationID) + " || " + nameof(confirmationKey), Bot.BotName);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
string request = SteamCommunityURL + "/mobileconf/ajaxop?op=" + (accept ? "allow" : "cancel") + "&p=" + deviceID + "&a=" + SteamID + "&k=" + WebUtility.UrlEncode(confirmationHash) + "&t=" + time + "&m=android&tag=conf&cid=" + confirmationID + "&ck=" + confirmationKey;
|
||||
|
||||
string json = await WebBrowser.UrlGetToContentRetry(request).ConfigureAwait(false);
|
||||
if (string.IsNullOrEmpty(json)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Steam.ConfirmationResponse response;
|
||||
|
||||
try {
|
||||
response = JsonConvert.DeserializeObject<Steam.ConfirmationResponse>(json);
|
||||
} catch (JsonException e) {
|
||||
Logging.LogGenericException(e, Bot.BotName);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (response != null) {
|
||||
return response.Success;
|
||||
}
|
||||
|
||||
Logging.LogNullError(nameof(response), Bot.BotName);
|
||||
return null;
|
||||
}
|
||||
|
||||
internal async Task<bool?> HandleConfirmations(string deviceID, string confirmationHash, uint time, HashSet<MobileAuthenticator.Confirmation> confirmations, bool accept) {
|
||||
if (string.IsNullOrEmpty(deviceID) || string.IsNullOrEmpty(confirmationHash) || (time == 0) || (confirmations == null) || (confirmations.Count == 0)) {
|
||||
Logging.LogNullError(nameof(deviceID) + " || " + nameof(confirmationHash) + " || " + nameof(time) + " || " + nameof(confirmations), Bot.BotName);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
string request = SteamCommunityURL + "/mobileconf/multiajaxop";
|
||||
@@ -374,7 +408,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
string json = await WebBrowser.UrlPostToContentRetry(request, data).ConfigureAwait(false);
|
||||
if (string.IsNullOrEmpty(json)) {
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
Steam.ConfirmationResponse response;
|
||||
@@ -383,7 +417,7 @@ namespace ArchiSteamFarm {
|
||||
response = JsonConvert.DeserializeObject<Steam.ConfirmationResponse>(json);
|
||||
} catch (JsonException e) {
|
||||
Logging.LogGenericException(e, Bot.BotName);
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (response != null) {
|
||||
@@ -391,7 +425,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
Logging.LogNullError(nameof(response), Bot.BotName);
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
internal async Task<Dictionary<uint, string>> GetOwnedGames() {
|
||||
|
||||
@@ -780,10 +780,10 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
// Remove from our pending inventory all items that are not steam cards and boosters
|
||||
inventory.RemoveWhere(item => (item.Type != Steam.Item.EType.TradingCard) && (item.Type != Steam.Item.EType.FoilTradingCard) && (item.Type != Steam.Item.EType.BoosterPack));
|
||||
|
||||
if (inventory.Count == 0) {
|
||||
return "Nothing to send, inventory seems empty!";
|
||||
if (inventory.RemoveWhere(item => (item.Type != Steam.Item.EType.TradingCard) && ((item.Type != Steam.Item.EType.FoilTradingCard) || !BotConfig.IsBotAccount) && (item.Type != Steam.Item.EType.BoosterPack)) > 0) {
|
||||
if (inventory.Count == 0) {
|
||||
return "Nothing to send, inventory seems empty!";
|
||||
}
|
||||
}
|
||||
|
||||
if (!await ArchiWebHandler.SendTradeOffer(inventory, BotConfig.SteamMasterID, BotConfig.SteamTradeToken).ConfigureAwait(false)) {
|
||||
@@ -1040,8 +1040,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
bool alreadyHandled = false;
|
||||
foreach (Bot bot in Bots.Where(bot => (bot.Value != this) && bot.Value.SteamClient.IsConnected && ((result.Items.Count == 0) || result.Items.Keys.Any(packageID => !bot.Value.OwnedPackageIDs.Contains(packageID)))).OrderBy(bot => bot.Key).Select(bot => bot.Value)) {
|
||||
|
||||
foreach (Bot bot in Bots.Where(bot => (bot.Value != this) && bot.Value.SteamClient.IsConnected).OrderBy(bot => bot.Key).Select(bot => bot.Value).Where(bot => (result.Items.Count == 0) || result.Items.Keys.Any(packageID => !bot.OwnedPackageIDs.Contains(packageID)))) {
|
||||
ArchiHandler.PurchaseResponseCallback otherResult = await bot.ArchiHandler.RedeemKey(key).ConfigureAwait(false);
|
||||
if (otherResult == null) {
|
||||
response.Append(Environment.NewLine + "<" + bot.BotName + "> Key: " + key + " | Status: Timeout!");
|
||||
|
||||
@@ -32,6 +32,7 @@ using System.Linq;
|
||||
namespace ArchiSteamFarm {
|
||||
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
|
||||
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
|
||||
[SuppressMessage("ReSharper", "ConvertToConstant.Local")]
|
||||
[SuppressMessage("ReSharper", "ConvertToConstant.Global")]
|
||||
internal sealed class BotConfig {
|
||||
internal enum EFarmingOrder : byte {
|
||||
@@ -58,10 +59,6 @@ namespace ArchiSteamFarm {
|
||||
[JsonProperty]
|
||||
internal string SteamPassword { get; set; }
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
[SuppressMessage("ReSharper", "ConvertToConstant.Local")]
|
||||
private readonly CryptoHelper.ECryptoMethod PasswordFormat = CryptoHelper.ECryptoMethod.PlainText;
|
||||
|
||||
[JsonProperty]
|
||||
internal string SteamParentalPIN { get; set; } = "0";
|
||||
|
||||
@@ -128,6 +125,9 @@ namespace ArchiSteamFarm {
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
internal readonly HashSet<uint> GamesPlayedWhileIdle = new HashSet<uint>();
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
private readonly CryptoHelper.ECryptoMethod PasswordFormat = CryptoHelper.ECryptoMethod.PlainText;
|
||||
|
||||
internal static BotConfig Load(string filePath) {
|
||||
if (string.IsNullOrEmpty(filePath)) {
|
||||
Logging.LogNullError(nameof(filePath));
|
||||
|
||||
@@ -102,6 +102,21 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
}
|
||||
|
||||
internal bool ReplaceIfNeededWith(HashSet<T> items) {
|
||||
Lock.EnterUpgradeableReadLock();
|
||||
|
||||
try {
|
||||
if (HashSet.SetEquals(items)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ReplaceWith(items);
|
||||
return true;
|
||||
} finally {
|
||||
Lock.ExitUpgradeableReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
internal void ReplaceWith(IEnumerable<T> items) {
|
||||
Lock.EnterWriteLock();
|
||||
|
||||
|
||||
@@ -25,14 +25,13 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal sealed class GlobalDatabase {
|
||||
internal sealed class GlobalDatabase : IDisposable {
|
||||
private static readonly JsonSerializerSettings CustomSerializerSettings = new JsonSerializerSettings {
|
||||
Converters = new List<JsonConverter> {
|
||||
Converters = new List<JsonConverter>(2) {
|
||||
new IPAddressConverter(),
|
||||
new IPEndPointConverter()
|
||||
}
|
||||
@@ -56,7 +55,6 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
[SuppressMessage("ReSharper", "AutoPropertyCanBeMadeGetOnly.Local")]
|
||||
internal readonly InMemoryServerListProvider ServerListProvider = new InMemoryServerListProvider();
|
||||
|
||||
private readonly object FileLock = new object();
|
||||
@@ -91,7 +89,7 @@ namespace ArchiSteamFarm {
|
||||
return globalDatabase;
|
||||
}
|
||||
|
||||
private void OnServerListUpdated(object sender, EventArgs e) => Save();
|
||||
public void Dispose() => ServerListProvider.Dispose();
|
||||
|
||||
// This constructor is used when creating new database
|
||||
private GlobalDatabase(string filePath) : this() {
|
||||
@@ -104,11 +102,12 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
// This constructor is used only by deserializer
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Local")]
|
||||
private GlobalDatabase() {
|
||||
ServerListProvider.ServerListUpdated += OnServerListUpdated;
|
||||
}
|
||||
|
||||
private void OnServerListUpdated(object sender, EventArgs e) => Save();
|
||||
|
||||
private void Save() {
|
||||
string json = JsonConvert.SerializeObject(this, CustomSerializerSettings);
|
||||
if (string.IsNullOrEmpty(json)) {
|
||||
|
||||
@@ -24,36 +24,36 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using SteamKit2.Discovery;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal sealed class InMemoryServerListProvider : IServerListProvider {
|
||||
internal sealed class InMemoryServerListProvider : IDisposable, IServerListProvider {
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
[SuppressMessage("ReSharper", "FieldCanBeMadeReadOnly.Local")]
|
||||
private HashSet<IPEndPoint> Servers = new HashSet<IPEndPoint>();
|
||||
private readonly ConcurrentHashSet<IPEndPoint> Servers = new ConcurrentHashSet<IPEndPoint>();
|
||||
|
||||
internal event EventHandler ServerListUpdated = delegate { };
|
||||
|
||||
public Task<IEnumerable<IPEndPoint>> FetchServerListAsync() => Task.FromResult<IEnumerable<IPEndPoint>>(Servers);
|
||||
|
||||
public Task UpdateServerListAsync(IEnumerable<IPEndPoint> endpoints) {
|
||||
if (endpoints == null) {
|
||||
Logging.LogNullError(nameof(endpoints));
|
||||
public Task UpdateServerListAsync(IEnumerable<IPEndPoint> endPoints) {
|
||||
if (endPoints == null) {
|
||||
Logging.LogNullError(nameof(endPoints));
|
||||
return Task.Delay(0);
|
||||
}
|
||||
|
||||
Servers.Clear();
|
||||
foreach (IPEndPoint endpoint in endpoints) {
|
||||
Servers.Add(endpoint);
|
||||
HashSet<IPEndPoint> newServers = new HashSet<IPEndPoint>(endPoints);
|
||||
|
||||
if (!Servers.ReplaceIfNeededWith(newServers)) {
|
||||
return Task.Delay(0);
|
||||
}
|
||||
|
||||
ServerListUpdated(this, EventArgs.Empty);
|
||||
|
||||
return Task.Delay(0);
|
||||
}
|
||||
|
||||
public void Dispose() => Servers.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ using Newtonsoft.Json;
|
||||
namespace ArchiSteamFarm.JSON {
|
||||
internal static class GitHub {
|
||||
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
|
||||
[SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Local")]
|
||||
internal sealed class ReleaseResponse {
|
||||
#pragma warning disable 649
|
||||
internal sealed class Asset {
|
||||
|
||||
@@ -343,7 +343,6 @@ namespace ArchiSteamFarm.JSON {
|
||||
|
||||
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
|
||||
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
|
||||
[SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Local")]
|
||||
internal sealed class ConfirmationResponse { // Deserialized from JSON
|
||||
#pragma warning disable 649
|
||||
[JsonProperty(PropertyName = "success", Required = Required.Always)]
|
||||
@@ -355,7 +354,6 @@ namespace ArchiSteamFarm.JSON {
|
||||
|
||||
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
|
||||
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
|
||||
[SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Local")]
|
||||
internal sealed class ConfirmationDetails { // Deserialized from JSON
|
||||
internal enum EType : byte {
|
||||
Unknown,
|
||||
|
||||
@@ -36,7 +36,7 @@ using Newtonsoft.Json;
|
||||
namespace ArchiSteamFarm {
|
||||
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
|
||||
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
|
||||
internal sealed class MobileAuthenticator {
|
||||
internal sealed class MobileAuthenticator : IDisposable {
|
||||
internal sealed class Confirmation {
|
||||
internal readonly uint ID;
|
||||
internal readonly ulong Key;
|
||||
@@ -66,7 +66,7 @@ namespace ArchiSteamFarm {
|
||||
|
||||
private static short SteamTimeDifference;
|
||||
|
||||
internal bool HasCorrectDeviceID => !string.IsNullOrEmpty(DeviceID) && !DeviceID.Equals("ERROR"); // "ERROR" is being used by SteamDesktopAuthenticator
|
||||
private readonly SemaphoreSlim ConfirmationsSemaphore = new SemaphoreSlim(1);
|
||||
|
||||
#pragma warning disable 649
|
||||
[JsonProperty(PropertyName = "shared_secret", Required = Required.Always)]
|
||||
@@ -81,9 +81,9 @@ namespace ArchiSteamFarm {
|
||||
|
||||
private Bot Bot;
|
||||
|
||||
private MobileAuthenticator() {
|
||||
internal bool HasCorrectDeviceID => !string.IsNullOrEmpty(DeviceID) && !DeviceID.Equals("ERROR"); // "ERROR" is being used by SteamDesktopAuthenticator
|
||||
|
||||
}
|
||||
private MobileAuthenticator() { }
|
||||
|
||||
internal void Init(Bot bot) {
|
||||
if (bot == null) {
|
||||
@@ -113,19 +113,45 @@ namespace ArchiSteamFarm {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint time = await GetSteamTime().ConfigureAwait(false);
|
||||
if (time == 0) {
|
||||
Logging.LogNullError(nameof(time), Bot.BotName);
|
||||
return false;
|
||||
}
|
||||
await ConfirmationsSemaphore.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
string confirmationHash = GenerateConfirmationKey(time, "conf");
|
||||
if (!string.IsNullOrEmpty(confirmationHash)) {
|
||||
return await Bot.ArchiWebHandler.HandleConfirmations(DeviceID, confirmationHash, time, confirmations, accept).ConfigureAwait(false);
|
||||
}
|
||||
try {
|
||||
uint time = await GetSteamTime().ConfigureAwait(false);
|
||||
if (time == 0) {
|
||||
Logging.LogNullError(nameof(time), Bot.BotName);
|
||||
return false;
|
||||
}
|
||||
|
||||
Logging.LogNullError(nameof(confirmationHash), Bot.BotName);
|
||||
return false;
|
||||
string confirmationHash = GenerateConfirmationKey(time, "conf");
|
||||
if (string.IsNullOrEmpty(confirmationHash)) {
|
||||
Logging.LogNullError(nameof(confirmationHash), Bot.BotName);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool? result = await Bot.ArchiWebHandler.HandleConfirmations(DeviceID, confirmationHash, time, confirmations, accept).ConfigureAwait(false);
|
||||
if (!result.HasValue) { // Request timed out
|
||||
return false;
|
||||
}
|
||||
|
||||
if (result.Value) { // Request succeeded
|
||||
return true;
|
||||
}
|
||||
|
||||
// Our multi request failed, this is almost always Steam fuckup that happens randomly
|
||||
// In this case, we'll accept all pending confirmations one-by-one, synchronously (as Steam can't handle them in parallel)
|
||||
// We totally ignore actual result returned by those calls, abort only if request timed out
|
||||
|
||||
foreach (Confirmation confirmation in confirmations) {
|
||||
bool? confirmationResult = await Bot.ArchiWebHandler.HandleConfirmation(DeviceID, confirmationHash, time, confirmation.ID, confirmation.Key, accept).ConfigureAwait(false);
|
||||
if (!confirmationResult.HasValue) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} finally {
|
||||
ConfirmationsSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task<Steam.ConfirmationDetails> GetConfirmationDetails(Confirmation confirmation) {
|
||||
@@ -334,7 +360,9 @@ namespace ArchiSteamFarm {
|
||||
hash = hmac.ComputeHash(buffer);
|
||||
}
|
||||
|
||||
return Convert.ToBase64String(hash, Base64FormattingOptions.None);
|
||||
return Convert.ToBase64String(hash);
|
||||
}
|
||||
|
||||
public void Dispose() => ConfirmationsSemaphore.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace ArchiSteamFarm {
|
||||
WCFHostname
|
||||
}
|
||||
|
||||
internal const string VersionNumber = "2.1.4.2";
|
||||
internal const string VersionNumber = "2.1.4.4";
|
||||
internal const string Copyright = "Copyright © ArchiSteamFarm 2015-2016";
|
||||
|
||||
internal const string GithubRepo = "JustArchi/ArchiSteamFarm";
|
||||
|
||||
@@ -26,10 +26,12 @@ using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal static class Utilities {
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[SuppressMessage("ReSharper", "UnusedParameter.Global")]
|
||||
internal static void Forget(this Task task) { }
|
||||
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
# Contributing
|
||||
|
||||
Before making an issue or pull request, you should carefully read **[ASF wiki](https://github.com/JustArchi/ArchiSteamFarm/wiki)** first.
|
||||
Before making an issue or pull request, you should carefully read **[ASF wiki](https://github.com/JustArchi/ArchiSteamFarm/wiki)** first. At least reading **[FAQ](https://github.com/JustArchi/ArchiSteamFarm/wiki/FAQ)** is mandatory.
|
||||
|
||||
## Issues
|
||||
|
||||
GitHub **[issues](https://github.com/JustArchi/ArchiSteamFarm/issues)** page is being used for ASF TODO list, regarding both features and bugs. It has rather strict policy - GitHub is not technical support and all cases that are not suggestions or bug reports should NOT be posted there. You have **[ASF chat](https://gitter.im/JustArchi/ArchiSteamFarm)** and **[Steam group](http://steamcommunity.com/groups/ascfarm/discussions/1/)** for general discussion, questions or technical issues. Please avoid using GitHub issues, unless you indeed want to report a bug or suggest an enhancement. Even prior to doing that, please make sure that you're indeed dealing with a bug, or your suggestion makes sense, preferably by asking on chat/steam group first. Invalid issues will be closed immediately.
|
||||
GitHub **[issues](https://github.com/JustArchi/ArchiSteamFarm/issues)** page is being used for ASF TODO list, regarding both features and bugs. It has rather strict policy - GitHub is NOT technical support and all cases that are not suggestions neither bug reports should NOT be posted there. You have **[ASF chat](https://gitter.im/JustArchi/ArchiSteamFarm)** and **[Steam group](http://steamcommunity.com/groups/ascfarm/discussions/1/)** for general discussion, questions or technical issues. Please avoid using GitHub issues, unless you indeed want to report a bug or suggest an enhancement. Even prior to doing that, please make sure that you're indeed dealing with a bug, or your suggestion makes sense, preferably by asking on chat/steam group first. Invalid issues will be closed immediately and won't be answered.
|
||||
|
||||
---
|
||||
|
||||
### Bugs
|
||||
|
||||
Posting a log is **mandatory**, regardless if it contains information that is relevant or not. You're allowed to make small modifications such as changing bot names to something more generic, but you should not be doing anything else. You want us to fix the bug you've encountered, then help us instead of making it harder - we're not being paid for that, and we're not forced to fix the bug you've encountered. Include as much relevant info as possible - if bug is reproducable, when it happens, if it's a result of a command - which one, does it happen always or only sometimes, with one account or all of them - everything you consider appropriate, that would help us reproduce the bug and fix it. The more information you include, the higher the chance of bug getting fixed. And this is probably what you want, right?
|
||||
Before reporting a bug you should carefully check if the "bug" you're encountering is in fact ASF bug and not technical issue that is answered in the **[FAQ](https://github.com/JustArchi/ArchiSteamFarm/wiki/FAQ#issues)** or in other place on the wiki. Typically technical issue is intentional ASF behaviour which might not match your expectations, e.g. failing to send or accept steam trades - logic for accepting and sending steam trades is outside of the ASF, as stated in the FAQ, and there is no bug related to that because it's up to Steam to accept such request, or not. If you're not sure if you're encountering ASF bug or technical issue, please use **[ASF chat](https://gitter.im/JustArchi/ArchiSteamFarm)** or **[Steam group](http://steamcommunity.com/groups/ascfarm/discussions/1/)** and avoid GitHub issues.
|
||||
|
||||
Regarding ASF bugs - Posting a log is **mandatory**, regardless if it contains information that is relevant or not. You're allowed to make small modifications such as changing bot names to something more generic, but you should not be doing anything else. You want us to fix the bug you've encountered, then help us instead of making it harder - we're not being paid for that, and we're not forced to fix the bug you've encountered. Include as much relevant info as possible - if bug is reproducable, when it happens, if it's a result of a command - which one, does it happen always or only sometimes, with one account or all of them - everything you consider appropriate, that would help us reproduce the bug and fix it. The more information you include, the higher the chance of bug getting fixed. If nobody is able to reproduce your bug, there is also no way of blindly fixing it.
|
||||
|
||||
It would also be cool if you could reproduce your issue on latest pre-release (and not stable) version, as this is most recent codebase that might include not-yet-released fix for your issue already. Of course, that is not mandatory, as ASF offers support for both latest pre-release as well as latest stable versions.
|
||||
|
||||
---
|
||||
|
||||
@@ -28,3 +32,8 @@ In general any pull request is welcome and should be accepted, unless there is a
|
||||
|
||||
Every pull request is carefully examined by our continuous integration system - it won't be accepted if it doesn't compile properly or causes any test to fail. We also expect that you at least barely tested the modification you're trying to add, and not blindly editing the file without even checking if it compiles. Consider the fact that you're not coding it only for yourself, but for thousands of users.
|
||||
|
||||
---
|
||||
|
||||
### Code style
|
||||
|
||||
Please stick with ASF code style when submitting PRs. In repo you can find standard **[VS settings](https://github.com/JustArchi/ArchiSteamFarm/blob/master/CodeStyle.vssettings)** file that you can use in Visual Studio for import. In addition to that, there is also **[DotSettings](https://github.com/JustArchi/ArchiSteamFarm/blob/master/ArchiSteamFarm.sln.DotSettings)** file for **[ReSharper](https://www.jetbrains.com/resharper/)** (optional). Consistency is the key.
|
||||
|
||||
Reference in New Issue
Block a user