While our code does not throw them, this is public helper and we don't need to enforce from other callers exceptions-less flow. We can use it for a failure.
As presented in the issue, we might end up in situation when parallel-processing and accepting two neutral+ trade offers will result in unwanted inventory state, because while they're both neutral+ and therefore OK to accept standalone, the combination of them both causes active badge progress degradation.
Considering the requirements we have, e.g. still processing trades in parallel, being performant, low on resources and with limited Steam servers overhead, the solution that I came up with in regards to this issue is quite simple:
- After we determine the trade to be neutral+, but before we tell the parse trade routine to accept it, we check if shared with other parallel processes set of handled sets contains any sets that we're currently processing.
- If no, we update that set to include everything we're dealing with, and tell the caller to accept this trade.
- If yes, we tell the caller to retry this trade after (other) accepted trades are confirmed and handled as usual.
This solves some issues and creates some optimistic assumptions:
- First of all, it solves the original issue, since if trade A and B both touch set S, then only one of them will be accepted. It's not deterministic which one (the one that gets to the check first), and not important anyway.
- We do not "lock" the sets before we determine that trade is neutral+, because otherwise unrelated users could spam us with non-neutral+ trades in order to lock the bot in infinite retry. This way they can't, as if the trade is determined to not be neutral+ then it never checks for concurrent processing.
- We are optimistic about resources usage. This routine could be made much more complicated to be more synchronous in order to avoid unnecessary calls to inventory and matching, however, that'd slow down the whole process only because the next call MAYBE will be determined as unneeded. Due to that, ASF is optimistic that trades will (usually) be unrelated, and can be processed in parallel, and if the conflict happens then simply we end up in a situation where we did some extra work for no reason, which is better than waiting with the work till all previous trades are processed.
- As soon as the conditions are met, the conflicting trades are retried to check if the conditions allow to accept them. If yes, they'll be accepted almost immediately after previous ones, if not, they'll be rejected as non-neutral+ anymore.
This way the additional code does not hurt the performance, parallel processing or anything else in usually expected optimistic scenarios, while adding some additional overhead in pessimistic ones, which is justified considering we don't want to degrade the badge progress.
After changes regarding to callbacks handling, we accidentally broke the reconnection logic. In particular, forced connection implicitly did disconnect with disconnect callback, but disconnect callback killed our callbacks handling loop for future connection since it was instructed to not reconnect... Pretty convulated logic.
Let's attempt to fix and simplify it. There is no forced connection concept anymore, but rather a new reconnect function which either, triggers reconnection through usual disconnection logic, or connects in edge case if we attempted to reconnect with already disconnnected client.
This way the status transition is more predictable, as we Connect() only in 3 cases:
- Initial start, including !start command, when we actually spawn the callbacks handling loop
- Upon disconnection, if we're configured to reconnect
- Reconnection, in case we're already disconnected and can't use above
And we use reconnect when:
- Failure in heartbeats to detect disconnections sooner
- Failure in refreshing access tokens, since if we lose our refresh token then the only way to get a new one is to reconnect
And finally disconnect is triggered when:
- Stopping the bot, especially !stop
- Bulletproofing against trying to connect when !KeepRunning and likewise
- Usual Steam maintenance and other network issues (which usually trigger reconnection)
The codebase is too huge to analyze every possible edge case, but with this logic I can no longer reproduce the previous issue
This is some next-level race condition, so for those interested:
- Concurrent collections are thread-safe in a way that each operation is atomic
- Naturally if you call two atomic operations in a row, the result is no longer atomic, since there could be some changes between the first and the last
- Certain LINQ operations such as OrderBy(), Reverse(), ToArray(), among more, use internal buffer for operation with certain optimization that checks if input is ICollection, if yes, it calls Count and CopyTo(), for OrderBy in this example
- In result, such LINQ call is not guaranteed to be thread-safe, since it assumes those two calls to be atomic, while they're not in reality.
This issue is quite hard to spot in real applications, since it's not that easy to trigger it (you need to call the operation on ICollection and then have another thread modifying it while enumerating). This is probably why we've never had any real problem until I've discovered this madness with @Aareksio in entirely different project.
As a workaround, we'll explicitly convert some ICollection inputs to IEnumerable, in particular around OrderBy(), so the optimization is skipped and the result is not corrupted.
I've added unit tests which ensure this workaround works properly, and you can easily reproduce the problem by removing AsLinqThreadSafeEnumerable() in them.
See https://github.com/dotnet/runtime/discussions/50687 for more insight
I have no clue who thought that ignoring this issue is a good idea, at the very least concurrent collections should have opt-out mechanism from those optimizations, there is no reason for them to not do that.
It seems that ASP.NET is trying to create initialized WebRootPath if it doesn't exist yet. This might be unwanted, as user might want to explicitly disable www directory while still having interest in IPC. On top of that, ASF will outright crash if creating such directory will be impossible, e.g. because of insufficient permission.
It makes sense for us to check first if the directory exists - if not, we can omit it entirely, so ASP.NET will default to NullFileProvider and simply respond 404 to everything unhandled from the code perspective.
@SuperSandro2000 will resolve https://github.com/NixOS/nixpkgs/issues/312242 without a need of disabling IPC. In other words, you can use IPC with no www folder attached in order to still have ASF API and /swagger available. ASF will no longer crash in this scenario, it also won't try to create a directory on read-only filesystem.
When excessive amount of "missing amounts", so items in the set was missing on our side, there was a possibility for our logic to classify a good trade as bad one, because we didn't fill in enough holes, as the subtraction in the condition was calculated on each loop rather than once initially.
Since this could only worsen the neutrality score, but never improve it (as the amounts were sorted ascending), there was no abusive possibility due to that, only ignoring otherwise valid trades classifying them as worse than they were in reality.
* Misc.
* Fix ASF crash
* Remove warning about automatic update of custom plugins if there is only official plugins enabled
* Fix previous mistake
* Revert "Fix ASF crash"
This reverts commit 42209e93ce.
* Downgrade OpenTelemetry.Exporter.Prometheus.AspNetCore due to issues with latest version
* Add unit to asf_bot_farming_minutes_remaining
* Upgrade some packages released last night (already tested to work)
* Don't forget about unit suffix
* Add build and runtime information metrics
It is not recommended to include this information as labels in all
metrics. Instead, we add two special metrics with a constant value of
"1" and restrict those static pieces of information to them
* Remove module version from metrics as it does not work
* Apply feedback
* Deduplicate code
* Reference related issue in upstream repo
* Closes#3156
* Misc
* Misc
* Rewrite update mechanism ONCE AGAIN, this time to eradicate FSW
* Make creating debug directory non-fatal again, like it used to be
* Deduplicate code
* Remove dead code
* Print update cleanup just once
* Address remaining feedback, go back to _old and _new
* One more nice improvement
GamesToFarm is not used after farming has stopped, so this doesn't solve any bug or misbehaviour, but some plugin creators might prefer to have up-to-date state just in case.
commit a5dd19643edd71ba3bcf9d120cc0ef20c1904104
Author: Łukasz Domeradzki <JustArchi@JustArchi.net>
Date: Tue Mar 26 03:43:50 2024 +0100
How about this one
commit 7f44554a5433931339dc479a6101f942c5d5fb97
Author: Łukasz Domeradzki <JustArchi@JustArchi.net>
Date: Tue Mar 26 03:14:04 2024 +0100
Here as well
commit 8593cd169949dc5876c1a3c4e4561d2ca38d7350
Author: Łukasz Domeradzki <JustArchi@JustArchi.net>
Date: Tue Mar 26 03:13:36 2024 +0100
Okay
commit 9d17fee1f8f9bd3ae91f144761885d10e52b67ae
Author: Łukasz Domeradzki <JustArchi@JustArchi.net>
Date: Tue Mar 26 03:08:49 2024 +0100
Restore everything first
commit 2835332dabf17a9dcdea3fc4f75e0c650add622c
Author: Łukasz Domeradzki <JustArchi@JustArchi.net>
Date: Tue Mar 26 03:00:33 2024 +0100
Ah right
commit 85e2db40c8d6c184e5732724ea928486456767e4
Author: Łukasz Domeradzki <JustArchi@JustArchi.net>
Date: Tue Mar 26 02:59:31 2024 +0100
And this?
commit 974cffb61782a4dbc83dfd93a66a627a69d04fd9
Author: Łukasz Domeradzki <JustArchi@JustArchi.net>
Date: Tue Mar 26 02:33:52 2024 +0100
Docker improvements
commit 95f40803615f7056f59a522b068600dfbb87b4de
Author: Łukasz Domeradzki <JustArchi@JustArchi.net>
Date: Tue Mar 26 02:22:30 2024 +0100
Misc
commit 0f5b526c603d5cfe0f29b4f4b8420d01f76161fc
Author: Łukasz Domeradzki <JustArchi@JustArchi.net>
Date: Tue Mar 26 02:09:42 2024 +0100
Make bundled plugins variant-specific
Since we're including them as part of OS-specific builds, we can also limit their dependencies to those OSes exclusively.
Might help cut some unnecessary dependencies.
* Add Monitoring plugin
* Prepare pipeline
* Fix Rider stupidity
* Fix Windows build
* Remove translation files
* Apply feedback
* Add steam id as additional tag to metrics
* Apply feedback
* Add runtime metrics
* Fix my brain not braining
* Use extension methods to add instrumentation and Add monitoring for outbound HTTP traffic
* Upgrade OpenTelemetry.Extensions.Hosting to prerelease due to runtime exception
* Remove config and add file that was supposed to be committed yesterday to fix the runtime exception
* Revert changes to publish.yml
* Remove localization
* Apply feedback
* Apply feedback
* Fix version number
* Revert use of property in Kestrel (even tho it's an outside caller to the source class)
Even if we have it always available, we don't need use it 99.9% of time, and even in 0.1% it's only supportive attribute for debugging. Make it optional, will help with robustness.
It makes sense to expose entire underlying asset to the callers, as underlying body might have features they like, such as currencyid or est_usd - values that do not exist in json and we're not making use of them, but we still want to keep if provided e.g. by ArchiHandler.
In rare occurances, we might not have a description assigned to the item. This is most notable in inactive trade offers, but we permit this to happen even in inventory fetches.
Assigning "default" description is unwanted if caller wants to have a way to determine that description wasn't there to begin with. It makes more sense to make it nullable and *expect* it to be null, then caller can do appropriate checking and decide what they want to do with that.
Also open constructors for plugins usage in case they'd like to construct assets manually, e.g. for sending.
* New inventory fetching
* use new method everywhere
* Store description in the asset, add protobuf body as a backing field for InventoryDescription, add properties to description
* parse trade offers as json, stub descriptions, fix build
* formatting, misc fixes
* fix pragma comments
* fix passing tradable property
* fix convesion of assets, add compatibility method
* fix fetching tradeoffers
* use 40k as default count per request
* throw an exception instead of silencing the error
* Initial implementation of plugin updates
* Update PluginsCore.cs
* Update IPluginUpdates.cs
* Update PluginsCore.cs
* Make it work
* Misc
* Revert "Misc"
This reverts commit bccd1bb2b8.
* Proper fix
* Make plugin updates independent of GitHub
* Final touches
* Misc
* Allow plugin creators for more flexibility in picking from GitHub releases
* Misc rename
* Make changelog internal again
This is ASF implementation detail, make body available instead and let people implement changelogs themselves
* Misc
* Add missing localization
* Add a way to disable plugin updates
* Update PluginsCore.cs
* Update PluginsCore.cs
* Misc
* Update IGitHubPluginUpdates.cs
* Update IGitHubPluginUpdates.cs
* Update IGitHubPluginUpdates.cs
* Update IGitHubPluginUpdates.cs
* Make zip selection ignore case
* Update ArchiSteamFarm/Core/Utilities.cs
Co-authored-by: Vita Chumakova <me@ezhevita.dev>
* Misc error notify
* Add commands and finally call it a day
* Misc progress percentages text
* Misc
* Flip DefaultPluginsUpdateMode as per the voting
* Misc
---------
Co-authored-by: Vita Chumakova <me@ezhevita.dev>
* Flash console window on input request
* Use BELL character instead of Beep, fix flash struct, add support for minimizing and flashing with Windows Terminal
* cross-platform minimization, use alert char instead of number, fix struct again
* remove console window
* formatting
* use MainWindowHandle if it's set (fix flashing winterm if ASF is launched in conhost)
* fix build
* remove support for flashing winterm
Previously WebApplication didn't offer any advantages over generic Host, but with release of .NET 8 there is now slim and empty builders, which limit amount of initialized dependencies and allow us to skip some unnecessary features in default pipeline.
* Good start
* Misc
* Make ApiAuthenticationMiddleware use new json
* Remove first newtonsoft dependency
* Pull latest ASFB json enhancements
* Start reimplementing newtonsoft!
* One thing at a time
* Keep doing all kind of breaking changes which need to be tested later
* Add back ShouldSerialize() support
* Misc
* Eradicate remaining parts of newtonsoft
* WIP
* Workaround STJ stupidity in regards to derived types
STJ can't serialize derived type properties by default, so we'll use another approach in our serializable file function
* Make CI happy
* Bunch of further fixes
* Fix AddFreeLicense() after rewrite
* Add full support for JsonDisallowNullAttribute
* Optimize our json utilities even further
* Misc
* Add support for fields in disallow null
* Misc optimization
* Fix deserialization of GlobalCache in STD
* Fix non-public [JsonExtensionData]
* Fix IM missing method exception, correct db storage helpers
* Fix saving into generic databases
Thanks STJ
* Make Save() function abstract to force inheritors to implement it properly
* Correct ShouldSerializeAdditionalProperties to be a method
* Misc cleanup
* Code review
* Allow JSON comments in configs, among other
* Allow trailing commas in configs
Users very often add them accidentally, no reason to throw on them
* Fix confirmation ID
Probably needs further fixes, will need to check later
* Correct confirmations deserialization
* Use JsonNumberHandling
* Misc
* Misc
* [JsonDisallowNull] corrections
* Forbid [JsonDisallowNull] on non-nullable structs
* Not really but okay
* Add and use ToJson() helpers
* Misc
* Misc
In order to keep compatibility with existing containers, we'll use the same paths for user-related overrides, that is, /app/config, /app/logs or /app/plugins. Instead, the only thing we'll do is moving ASF away from /app to new /asf directory, which will hopefully limit amount of screwups that users are doing within existing /app directory.
Also while at it, add symlink for a bit better integration.
- On start, we create/load crash file, if previous startup was less than 5 minutes ago, we also increase the counter
- If counter reaches 5, we abort the process, that is, freeze the process by not loading anything other than auto-updates
- In order for user to recover from above, he needs to manually remove ASF.crash file
- If process exits normally without reaching counter of 5 yet, we remove crash file (reset the counter), normal exit includes SIGTERM, SIGINT, clicking [X], !exit, !restart, and anything else that allows ASF to gracefully quit
- If process exits abnormally, that includes SIGKILL, unhandled exceptions, as well as power outages and anything that prevents ASF from graceful quit, we keep crash file around
- Update procedure, as an exception, allows crash file removal even with counter of 5. This should allow crash file recovery for people that crashed before, without a need of manual removal.
This addresses two things:
- It allows for better load-balancing, as STD refresh can be postponed for a short while after bot logs in - it has more important matters to handle right away, and STD is optional/supportive plugin.
- It helps @xPaw sleep better at night working around fools with their ASFs crashing thirty times per second due to third-party plugins.
> As of IdentityModel 7x, this is a legacy tool that should be replaced with Microsoft.IdentityModel.JsonWebTokens.
> This is a newer, faster version of System.IdentityModel.Tokens.Jwt that has additional functionality
Thanks to @xPaw findings, it seems that access token we get on logon can be used for all functionality we require in ASF. This means we no longer need to fetch the one from points shop in AWH and can safely remove that.
Since access token in AWH is public API, this commit:
- Makes Bot.AccessToken public API.
- Deprecates ArchiWebHandler.CachedAccessToken with intention of removal in the next version. Until then, it resolves to Bot.AccessToken internally so all plugins can keep working during transition period.
- Deprecates Utilities.ReadJwtToken(), probably nobody else than me used it, just switch over to Utilities.TryReadJwtToken(), much better design.
- Reverts ArchiCacheable parts back to stable API, as we no longer need the breaking change done in #3133
After investigation, it turns out that the token actually has correct scope (THANK GOD), it's the fact that Valve started issuing those on much shorter notice than our cache.
Up until now, we played it smartly by assuming cached access token should be valid for at least 6 hours, since every time we visited the page, we got a new token that was valid for 24h since issuing. This however is no longer the case and Valve seems to recycle the same token for every request now, probably until we get close to its expiration. This also means that with unlucky timing, we might be trying to use access token that has expired already even for up to 6 more hours, which is unwanted and causes all kind of issues, with 403 in trade offers being one of them.
I could make stupid solution and cache token for shorter, e.g. 1 minute, but instead I did 200 IQ move and rewrote the functionality in a way to actually parse that token, its validity, and set the cache to be valid for a brief moment before the token actually expires. This way, we're not only more efficient (we can cache the token even for 24h if needed), but we're also invalidating it as soon as it goes out of the scope.
Selected NU190x warnings can happen retroactively when given library is found with vulnerabilities. While this is important for development and for building, we should not retroactively cause selected git tags fail to build purely because a package we references was found to be vulnerable - warning during build is sufficient.
Resolves https://aur.archlinux.org/packages/asf and other sources trying to build older tag such as 5.5.1.4 as of today. Will apply from future release naturally.
All of them are common enough to be contained into a single flags property, this will vastly improve readability of the bot config, among being ready to add more properties in the future without polluting it.
Also hooray for 6 bytes less of memory usage of each bot, glorious.
Those are usually stash accounts, and while we still want to match them, we can leave them only as a last resort if no other bots are available.
This decreases chance of hitting a bot that was just recently turned off or had its items traded away, as what usually happens with such accounts.
It was possible before if the inventory state was the same as previously announced, even if server purged the info long time ago. Also, add required logic for recovery if that happens regardless.
We can keep inventory checksum before deduplication in the cache. If it's determined to be the same, then our inventory state didn't change, so it also doesn't make much sense to ask server for set parts and announcement.
* Initial implementation of announce with diff
* Add missing logic pieces
* Change in logic
* Fix checksums
* Add deduplication logic
* Update SetPart.cs
* Use standalone endpoint for diff
* Use different hashcode impl
* Update AssetForListing.cs
* Misc
* Push all the changes for this to finally work
* Use original index rather than self-calculated
ASFB makes some calculations based on index, it's better for us to have holes rather than hiding skipped items.
* Handle edge case of no assets after deduplication
* Remove dead code
* Address trim warnings
* Misc optimization
--no-restore seems to cause trimming to not run when it wasn't originally specified when doing build. Originally we were building ASFs in parallel for each variant, so it made sense, now with better CI-powered parallelism, --no-restore makes only sense when publishing plugins, as indeed in those places we're doing stuff in parallel which restore doesn't like.
This should resolve trimming not triggering for OS-specific builds.
We intend to give the caller best result, operation canceled has no value for him, he can check if cancellation token he provided himself got canceled or not
* Initial .NET 8
* Make it compile in release mode ignoring warnings for now
* First round of improvements
* Second round of improvements
* Third round of improvements
* Use new throws
* Fix .NET Framework, YAY, thanks madness!
Madness devs are awesome
* Misc
* Misc
* AF_NETLINK might be required for some http calls
No clue why
* Fix service files
Doesn't do what it should
* Update CardsFarmer.cs
* New improvements
* Address feedback
* Misc
* Misc
* Misc refactor
* Misc
* Add GetClanChatInfo as public API function
* make JoinChatRoomGroup public API. add LeaveChatRoomGroup as public API
* Rename GetClanChatRoomInfo method. Change return type of GetClanChatRoomInfo method
We handled a situation where linux terminal is closing STDIN durng interactive console. This handles even further edge case where linux terminal is closing STDIN during user masked input.
* Implement support for access tokens
A bit more work and testing is needed
* Make ValidUntil computed, fix netf, among others
* netf fixes as always
* Allow AWH to forcefully refresh session
* Unify access token lifetime
AF_UNIX is apparently needed on arch
AF_NETLINK will be mandatory since .NET 8, but based on my research even .NET 7 uses it in some conditions, so it makes sense to patch it right away as our previous settings were too restrictive, even if it did in fact work
Make it so the design actually follows what Steam gives us now. There is no need for standalone Confirmation object anymore, rather re-use what Steam gives us. Optimize parsing type, expose it as public API. Small breaking change in HandleConfirmations() action.
@@ -9,7 +9,7 @@ Before making an issue or pull request, you should carefully read **[ASF wiki](h
Examples of **invalid** issues:
- Asking how to install the program or use one of its functions
- Having technical difficulties running the program in some environment, encountering expected issues caused by the user's neglect
- Reporting problems that are not caused by ASF, such as ASF-ui issues or Steam not allowing you to log in
- Reporting problems that are not caused by ASF, such as ASF-ui issues or Steam not allowing you to send trade offers
- All issues encountered while running ASF in unsupported environment/setup, such as those with modified ASF source, having more bots than our maximum recommended limit or using custom plugins
- Other activities that are not related to ASF development in any way and do not require any development action from us
- label:I also read **[Setting-up](https://github.com/JustArchiNET/ArchiSteamFarm/wiki/Setting-up)** and **[FAQ](https://github.com/JustArchiNET/ArchiSteamFarm/wiki/FAQ)**, I don't need **[help](https://github.com/JustArchiNET/ArchiSteamFarm/blob/main/.github/SUPPORT.md)**, this is a bug report
required:true
- label:This is not a **[duplicate](https://github.com/JustArchiNET/ArchiSteamFarm/issues?q=is%3Aissue)** of an existing issue
required:true
- label:I don't own more than **[10 accounts in total](https://github.com/JustArchiNET/ArchiSteamFarm/wiki/FAQ#how-many-bots-can-i-run-with-asf)**
required:true
- label:I'm not using **[custom plugins](https://github.com/JustArchiNET/ArchiSteamFarm/wiki/Plugins)**
@@ -42,12 +44,12 @@ body:
- docker-linux/arm/v7
- docker-linux/arm64
- generic (with latest .NET runtime)
- generic-netf (with latest Mono runtime)
- linux-arm
- linux-arm64
- linux-x64
- osx-arm64
- osx-x64
- win-arm64
- win-x64
validations:
required:true
@@ -147,6 +149,7 @@ body:
Ensure that your config has redacted (but NOT removed) potentially-sensitive properties, such as:
- IPCPassword (recommended)
- LicenseID (mandatory)
- SteamOwnerID (optionally)
- WebProxy (optionally, if exposing private details)
- WebProxyPassword (optionally, if exposing private details)
- label:I also read **[Setting-up](https://github.com/JustArchiNET/ArchiSteamFarm/wiki/Setting-up)** and **[FAQ](https://github.com/JustArchiNET/ArchiSteamFarm/wiki/FAQ)**, I don't need **[help](https://github.com/JustArchiNET/ArchiSteamFarm/blob/main/.github/SUPPORT.md)**, this is an enhancement idea
required:true
- label:This is not a **[duplicate](https://github.com/JustArchiNET/ArchiSteamFarm/issues?q=is%3Aissue)** of an existing issue
required:true
- label:My idea doesn't duplicate existing ASF functionality described on the **[wiki](https://github.com/JustArchiNET/ArchiSteamFarm/wiki)**
required:true
- label:I believe that my idea falls into ASF's scope and should be offered as part of ASF built-in functionality
**Pre-releases are experimental versions that often contain unpatched bugs, work-in-progress features and rewritten implementations. If you don't consider yourself advanced user, please download **[latest stable release](https://github.com/JustArchiNET/ArchiSteamFarm/releases/latest)** instead. Pre-release versions are dedicated to users who know how to report bugs, deal with issues and give feedback - no technical support will be given. Check out ASF **[release cycle](https://github.com/JustArchiNET/ArchiSteamFarm/wiki/Release-cycle)** if you'd like to learn more.**
**Pre-releases are test versions that often contain unpatched bugs, work-in-progress features and rewritten implementations. If you don't consider yourself advanced user, please download **[latest stable release](https://github.com/JustArchiNET/ArchiSteamFarm/releases/latest)** instead. Pre-release versions are dedicated to users who know how to report bugs, deal with issues and give feedback - no technical support will be given. Check out ASF **[release cycle](https://github.com/JustArchiNET/ArchiSteamFarm/wiki/Release-cycle)** if you'd like to learn more.**
---
@@ -14,4 +14,6 @@ This is automated GitHub deployment, human-readable changelog should be availabl
ASF is available for free, this release was made possible thanks to the people that decided to support the project. If you're grateful for what we're doing, please consider a donation. Developing ASF requires massive amount of time and knowledge, especially when it comes to Steam (and its problems). Even $1 is highly appreciated and shows that you care. Thank you!
Our **[wiki](https://github.com/JustArchiNET/ArchiSteamFarm/wiki)** is the official online documentation which covers at least a significant majority (if not all) of ASF subjects you could be interested in. We recommend to start with **[setting up](https://github.com/JustArchiNET/ArchiSteamFarm/wiki/Setting-up)**, **[configuration](https://github.com/JustArchiNET/ArchiSteamFarm/wiki/Configuration)** and our **[FAQ](https://github.com/JustArchiNET/ArchiSteamFarm/wiki/FAQ)** which should help you with setting up ASF, configuring it, as well as answering the most common questions that you might have. For more advanced matters, as well as further elaboration, we have other pages available on our **[wiki](https://github.com/JustArchiNET/ArchiSteamFarm/wiki)** that you can visit.
We also have three independent support channels dedicated to our ASF users, in case you couldn't manage to solve the issue yourself. We answer all support and technical matters in our **[GitHub discussions](https://github.com/JustArchiNET/ArchiSteamFarm/discussions/categories/support)**, **[Steam group](https://steamcommunity.com/groups/archiasf/discussions/1)**, and on our **[Discord server](https://discord.gg/hSQgt8j)**. You're free to use the support channel that matches your preferences, although keep in mind that you have a higher chance solving your issue on the GitHub or Steam, where we're doing our best to answer all questions that couldn't be answered by our community itself (as opposed to Discord server where we're not active 24/7 and therefore not always able to answer).
We also have three independent support channels dedicated to our ASF users, in case you couldn't manage to solve the issue yourself. We answer all support and technical matters in our **[GitHub discussions](https://github.com/JustArchiNET/ArchiSteamFarm/discussions/categories/support-english)**, **[Steam group](https://steamcommunity.com/groups/archiasf/discussions/1)**, and on our **[Discord server](https://discord.gg/hSQgt8j)**. You're free to use the support channel that matches your preferences, although keep in mind that you have a higher chance solving your issue on the GitHub or Steam, where we're doing our best to answer all questions that couldn't be answered by our community itself (as opposed to Discord server where we're not active 24/7 and therefore not always able to answer).
GitHub **issues** (unlike discussions), are being used solely for ASF development, especially in regards to bugs and enhancements. We have a very strict policy regarding that, as GitHub issues is **not** a general support channel, it's dedicated exclusively to ASF development and we're not answering common ASF matters there, as we have appropriate support channels (mentioned above) for that. Common matters include not only general questions or issues that are obviously related to program usage, but also users reporting "bugs" that are clearly considered intended behaviour coming for example (and mainly) from misconfiguration or lack of understanding how the program works. If you're not sure whether your matter relates to ASF development or not, especially if you're not sure if it's a bug or intended behaviour, we recommend to use a support channel instead, where we'll answer you in calm atmosphere and forward your matter as GitHub issue if deemed appropriate. Invalid GitHub issues will be closed immediately and won't be answered.
PLUGINS_INCLUDED:ArchiSteamFarm.OfficialPlugins.Monitoring# Apart from declaring them here, there is certain amount of hardcoding needed below for uploading
// Your plugin class should inherit the plugin interfaces it wants to handle
// If you do not want to handle a particular action (e.g. OnBotMessage that is offered in IBotMessage), it's the best idea to not inherit it at all
// This will keep your code compact, efficient and less dependent. You can always add additional interfaces when you'll need them, this example project will inherit quite a bit of them to show you potential usage
// This is used for identification purposes, typically you want to use a friendly name of your plugin here, such as the name of your main class
// Please note that this property can have direct dependencies only on structures that were initialized by the constructor, as it's possible to be called before OnLoaded() takes place
[JsonInclude]
[Required]
publicstringName=>nameof(ExamplePlugin);
// This will be displayed to the user and written in the log file, typically you should point it to the version of your library, but alternatively you can do some more advanced logic if you'd like to
// Please note that this property can have direct dependencies only on structures that were initialized by the constructor, as it's possible to be called before OnLoaded() takes place
// This method, apart from being called before any bot initialization takes place, allows you to read custom global config properties that are not recognized by ASF
// Thanks to that, you can extend default ASF config with your own stuff, then parse it here in order to customize your plugin during runtime
// Keep in mind that, as noted in the interface, additionalConfigProperties can be null if no custom, unrecognized properties are found by ASF, you should handle that case appropriately
// In addition to that, this method also guarantees that all plugins were already OnLoaded(), which allows cross-plugins-communication to be possible
// It's a good idea to prefix your custom properties with the name of your plugin, so there will be no possible conflict of ASF or other plugins using the same name, neither now or in the future
// This method, apart from being called during bot modules initialization, allows you to read custom bot config properties that are not recognized by ASF
// Thanks to that, you can extend default bot config with your own stuff, then parse it here in order to customize your plugin during runtime
// Keep in mind that, as noted in the interface, additionalConfigProperties can be null if no custom, unrecognized properties are found by ASF, you should handle that case appropriately
// Also keep in mind that this function can be called multiple times, e.g. when user edits his bot configs during runtime
// Also keep in mind that this function can be called multiple times, e.g. when user edits their bot configs during runtime
// Take a look at OnASFInit() for example parsing code
// For example, we'll ensure that every bot starts paused regardless of Paused property, in order to do this, we'll just call Pause here in InitModules()
// Thanks to the fact that this method is called with each bot config reload, we'll ensure that our bot stays paused even if it'd get unpaused otherwise
bot.ArchiLogger.LogGenericInfo("Pausing this bot as asked from the plugin");
// This is the earliest method that will be called, right after loading the plugin, long before any bot initialization takes place
// It's a good place to initialize all potential (non-bot-specific) structures that you will need across lifetime of your plugin, such as global timers, concurrent dictionaries and alike
<value>Neke konfirmacije su neuspješne, odprilike {0} od {1} razmjena je poslano uspješno.</value>
<comment>{0} will be replaced by amount of the trade offers that succeeded (number), {1} will be replaced by amount of the trade offers that were supposed to be sent in total (number)</comment>
<value>Gebe Benutzerkonto {0} ({1}) mit insgesamt aus {2} Gegenständen bestehendem Inventar bekannt...</value>
<value>Benutzerkonto {0} ({1}) mit aus insgesamt {2} Gegenständen bestehendem Inventar wird angekündigt...</value>
<comment>{0} will be replaced by steam ID (number), {1} will be replaced by user's nickname, {2} will be replaced with number of items in the inventory</comment>
</data>
<data name="MatchingFound" xml:space="preserve">
@@ -75,11 +73,11 @@
<comment>{0} will be replaced by number of items matched, {1} will be replaced by steam ID (number), {2} will be replaced by user's nickname</comment>
<value>Einige Bestätigungen sind fehlgeschlagen, etwa {0} von {1} Transaktionen wurden erfolgreich versendet.</value>
<value>Einige Bestätigungen sind fehlgeschlagen. Lediglich {0} von {1} Transaktionen wurden erfolgreich versendet.</value>
<comment>{0} will be replaced by amount of the trade offers that succeeded (number), {1} will be replaced by amount of the trade offers that were supposed to be sent in total (number)</comment>
<value>Annonce de {0} ({1}) avec l'inventaire réalisé à partir de {2} au total sur la liste...</value>
<comment>{0} will be replaced by steam ID (number), {1} will be replaced by user's nickname, {2} will be replaced with number of items in the inventory</comment>
</data>
<data name="MatchingFound" xml:space="preserve">
<value>Correspondance totale de {0} éléments avec le bot {1} ({2}), envoi de l'offre d'échange...</value>
<comment>{0} will be replaced by number of items matched, {1} will be replaced by steam ID (number), {2} will be replaced by user's nickname</comment>
<value>Certaines confirmations ont échoué, environ {0} sur les transactions {1} ont été envoyées avec succès.</value>
<comment>{0} will be replaced by amount of the trade offers that succeeded (number), {1} will be replaced by amount of the trade offers that were supposed to be sent in total (number)</comment>
<value>חלק מהאישורים נכשלו, בערך {0} מתוך {1} עסקאות נשלחו בהצלחה.</value>
<comment>{0} will be replaced by amount of the trade offers that succeeded (number), {1} will be replaced by amount of the trade offers that were supposed to be sent in total (number)</comment>
<value>Alcune conferme sono fallite, circa {0} su {1} sono state inviate con successo.</value>
<comment>{0} will be replaced by amount of the trade offers that succeeded (number), {1} will be replaced by amount of the trade offers that were supposed to be sent in total (number)</comment>
<value>Kunngjør {0} ({1}) med inventar laget av totalt {2} gjenstander på oppføringen...</value>
<comment>{0} will be replaced by steam ID (number), {1} will be replaced by user's nickname, {2} will be replaced with number of items in the inventory</comment>
</data>
<data name="MatchingFound" xml:space="preserve">
<value>Matchet totalt {0} gjenstander med bot {1} ({2}), sender byttehandel...</value>
<comment>{0} will be replaced by number of items matched, {1} will be replaced by steam ID (number), {2} will be replaced by user's nickname</comment>
<value>Noen bekreftelser har mislyktes, ca {0} av {1} byttehandler ble sendt.</value>
<comment>{0} will be replaced by amount of the trade offers that succeeded (number), {1} will be replaced by amount of the trade offers that were supposed to be sent in total (number)</comment>
<value>Anunțând {0} ({1}) cu inventarul făcut din {2} articole în total pe listă...</value>
<comment>{0} will be replaced by steam ID (number), {1} will be replaced by user's nickname, {2} will be replaced with number of items in the inventory</comment>
</data>
<data name="MatchingFound" xml:space="preserve">
<value>Se potrivește cu un total de {0} elemente cu botul {1} ({2}), se trimite oferta de schimb...</value>
<comment>{0} will be replaced by number of items matched, {1} will be replaced by steam ID (number), {2} will be replaced by user's nickname</comment>
<value>Unele confirmări au eșuat, aproximativ {0} din {1} de schimburi au fost trimise cu succes.</value>
<comment>{0} will be replaced by amount of the trade offers that succeeded (number), {1} will be replaced by amount of the trade offers that were supposed to be sent in total (number)</comment>
<value>Oznámenie {0} ({1}) s inventárom v ktorom je {2} položiek na zozname...</value>
<comment>{0} will be replaced by steam ID (number), {1} will be replaced by user's nickname, {2} will be replaced with number of items in the inventory</comment>
</data>
<data name="MatchingFound" xml:space="preserve">
<value>Zhodné s celkom {0} položiek s botom {1} ({2}), posielanie obchodnej ponuky...</value>
<comment>{0} will be replaced by number of items matched, {1} will be replaced by steam ID (number), {2} will be replaced by user's nickname</comment>
<value>Niektoré potvrdenia sa nepodarili, úspešne bolo poslaných približne {0} z {1} obchodov.</value>
<comment>{0} will be replaced by amount of the trade offers that succeeded (number), {1} will be replaced by amount of the trade offers that were supposed to be sent in total (number)</comment>
<comment>{0} will be replaced by steam ID (number), {1} will be replaced by user's nickname, {2} will be replaced with number of items in the inventory</comment>
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.