Compare commits

...

824 Commits

Author SHA1 Message Date
Archi
e4c20df4a8 Misc 2024-02-27 01:44:21 +01:00
Archi
5135677360 Add back missing STJ feature of non-standard Guid deserialization 2024-02-27 01:34:56 +01:00
Archi
f6004f558b Closes #3147 2024-02-27 00:42:32 +01:00
renovate[bot]
ddf08c4dc0 chore(deps): update asf-ui digest to f916fb3 2024-02-26 19:35:51 +00:00
ArchiBot
388eaf614d Automatic translations update 2024-02-26 02:05:51 +00:00
renovate[bot]
3a0768f9ef chore(deps): update asf-ui digest to 05de7b9 2024-02-25 21:40:17 +00:00
ArchiBot
cb0e04c022 Automatic translations update 2024-02-25 02:05:54 +00:00
Archi
55cdac205d Merge branch 'main' of https://github.com/JustArchiNET/ArchiSteamFarm 2024-02-24 18:39:16 +01:00
Archi
1bc0d6f16c Archi had too much coding lately 2024-02-24 18:39:09 +01:00
renovate[bot]
f7377a7043 chore(deps): update asf-ui digest to d0d90a5 2024-02-24 04:29:58 +00:00
ArchiBot
76f1ad45dd Automatic translations update 2024-02-24 02:03:45 +00:00
renovate[bot]
f21ffde803 chore(deps): update github/codeql-action action to v3.24.5 2024-02-23 16:55:14 +00:00
renovate[bot]
e9c96f175f chore(deps): update asf-ui digest to f2ef756 2024-02-23 14:02:04 +00:00
Archi
0f9a4c7c31 Update Commands.cs 2024-02-23 14:19:09 +01:00
Archi
87451615e8 Extract addlicense logic to actions 2024-02-23 14:14:16 +01:00
ArchiBot
fa19aaae2e Automatic translations update 2024-02-23 02:08:38 +00:00
Archi
6b8280fceb Bump 2024-02-23 02:49:20 +01:00
Archi
7a13895429 Fix false positives 2024-02-23 02:49:03 +01:00
Archi
75668ea099 Bump 2024-02-23 02:43:06 +01:00
Archi
d3aa881f55 Fix STD serialization after STJ changes 2024-02-23 02:42:18 +01:00
Archi
33a1fdf556 Misc 2024-02-23 01:18:09 +01:00
Archi
0c848ec366 Misc 2024-02-23 01:15:35 +01:00
Archi
21d4e46b81 Bump 2024-02-22 23:45:13 +01:00
Archi
7a21c7bc45 Don't copy all variant-specific files if ASFVariant is not declared 2024-02-22 23:44:06 +01:00
renovate[bot]
9358aa5d1f chore(deps): update github/codeql-action action to v3.24.4 2024-02-22 15:09:20 +00:00
Archi
6c9e9da740 Modernize unit tests 2024-02-22 16:08:54 +01:00
renovate[bot]
a78a607f73 chore(deps): update asf-ui digest to cd1173a 2024-02-22 03:20:14 +00:00
ArchiBot
879323ed20 Automatic translations update 2024-02-22 02:04:27 +00:00
renovate[bot]
4e081b26a1 chore(deps): update asf-ui digest to e055bd1 2024-02-21 13:02:17 +00:00
Archi
6cd3459dd4 Misc 2024-02-21 13:20:26 +01:00
Archi
482576f16d Add extra constructors also to other public collections 2024-02-21 13:18:23 +01:00
Archi
f63dbffee3 Add extra constructors 2024-02-21 13:08:32 +01:00
renovate[bot]
d9cdc806fe chore(deps): update wiki digest to 03614e1 2024-02-21 04:05:58 +00:00
Archi
36a78b55a4 Good idea 2024-02-21 03:46:25 +01:00
Archi
ab983099cc Misc 2024-02-21 03:32:14 +01:00
Archi
aab8f5f0b5 BIG BUMP 2024-02-21 03:11:32 +01:00
Łukasz Domeradzki
6b0bf0f9c1 Closes #3061 (#3145)
* 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
2024-02-21 03:09:36 +01:00
ArchiBot
3968130e15 Automatic translations update 2024-02-21 02:04:51 +00:00
ArchiBot
dd488a1c9c Automatic translations update 2024-02-20 02:03:29 +00:00
renovate[bot]
de3c332803 chore(deps): update asf-ui digest to 0e9cf35 2024-02-19 22:19:08 +00:00
renovate[bot]
684b7b6e28 chore(deps): update asf-ui digest to 7b31664 2024-02-19 16:19:48 +00:00
ArchiBot
d36f16e205 Automatic translations update 2024-02-18 02:05:50 +00:00
renovate[bot]
c3ab3d767f chore(deps): update asf-ui digest to 82412dc 2024-02-17 22:55:28 +00:00
renovate[bot]
a790c34976 chore(deps): update dependency markdig.signed to v0.35.0 2024-02-17 06:16:26 +00:00
ArchiBot
be208eab92 Automatic translations update 2024-02-17 02:04:16 +00:00
renovate[bot]
c9598709d6 chore(deps): update asf-ui digest to 6eca08a 2024-02-16 09:31:51 +00:00
ArchiBot
367e7f841c Automatic translations update 2024-02-16 02:05:09 +00:00
renovate[bot]
8c988cfed6 chore(deps): update asf-ui digest to eebc86f 2024-02-15 21:33:19 +00:00
renovate[bot]
5b15535661 chore(deps): update github/codeql-action action to v3.24.3 2024-02-15 14:21:59 +00:00
renovate[bot]
e0c692e0ab chore(deps): update wiki digest to 5ea73d0 2024-02-15 04:18:35 +00:00
Archi
1ddf6b34e2 Misc lang correction 2024-02-15 03:50:27 +01:00
ArchiBot
8476c8c221 Automatic translations update 2024-02-14 02:04:50 +00:00
renovate[bot]
24ec938565 chore(deps): update mstest monorepo to v3.2.1 2024-02-13 22:23:04 +00:00
renovate[bot]
0dbfba275e chore(deps): update asf-ui digest to 661a7d1 2024-02-13 19:08:50 +00:00
renovate[bot]
55c5c70b52 chore(deps): update github/codeql-action action to v3.24.1 2024-02-13 15:20:35 +00:00
renovate[bot]
17aa8297da chore(deps): update crowdin/github-action action to v1.19.0 2024-02-13 10:47:17 +00:00
ArchiBot
b0aa2a9104 Automatic translations update 2024-02-13 02:04:51 +00:00
Archi
898c402dfc Misc 2024-02-12 19:20:53 +01:00
Archi
9258819c84 Misc 2024-02-12 15:24:41 +01:00
ArchiBot
b5db0b511c Automatic translations update 2024-02-12 02:05:33 +00:00
renovate[bot]
7c6804449c chore(deps): update wiki digest to e004808 2024-02-11 21:50:56 +00:00
ArchiBot
ff28e2bf8f Automatic translations update 2024-02-11 02:06:02 +00:00
Archi
3510cd1be0 Bump 2024-02-10 18:31:26 +01:00
Archi
5320e8d9cc Misc refactor after #3142 2024-02-10 18:03:32 +01:00
LRFLEW
6ab9e2958d Use FixedTimeEquals for IPC Password testing (#3142) 2024-02-10 17:42:45 +01:00
ArchiBot
e47f286bda Automatic translations update 2024-02-10 02:03:17 +00:00
ArchiBot
84b1599ca6 Automatic translations update 2024-02-09 02:04:10 +00:00
Archi
c5396a8ec8 Update publish.yml 2024-02-08 12:43:42 +01:00
Archi
9a9f18184b Misc 2024-02-08 12:37:50 +01:00
Archi
10d97e16e3 Closes #3140 2024-02-08 12:31:43 +01:00
renovate[bot]
5ece500396 chore(deps): update asf-ui digest to 7406f71 2024-02-08 04:42:12 +00:00
ArchiBot
1a311513ca Automatic translations update 2024-02-08 02:04:11 +00:00
renovate[bot]
6e0e4835d1 chore(deps): update ncipollo/release-action action to v1.14.0 2024-02-07 16:15:35 +00:00
renovate[bot]
ae4a784c3a chore(deps): update asf-ui digest to 558b879 2024-02-07 13:01:40 +00:00
renovate[bot]
287be65e7f chore(deps): update crowdin/github-action action to v1.18.0 2024-02-07 10:34:04 +00:00
renovate[bot]
476653a6cc chore(deps): update actions/setup-node action to v4.0.2 2024-02-07 07:24:21 +00:00
renovate[bot]
407496efd6 chore(deps): update dependency microsoft.net.test.sdk to v17.9.0 2024-02-07 04:18:59 +00:00
ArchiBot
9995b807f9 Automatic translations update 2024-02-07 02:04:09 +00:00
Archi
a6f4692b75 Update TrimmerRoots.xml 2024-02-07 00:16:45 +01:00
Archi
024b7ff824 Bump 2024-02-07 00:16:11 +01:00
Archi
68e46096ad Closes #3137 2024-02-07 00:15:50 +01:00
renovate[bot]
d9f5f60854 chore(deps): update wiki digest to 8f35971 2024-02-06 19:30:47 +00:00
Archi
82ccd4ddce Use different text for private apps 2024-02-06 20:08:21 +01:00
renovate[bot]
5f69b337a6 chore(deps): update actions/upload-artifact action to v4.3.1 2024-02-06 13:05:46 +00:00
Archi
dbbb6802d4 Misc 2024-02-06 12:16:16 +01:00
renovate[bot]
459cb44ff4 chore(deps): update actions/download-artifact action to v4.1.2 2024-02-06 07:31:32 +00:00
renovate[bot]
c1ebc813d5 chore(deps): update asf-ui digest to 630dbb8 2024-02-06 03:42:11 +00:00
ArchiBot
948c42055b Automatic translations update 2024-02-06 02:04:37 +00:00
Archi
06d049d882 Bump 2024-02-06 02:15:36 +01:00
Archi
04f5e91e92 Closes #3109 2024-02-06 02:14:54 +01:00
renovate[bot]
02a479ba13 chore(deps): update wiki digest to 024c6b4 2024-02-06 00:19:11 +00:00
Archi
01b99d20f6 Try to bulletproof against #3119 a bit 2024-02-05 20:49:34 +01:00
Archi
9c6cd7f692 Refactor FarmingResetSemaphore into FarmingResetEvent 2024-02-05 20:41:24 +01:00
Archi
7899829dc7 Refactor docker containers to use /asf
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.
2024-02-05 16:26:54 +01:00
ArchiBot
d0693d362a Automatic translations update 2024-02-05 02:07:18 +00:00
Archi
227066f355 Bump 2024-02-04 22:31:10 +01:00
Archi
348c43b259 Skip spamming ASFB with requests from unlicensed users
Check license prior to fetching inventory and sending data to ASFB, will also limit traffic on Steam side.
2024-02-04 22:28:59 +01:00
ArchiBot
a23b7e1594 Automatic translations update 2024-02-04 02:06:40 +00:00
Archi
1c7952f8dd Bump 2024-02-03 22:02:57 +01:00
Archi
f0ef4c6ba6 Implement basic crash protection
- 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.
2024-02-03 21:18:47 +01:00
renovate[bot]
eb71b640c5 chore(deps): update dependency microsoft.identitymodel.jsonwebtokens to v7.3.1 2024-02-03 08:28:40 +00:00
renovate[bot]
6b7a0ff1ce chore(deps): update github/codeql-action action to v3.24.0 2024-02-02 19:35:45 +00:00
Archi
d08740b6d7 Schedule refresh of licenses for STD plugin with delay
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.
2024-02-02 13:17:55 +01:00
ArchiBot
70e3649e60 Automatic translations update 2024-02-02 02:04:28 +00:00
renovate[bot]
24d79f9b20 chore(deps): update asf-ui digest to 6ca9093 2024-02-01 22:02:19 +00:00
Archi
43c2ae6746 Remove obsolete stuff 2024-02-01 14:27:56 +01:00
Archi
4bd2a3ec7f Bump 2024-02-01 14:22:53 +01:00
renovate[bot]
a90b375a72 chore(deps): update asf-ui digest to e3fd680 2024-02-01 03:36:01 +00:00
renovate[bot]
9f1e562a19 chore(deps): update asf-ui digest to 7b4a361 2024-01-31 18:14:11 +00:00
renovate[bot]
4edfcff2e0 chore(deps): update asf-ui digest to 9607b07 2024-01-31 14:06:00 +00:00
renovate[bot]
d4b48ab235 chore(deps): update crowdin/github-action action to v1.17.0 2024-01-31 08:04:58 +00:00
ArchiBot
f1e5c31110 Automatic translations update 2024-01-31 02:04:51 +00:00
renovate[bot]
32c4522b47 chore(deps): update asf-ui digest to b207bca 2024-01-30 23:15:29 +00:00
Archi
fc760e1a84 Misc 2024-01-30 14:30:52 +01:00
Archi
2a92bf4dec Bump 2024-01-30 13:51:18 +01:00
Archi
716b253a04 Move from System.IdentityModel.Tokens.Jwt to Microsoft.IdentityModel.JsonWebTokens
> 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
2024-01-30 13:26:32 +01:00
renovate[bot]
0384365315 chore(deps): update dependency system.identitymodel.tokens.jwt to v7.3.0 2024-01-30 06:46:07 +00:00
renovate[bot]
45dc910e01 chore(deps): update asf-ui digest to b341e7f 2024-01-30 04:38:00 +00:00
ArchiBot
f7e57e7d39 Automatic translations update 2024-01-30 02:04:51 +00:00
Archi
ace4151bbc Remove dead code 2024-01-29 19:05:14 +01:00
Archi
088c3a35ed Bump 2024-01-29 18:57:54 +01:00
Archi
608bece8dc Misc 2024-01-29 18:49:29 +01:00
Archi
119caebfa8 Deprecate CachedAccessToken, move to Bot.AccessToken instead
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
2024-01-29 18:42:21 +01:00
Archi
2e0771b8d9 Closes #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.
2024-01-29 17:53:46 +01:00
Archi
a08080a2ce Do not treat NU190x as error
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.
2024-01-28 16:05:10 +01:00
renovate[bot]
d4bcdfde3e chore(deps): update asf-ui digest to abebbe3 2024-01-28 03:59:05 +00:00
ArchiBot
249ebfb590 Automatic translations update 2024-01-28 02:06:03 +00:00
ArchiBot
56fc50e5ad Automatic translations update 2024-01-27 02:03:38 +00:00
renovate[bot]
5d8db36753 chore(deps): update github/codeql-action action to v3.23.2 2024-01-26 17:09:43 +00:00
renovate[bot]
54f493467e chore(deps): update asf-ui digest to 2111058 2024-01-26 02:15:52 +00:00
ArchiBot
7ed39db953 Automatic translations update 2024-01-26 02:05:48 +00:00
Archi
cc28d7520e Misc 2024-01-25 23:34:58 +01:00
renovate[bot]
109b307d0f chore(deps): update peter-evans/dockerhub-description action to v4 (#3132)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-25 22:11:27 +01:00
renovate[bot]
178ecc2e4a chore(deps): update asf-ui digest to 89945a1 2024-01-25 20:38:29 +00:00
renovate[bot]
ddfa7220d8 chore(deps): update mstest monorepo to v3.2.0 2024-01-25 06:39:34 +00:00
ArchiBot
812523baef Automatic translations update 2024-01-25 02:10:26 +00:00
renovate[bot]
c5fd423a78 chore(deps): update wiki digest to 1324210 2024-01-24 19:28:33 +00:00
Archi
0b6c308d2e Bump 2024-01-24 16:29:27 +01:00
renovate[bot]
d6ef5e5404 chore(deps): update actions/upload-artifact action to v4.3.0 2024-01-24 13:06:56 +00:00
Archi
9c304b8965 Actually make --ignore-unsupported-environment ignore the issue 2024-01-24 12:45:46 +01:00
Archi
05c5a7fc30 Closes #3128 2024-01-24 12:43:36 +01:00
renovate[bot]
2a2d4f09f1 chore(deps): update asf-ui digest to 97c6b06 2024-01-24 04:57:59 +00:00
ArchiBot
ce5cd7bc8f Automatic translations update 2024-01-24 02:09:10 +00:00
renovate[bot]
a072a7b7d6 chore(deps): update wiki digest to d8d3cd4 2024-01-23 22:59:22 +00:00
Archi
8a52f4fbbb Closes #3126
Smartly putting it in the middle making breaking change, since nobody managed to reference this public API yet! \o/
2024-01-23 23:01:22 +01:00
Archi
ba07405d9a Refactor selected boolean bot config properties
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.
2024-01-23 22:49:33 +01:00
Archi
6239457f01 Closes #3127 2024-01-23 21:23:06 +01:00
renovate[bot]
584fe4bd37 chore(deps): update wiki digest to 01a1155 2024-01-23 06:32:47 +00:00
renovate[bot]
1f4fa2ed90 chore(deps): update asf-ui digest to 80c331a 2024-01-23 04:45:51 +00:00
renovate[bot]
d24731999a chore(deps): update jetbrains/qodana-action action to v2023.3.1 2024-01-22 23:04:28 +00:00
ArchiBot
1a3e82a1b0 Automatic translations update 2024-01-22 02:11:05 +00:00
ArchiBot
f45b10f2ff Automatic translations update 2024-01-21 02:11:05 +00:00
ArchiBot
2097fea6a0 Automatic translations update 2024-01-20 02:07:31 +00:00
renovate[bot]
c4bdb39c6d chore(deps): update crowdin/github-action action to v1.16.1 2024-01-19 15:58:09 +00:00
renovate[bot]
2f2b411293 chore(deps): update asf-ui digest to 9105e9b 2024-01-19 11:09:53 +00:00
renovate[bot]
860b979afb chore(deps): update actions/upload-artifact action to v4.2.0 2024-01-19 00:18:38 +00:00
renovate[bot]
401d3f65f5 chore(deps): update asf-ui digest to e77a557 2024-01-18 22:35:27 +00:00
renovate[bot]
051cf043f3 chore(deps): update asf-ui digest to d8b90fb 2024-01-18 07:09:47 +00:00
renovate[bot]
4ebb4cfd9e chore(deps): update github/codeql-action action to v3.23.1 2024-01-18 02:20:11 +00:00
ArchiBot
d58a2d2717 Automatic translations update 2024-01-18 02:08:26 +00:00
renovate[bot]
51b764a39f chore(deps): update asf-ui digest to 38f6759 2024-01-17 20:20:07 +00:00
renovate[bot]
0f94a05a69 chore(deps): update asf-ui digest to cd3a947 2024-01-17 06:54:52 +00:00
ArchiBot
16fd3c067f Automatic translations update 2024-01-17 02:08:36 +00:00
ArchiBot
e13881763c Automatic translations update 2024-01-16 02:08:17 +00:00
renovate[bot]
90241c6076 chore(deps): update asf-ui digest to 344cfcf 2024-01-15 04:06:46 +00:00
ArchiBot
d020a97209 Automatic translations update 2024-01-15 02:09:55 +00:00
renovate[bot]
d9ceea448f chore(deps): update asf-ui digest to 255221f 2024-01-14 11:09:25 +00:00
renovate[bot]
ec01846653 chore(deps): update asf-ui digest to 9919655 2024-01-13 10:20:06 +00:00
renovate[bot]
7d3c0a9d13 chore(deps): update asf-ui digest to e1b0524 2024-01-13 05:36:41 +00:00
ArchiBot
de88e3072b Automatic translations update 2024-01-13 02:07:46 +00:00
renovate[bot]
5e2ad8eb19 chore(deps): update actions/upload-artifact action to v4.1.0 2024-01-12 18:29:45 +00:00
renovate[bot]
127107a96c chore(deps): update asf-ui digest to fa12667 2024-01-12 07:29:26 +00:00
Archi
1587c6facb Bump 2024-01-11 16:51:05 +01:00
Archi
042fadca28 Merge branch 'main' of https://github.com/JustArchiNET/ArchiSteamFarm 2024-01-11 16:46:48 +01:00
Archi
4a9e6f6cc6 Deprioritize bots with 1-game inventory
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.
2024-01-11 16:46:45 +01:00
renovate[bot]
b7ac24eb7b chore(deps): update asf-ui digest to 392b585 2024-01-11 06:54:53 +00:00
renovate[bot]
c78d26a701 chore(deps): update dependency system.identitymodel.tokens.jwt to v7.2.0 2024-01-11 00:47:47 +00:00
renovate[bot]
53614661c1 chore(deps): update asf-ui digest to cd30e8d 2024-01-10 21:09:00 +00:00
renovate[bot]
ea8c300a1a chore(deps): update actions/download-artifact action to v4.1.1 2024-01-10 19:29:00 +00:00
renovate[bot]
bb91bf3918 chore(deps): update dependency system.identitymodel.tokens.jwt to v7.1.2 2024-01-09 18:38:55 +00:00
renovate[bot]
b9f72c293d chore(deps): update asf-ui digest to 701562c 2024-01-09 03:29:49 +00:00
renovate[bot]
7a14a394c2 chore(deps): update github/codeql-action action to v3.23.0 (#3122)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-08 18:31:49 +01:00
Sebastian Göls
dbf7148fbe Happy new year! (#3121)
Co-authored-by: Sebastian Göls <sebastian.goels@salvagninigroup.com>
2024-01-08 11:33:28 +01:00
ArchiBot
59d51d1b15 Automatic translations update 2024-01-08 02:08:23 +00:00
renovate[bot]
acff4602cd chore(deps): update asf-ui digest to 8a350cd 2024-01-07 03:34:22 +00:00
ArchiBot
44f135eb14 Automatic translations update 2024-01-07 02:10:04 +00:00
renovate[bot]
f4945c024e chore(deps): update asf-ui digest to f30f6ea 2024-01-06 04:22:52 +00:00
ArchiBot
a329e2a3da Automatic translations update 2024-01-06 02:06:55 +00:00
renovate[bot]
608edf2569 chore(deps): update asf-ui digest to e2a9bdb 2024-01-05 04:04:42 +00:00
ArchiBot
f203e02a45 Automatic translations update 2024-01-05 02:07:53 +00:00
renovate[bot]
bdcc00af1f chore(deps): update asf-ui digest to ede4734 2024-01-04 21:04:35 +00:00
renovate[bot]
21d004fb26 chore(deps): update crowdin/github-action action to v1.16.0 2024-01-04 13:47:19 +00:00
renovate[bot]
d4ae307676 chore(deps): update asf-ui digest to 6ce634f 2024-01-04 04:05:08 +00:00
ArchiBot
0a9993e85a Automatic translations update 2024-01-04 02:07:12 +00:00
Archi
1f2269dcf2 Misc code syntax 2024-01-03 15:06:08 +01:00
Archi
bb73916af0 Misc optimizations 2024-01-03 14:57:49 +01:00
Archi
12c4b7e924 Apply frozen collections optimizations 2024-01-03 13:46:54 +01:00
renovate[bot]
c6b78b118c chore(deps): update asf-ui digest to 8a2b86b 2024-01-03 07:37:18 +00:00
Archi
aaa6b6674a Bump 2024-01-03 00:34:34 +01:00
Archi
be2e173404 Misc followup 2024-01-03 00:34:12 +01:00
Archi
bfb189d55b Bump 2024-01-03 00:29:41 +01:00
Archi
3d503ed5ee Fix invalid heartbeats from inactive STM 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.
2024-01-03 00:23:27 +01:00
Archi
ab01733860 Bump 2024-01-02 01:21:00 +01:00
Archi
dd0949b58d Fix enum pointer failures in swagger schema 2024-01-02 01:19:28 +01:00
Archi
d825f74489 Bump 2024-01-01 23:25:50 +01:00
Archi
7f1ecdd585 Misc 2024-01-01 23:22:19 +01:00
Archi
01b2e205be Make std command return before refresh finishes 2024-01-01 23:16:52 +01:00
Archi
d398e84f25 Misc 2024-01-01 23:00:58 +01:00
Archi
ac427ed1ec Misc match improvements 2024-01-01 22:58:54 +01:00
ArchiBot
0118ccb614 Automatic translations update 2023-12-31 02:08:18 +00:00
renovate[bot]
697e059b66 chore(deps): update asf-ui digest to d9c38ab 2023-12-30 04:15:17 +00:00
renovate[bot]
45539018f5 chore(deps): update dependency nlog.web.aspnetcore to v5.3.8 2023-12-29 22:50:02 +00:00
renovate[bot]
1ebf2b6272 chore(deps): update asf-ui digest to 99254ec 2023-12-29 19:06:24 +00:00
Archi
800dae280a Bump 2023-12-29 13:24:44 +01:00
Archi
bccdf269f0 Closes #3115 2023-12-29 13:23:38 +01:00
renovate[bot]
757072cb01 chore(deps): update asf-ui digest to e52146a 2023-12-29 03:25:23 +00:00
renovate[bot]
b601d31d5c chore(deps): update asf-ui digest to 43ce08a 2023-12-28 18:49:37 +00:00
renovate[bot]
86ae501ce5 chore(deps): update asf-ui digest to bb4c28c 2023-12-27 04:47:07 +00:00
ArchiBot
c22e5c146f Automatic translations update 2023-12-27 02:06:17 +00:00
renovate[bot]
4a710b4ffe chore(deps): update crazy-max/ghaction-import-gpg action to v6.1.0 2023-12-26 20:02:14 +00:00
renovate[bot]
f6ad3747f4 chore(deps): update asf-ui digest to 3eb41f0 2023-12-26 04:04:41 +00:00
ArchiBot
ec205bb7a2 Automatic translations update 2023-12-26 02:06:22 +00:00
renovate[bot]
464edddacf chore(deps): update asf-ui digest to 2e91573 2023-12-25 04:12:10 +00:00
ArchiBot
e6c6bce8a7 Automatic translations update 2023-12-25 02:07:40 +00:00
renovate[bot]
6a413b4d29 chore(deps): update asf-ui digest to 2dbdfaa 2023-12-24 19:43:31 +00:00
ArchiBot
4f30e2e3c7 Automatic translations update 2023-12-24 02:08:02 +00:00
Archi
2bef94e3b4 Misc 2023-12-23 23:37:29 +01:00
Archi
cf94c417d2 Misc 2023-12-23 23:16:44 +01:00
ArchiBot
e82100308c Automatic translations update 2023-12-23 02:05:36 +00:00
renovate[bot]
ada67a0f97 chore(deps): update github/codeql-action action to v3.22.12 2023-12-22 06:50:53 +00:00
renovate[bot]
1bd20f7144 chore(deps): update asf-ui digest to 88a332c 2023-12-22 04:51:36 +00:00
ArchiBot
9b295ad85e Automatic translations update 2023-12-22 02:06:52 +00:00
Archi
d1953215e8 Bump 2023-12-22 00:21:25 +01:00
Archi
e480aca8b2 Use inventories items deduplication logic aligned with ASFB 2023-12-22 00:18:52 +01:00
Archi
2befe20f76 Use set parts also as inventories request optimization 2023-12-21 23:46:42 +01:00
ArchiBot
c16485ad0b Automatic translations update 2023-12-21 02:07:13 +00:00
Archi
f036e99450 Misc
Specifying target framework should no longer be needed, as we have only one left
2023-12-20 18:46:19 +01:00
renovate[bot]
2804a36920 chore(deps): update asf-ui digest to 295cb26 2023-12-20 04:08:32 +00:00
ArchiBot
30e62813c7 Automatic translations update 2023-12-20 01:57:07 +00:00
renovate[bot]
621ce390c2 chore(deps): update asf-ui digest to 53e75ab 2023-12-19 23:06:09 +00:00
renovate[bot]
8d40423d9d chore(deps): update asf-ui digest to 71014eb 2023-12-19 15:49:29 +00:00
renovate[bot]
1cf8959b92 chore(deps): update actions/download-artifact action to v4.1.0 2023-12-19 04:26:50 +00:00
ArchiBot
56aafe3374 Automatic translations update 2023-12-19 02:07:52 +00:00
renovate[bot]
5570bd2999 chore(deps): update actions/setup-node action to v4.0.1 2023-12-18 14:02:50 +00:00
ArchiBot
9bec394436 Automatic translations update 2023-12-18 02:08:05 +00:00
ArchiBot
3ae1a7ccfd Automatic translations update 2023-12-17 02:08:56 +00:00
renovate[bot]
e0b1c4c16f chore(deps): update asf-ui digest to 68c799a 2023-12-16 19:31:03 +00:00
Archi
eb5bc560a4 Misc 2023-12-16 14:33:32 +01:00
Archi
f9309b7c54 Bump 2023-12-16 14:28:13 +01:00
renovate[bot]
3c2a154b39 chore(deps): update asf-ui digest to b924e3b 2023-12-16 07:42:41 +00:00
ArchiBot
23d07eb43e Automatic translations update 2023-12-16 02:06:58 +00:00
Archi
20af0edd4d Misc 2023-12-15 14:22:22 +01:00
Archi
4b29daabd4 Fix possible NRE 2023-12-15 14:20:58 +01:00
renovate[bot]
a60513e998 chore(deps): update asf-ui digest to 531e8ec 2023-12-15 03:03:17 +00:00
ArchiBot
188b96b951 Automatic translations update 2023-12-15 02:08:24 +00:00
renovate[bot]
b8e9dca6d3 chore(deps): update dependency markdig.signed to v0.34.0 2023-12-14 21:15:35 +00:00
Archi
157537c6ec Bump 2023-12-14 22:15:07 +01:00
renovate[bot]
a363b92075 chore(deps): update actions/download-artifact action to v4 (#3099)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-14 21:44:05 +01:00
renovate[bot]
f993d3d365 chore(deps): update actions/upload-artifact action to v4 (#3100)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-14 21:43:56 +01:00
Jack Nolddor
3f4520edf3 chore: blacklist Winter Sale 2023 appid (#3098) 2023-12-14 21:09:56 +01:00
Archi
6e7041d8c5 Fix possible unhandled exception crash
We should throw ONLY when caller asked us to cancel, not when httpclient's timeout is reached.

Thanks @nolddor
2023-12-14 21:07:48 +01:00
Archi
e6cf5971a6 Update ArchiSteamFarm.sln.DotSettings 2023-12-14 14:22:53 +01:00
Archi
845af42080 Update qodana.yaml 2023-12-14 14:18:16 +01:00
Archi
c74481590b Update qodana.yaml 2023-12-14 14:10:14 +01:00
Archi
0ac5447198 Update qodana.yaml 2023-12-14 14:03:19 +01:00
ArchiBot
e0428f8a91 Automatic translations update 2023-12-14 02:07:39 +00:00
renovate[bot]
cc0d2cb1d4 Update wiki digest to e1c80a5 2023-12-13 23:16:06 +00:00
Archi
a0769eaf9a Bump 2023-12-14 00:15:37 +01:00
renovate[bot]
dc35545043 Update github/codeql-action action to v3 (#3097)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-13 18:15:41 +01:00
Archi
890709429c Misc 2023-12-13 10:02:19 +01:00
Archi
6f6a561b9e Update code-quality.yml 2023-12-13 09:46:20 +01:00
ArchiBot
91d314c861 Automatic translations update 2023-12-13 02:08:27 +00:00
renovate[bot]
8abae9d4be Update github/codeql-action action to v2.22.10 2023-12-12 18:36:05 +00:00
Archi
26a390760e Merge branch 'main' of https://github.com/JustArchiNET/ArchiSteamFarm 2023-12-12 14:34:54 +01:00
Archi
87933a2c92 Update ArchiSteamFarm.sln.DotSettings 2023-12-12 14:34:50 +01:00
ArchiBot
1f843bb5d6 Automatic translations update 2023-12-12 02:08:22 +00:00
renovate[bot]
e87e78a372 Update ASF-ui digest to f84a296 2023-12-11 23:25:18 +00:00
Archi
91c82302bb Disable stack trace support in trimmed builds 2023-12-12 00:24:59 +01:00
Archi
d5a41dce1d Misc 2023-12-12 00:05:19 +01:00
Archi
40ab1d848c .NET 8 code enhancements 2023-12-11 23:55:13 +01:00
renovate[bot]
cc3a0a4144 Update JetBrains/qodana-action action to v2023.3.0 2023-12-11 16:31:06 +00:00
renovate[bot]
5448403f43 Update wiki digest to 15af6e4 2023-12-11 13:19:51 +00:00
Archi
636dd139c2 Misc 2023-12-11 11:42:19 +01:00
Archi
e7ad69be26 Closes #3093 2023-12-11 11:38:37 +01:00
ArchiBot
2f56b6dc3a Automatic translations update 2023-12-11 02:08:21 +00:00
renovate[bot]
ba7073df98 Update github/codeql-action action to v2.22.9 2023-12-10 12:22:37 +00:00
renovate[bot]
457bf6dfbb Update dependency NLog.Web.AspNetCore to v5.3.7 2023-12-10 11:22:34 +00:00
renovate[bot]
1e6279e1ca Update ASF-ui digest to 44cbd70 2023-12-10 08:40:06 +00:00
ArchiBot
6e455d0eba Automatic translations update 2023-12-09 02:06:46 +00:00
ArchiBot
9c9a74b448 Automatic translations update 2023-12-08 02:08:17 +00:00
ArchiBot
b7790961fc Automatic translations update 2023-12-07 02:08:13 +00:00
renovate[bot]
5ecc050b12 Update wiki digest to d849fb2 2023-12-06 12:31:12 +00:00
Sebastian Göls
c9819bde7f Add duplicate-check to issue templates (#3092)
* Update Bug-report.yml

* Update Enhancement-idea.yml

* Update Enhancement-idea.yml

* Update Bug-report.yml
2023-12-06 13:30:54 +01:00
ArchiBot
12660449ed Automatic translations update 2023-12-06 02:08:19 +00:00
renovate[bot]
7237e0affc Update wiki digest to 05c3655 2023-12-05 13:17:54 +00:00
ArchiBot
85bb68825b Automatic translations update 2023-12-05 02:08:13 +00:00
Archi
48b8a28c7a Bump 2023-12-05 01:55:23 +01:00
Archi
b16a459ab8 Misc 2023-12-05 01:53:14 +01:00
Archi
da2fd37aa1 Make STD work also in on-demand mode 2023-12-05 01:43:55 +01:00
Archi
8d1d508fe5 Use ASF's global database for STD package access tokens 2023-12-05 00:05:16 +01:00
Archi
92858de9e2 Misc 2023-12-05 00:04:38 +01:00
Archi
a7b1e01161 Revert "Disable server-side functionality in custom ASF builds"
This reverts commit 42ceb6d413.
2023-12-04 23:06:11 +01:00
renovate[bot]
e14d00b760 Update actions/setup-dotnet action to v4 (#3089)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-04 21:04:53 +01:00
ArchiBot
d737201ab5 Automatic translations update 2023-12-04 02:08:17 +00:00
Archi
5fcd5d51f9 Bump 2023-12-04 00:18:34 +01:00
Archi
fa74d98879 Kill API key leftovers 2023-12-03 21:51:32 +01:00
Archi
27f965d4af Kill API keys entirely
Economy bans we can handle ourselves server-side
2023-12-03 21:49:05 +01:00
Archi
ddd34d4a45 Bump 2023-12-03 17:01:55 +01:00
Archi
a3aa93fce8 Fix diff announcement with no items added/changed 2023-12-03 16:11:38 +01:00
Archi
0095d458e7 Handle new no requirements for API key 2023-12-03 14:08:24 +01:00
Archi
def3e26c92 Closes #3084 2023-12-03 13:48:51 +01:00
ArchiBot
10eb226722 Automatic translations update 2023-12-03 02:08:31 +00:00
renovate[bot]
3738ebed21 Update wiki digest to a9c7d7d 2023-12-02 20:06:57 +00:00
Archi
eff60bf307 Implement background announcements for ASF STM
This will be used exclusively by users with extraordinary large inventories, or if ASF backend will just be slower than usual.
2023-12-02 19:36:34 +01:00
Archi
b940af6e83 Misc 2023-12-02 18:49:32 +01:00
Archi
42ceb6d413 Disable server-side functionality in custom ASF builds 2023-12-02 18:37:40 +01:00
Archi
eef66cebf3 Add session data for SDA compatibility 2023-12-02 17:00:50 +01:00
Archi
fac8cb2c9a Misc 2023-12-02 15:16:26 +01:00
Archi
9ce195b4ec Use more correct way for resolving #3087 2023-12-02 15:14:19 +01:00
Archi
c4bcb679f9 Closes #3087 2023-12-02 15:08:59 +01:00
Archi
0d4871ca02 Fix delayed password input crash 2023-12-02 13:15:53 +01:00
renovate[bot]
8397a69130 Update ASF-ui digest to 62a5d46 2023-12-02 03:45:07 +00:00
ArchiBot
8515d72048 Automatic translations update 2023-12-02 02:06:10 +00:00
renovate[bot]
32af0abb6c Update ASF-ui digest to 7da00f0 2023-12-01 22:11:20 +00:00
renovate[bot]
e05f79b951 Update JetBrains/qodana-action action to v2023.2.9 2023-12-01 16:18:49 +00:00
renovate[bot]
9699686da5 Update ASF-ui digest to fe06a7c 2023-11-30 18:28:36 +00:00
ArchiBot
2052020d3d Automatic translations update 2023-11-30 02:08:00 +00:00
Archi
a894b7096a Bump 2023-11-29 20:36:24 +01:00
Archi
52b9d2d662 Revert market_fee_app
There are occurances where this is definitely wrong to use
2023-11-29 20:35:39 +01:00
Archi
f3cc0b938a Bump 2023-11-29 20:08:11 +01:00
Archi
80c362d5ed Fix regression in trades received now
Also improve that code while I'm at it
2023-11-29 19:52:43 +01:00
Archi
c7546194f8 Add handling of points shop items, and skip them for announcements 2023-11-29 19:17:02 +01:00
renovate[bot]
53993bfd34 Update ASF-ui digest to 3bea7c7 2023-11-29 13:36:45 +00:00
Archi
181bc28462 Bump 2023-11-29 14:36:25 +01:00
Archi
aea9dee4ea Further decrease server load
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.
2023-11-29 14:26:57 +01:00
Archi
a3270e4081 Misc optimization 2023-11-29 13:21:12 +01:00
ArchiBot
ccf191f1ba Automatic translations update 2023-11-29 02:08:48 +00:00
Archi
a76b6fc32f Bump 2023-11-29 00:10:46 +01:00
Łukasz Domeradzki
36ae066c65 Closes #3073 (#3077)
* 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
2023-11-29 00:08:16 +01:00
ArchiBot
8cf2d1bc94 Automatic translations update 2023-11-28 02:08:22 +00:00
Archi
d4e8182ffc Update Startup.cs 2023-11-27 15:51:21 +01:00
Archi
96239a97f3 Address trim warnings 2023-11-27 15:39:30 +01:00
Archi
70d3ca47d7 Update TypeController.cs 2023-11-27 14:15:33 +01:00
Archi
a59087b574 Resolve some warnings 2023-11-27 13:01:52 +01:00
Archi
306fbef3c2 Update code-quality.yml 2023-11-27 11:12:35 +01:00
Archi
2b7fc937ad Update code-quality.yml 2023-11-27 10:50:36 +01:00
ArchiBot
3c338e05df Automatic translations update 2023-11-27 02:07:55 +00:00
ArchiBot
dcf38c1357 Automatic translations update 2023-11-26 02:08:13 +00:00
renovate[bot]
500499ec19 Update ASF-ui digest to 853672c 2023-11-25 04:14:31 +00:00
ArchiBot
d84c15b26c Automatic translations update 2023-11-25 02:06:13 +00:00
renovate[bot]
9597f21cd7 Update wiki digest to e782280 2023-11-24 13:06:48 +00:00
Archi
fedf477241 Merge branch 'main' of https://github.com/JustArchiNET/ArchiSteamFarm 2023-11-24 10:34:28 +01:00
Archi
2291321173 Handle ASF_PATH first
ASF_CRYPTKEY_FILE might reference relative path, we should evaluate ASF_PATH first due to that.
2023-11-24 10:34:20 +01:00
renovate[bot]
2529ee301e Update ASF-ui digest to 085af69 2023-11-24 03:53:37 +00:00
ArchiBot
ba1313f44f Automatic translations update 2023-11-24 02:06:46 +00:00
renovate[bot]
d640671640 Update github/codeql-action action to v2.22.8 2023-11-23 11:13:08 +00:00
renovate[bot]
f0a54875ed Update ASF-ui digest to eb74516 2023-11-23 03:54:46 +00:00
ArchiBot
acca0a7a52 Automatic translations update 2023-11-23 02:07:40 +00:00
renovate[bot]
fa26f31d95 Update dessant/lock-threads action to v5.0.1 2023-11-22 15:51:44 +00:00
renovate[bot]
2851cffbc5 Update wiki digest to 2b65e86 2023-11-22 14:16:36 +00:00
renovate[bot]
33649f95a8 Update ASF-ui digest to 5a3687d 2023-11-22 11:14:35 +00:00
Archi
d3142d077c Misc 2023-11-22 12:09:35 +01:00
ArchiBot
dda62f6db6 Automatic translations update 2023-11-22 02:08:55 +00:00
renovate[bot]
578cb95b43 Update wiki digest to 904d4b6 2023-11-21 18:56:56 +00:00
ArchiBot
81118633e0 Automatic translations update 2023-11-21 02:09:39 +00:00
ArchiBot
5ab84286dc Automatic translations update 2023-11-20 02:08:10 +00:00
renovate[bot]
b5e33c041a Update ASF-ui digest to 2360415 2023-11-19 18:34:01 +00:00
Archi
f4f73bcadc Apply CI permissions 2023-11-19 19:33:42 +01:00
ArchiBot
4f459e21f2 Automatic translations update 2023-11-19 02:08:56 +00:00
Archi
69352db431 Bump 2023-11-19 02:49:40 +01:00
Archi
120f7e36d6 Further enhancements for #3075 and #3051 2023-11-19 02:41:22 +01:00
Archi
2e9791faa3 Closes #3075 2023-11-19 02:25:47 +01:00
Archi
fe8e125b86 Bump 2023-11-18 22:09:06 +01:00
Archi
16f4dff0c2 CI: Remove --no-restore where not needed
--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.
2023-11-18 21:24:58 +01:00
renovate[bot]
915693d7fa Update ASF-ui digest to 8b1afc3 2023-11-18 03:48:20 +00:00
ArchiBot
52adac7cab Automatic translations update 2023-11-18 02:07:01 +00:00
renovate[bot]
a066b952d7 Update docker/build-push-action action to v5.1.0 2023-11-17 13:35:49 +00:00
ArchiBot
cb0e2218a6 Automatic translations update 2023-11-17 02:07:52 +00:00
renovate[bot]
f9949967dc Update github/codeql-action action to v2.22.7 2023-11-16 23:04:21 +00:00
renovate[bot]
0bf1b7af91 Update ASF-ui digest to a6af7da 2023-11-16 20:49:32 +00:00
Sebastian Göls
193f0e3c08 Use generic ProducesResponseTypeAttribute instead of typeof(...) (#3074) 2023-11-16 21:49:15 +01:00
renovate[bot]
5cbe11241c Update crowdin/github-action action to v1.15.2 2023-11-16 16:26:01 +00:00
ArchiBot
03fc9cb4be Automatic translations update 2023-11-16 02:08:16 +00:00
renovate[bot]
2ed7cdf98e Update ASF-ui digest to 0b53178 2023-11-15 11:11:26 +00:00
renovate[bot]
305d6a2d17 Update crowdin/github-action action to v1.15.1 2023-11-15 07:02:34 +00:00
renovate[bot]
6f784621ed Update github/codeql-action action to v2.22.6 2023-11-15 03:45:42 +00:00
ArchiBot
83d6d43845 Automatic translations update 2023-11-15 02:07:36 +00:00
Archi
90e9f47899 Add default implementation for IBotsComparer 2023-11-15 00:02:26 +01:00
Archi
185d7adccf Misc 2023-11-14 23:51:28 +01:00
Archi
977f571db6 Closes #3072 2023-11-14 23:50:36 +01:00
Archi
1c22a45cb6 Add win-arm64 as recognized supported platform for issue template 2023-11-14 23:45:54 +01:00
Archi
608229f962 Kill netf leftovers 2023-11-14 22:17:12 +01:00
Archi
3f04144e41 Use cancellation in more places 2023-11-14 21:20:51 +01:00
Archi
ac9d4a7783 Actually make ArchiCacheable catch cancellation
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
2023-11-14 21:15:18 +01:00
Archi
adbf0748f8 Closes #3066 2023-11-14 21:10:35 +01:00
Archi
263c77da12 We don't target multiple frameworks anymore 2023-11-14 21:04:40 +01:00
Archi
103c543eea Closes #3067 2023-11-14 20:58:02 +01:00
Archi
0ae03c7cd5 Syntax improvements 2023-11-14 20:21:02 +01:00
Archi
f2ff2f4929 Closes #3060 2023-11-14 20:01:29 +01:00
Łukasz Domeradzki
b34f18497d .NET 8 (#3005)
* 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
2023-11-14 19:12:33 +01:00
renovate[bot]
1e04acb904 Update dotnet monorepo to v8 (#3071)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-14 17:25:06 +01:00
Archi
2b06153fa2 Add additional test case 2023-11-14 16:25:40 +01:00
Archi
e1c364cf7a Fix wrong Bosnian language mapping 2023-11-14 15:38:10 +01:00
renovate[bot]
e281a04450 Update dessant/lock-threads action to v5 (#3070)
* Update dessant/lock-threads action to v5

* Update lock-threads.yml

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Archi <JustArchi@JustArchi.net>
2023-11-14 10:05:53 +01:00
ArchiBot
b40d55c2e4 Automatic translations update 2023-11-13 02:07:15 +00:00
Archi
3e96f37979 BUMPO 2023-11-12 03:16:07 +01:00
ArchiBot
72703dc61d Automatic translations update 2023-11-12 02:07:51 +00:00
renovate[bot]
4ba2f16907 Update wiki digest to 4b67f91 2023-11-12 00:09:43 +00:00
Archi
d6a7c9a843 Misc 2023-11-11 21:31:37 +01:00
Archi
9a304a3992 Misc code cleanup 2023-11-11 21:30:44 +01:00
tre3p
be4a625dc2 Add ability to send group messages for plugin developers (#3069)
* Add GetClanChatInfo as public API function

* make JoinChatRoomGroup public API. add LeaveChatRoomGroup as public API

* Rename GetClanChatRoomInfo method. Change return type of GetClanChatRoomInfo method
2023-11-11 21:29:57 +01:00
Archi
e44b7d3248 Fix wrong access for version command
FamilySharing is alright, as per the wiki
2023-11-11 14:53:44 +01:00
Archi
fc13f2c488 Handle further linux terminal edge case
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.
2023-11-11 14:37:08 +01:00
renovate[bot]
494b1dfd1b Update ASF-ui digest to c582499 2023-11-10 18:25:41 +00:00
Archi
eb41dace9f Run code quality also in push though 2023-11-10 19:25:24 +01:00
Archi
2b2607cfcd Disable qodana for remote PRs
We don't have a token for them
2023-11-10 19:03:21 +01:00
Archi
bde696124b Misc optimization 2023-11-10 19:02:03 +01:00
renovate[bot]
1f4f50591a Update dependency Microsoft.NET.Test.Sdk to v17.8.0 2023-11-09 23:03:58 +00:00
renovate[bot]
5377598226 Update ASF-ui digest to 3384658 2023-11-09 16:38:19 +00:00
renovate[bot]
050d8dca12 Update ASF-ui digest to 8fb3c49 2023-11-08 19:27:50 +00:00
renovate[bot]
54e31504d5 Update dependency JetBrains.Annotations to v2023.3.0 2023-11-08 00:43:16 +00:00
ArchiBot
d3d8a6ffcb Automatic translations update 2023-11-07 02:06:41 +00:00
renovate[bot]
4518547543 Update dependency SteamKit2 to v2.5.0 2023-11-06 16:04:24 +00:00
ArchiBot
1061676462 Automatic translations update 2023-11-06 02:07:17 +00:00
Archi
1d7794590e Bump 2023-11-05 23:38:29 +01:00
Archi
5d32eb6325 Fix netf as usual 2023-11-05 23:27:45 +01:00
Archi
3a9e9de596 Closes #2980 2023-11-05 23:22:15 +01:00
renovate[bot]
b46e936ac6 Update ASF-ui digest to 8884585 2023-11-05 01:49:14 +00:00
renovate[bot]
fc46c65bdf Update ASF-ui digest to 8651392 2023-11-03 03:46:46 +00:00
ArchiBot
57de1f3c10 Automatic translations update 2023-11-03 02:06:09 +00:00
renovate[bot]
a59c3d8fd2 Update crowdin/github-action action to v1.14.1 2023-11-02 09:48:27 +00:00
ArchiBot
6c774f5980 Automatic translations update 2023-11-02 02:05:47 +00:00
Archi
b126c5fd75 Bump 2023-11-02 01:19:32 +01:00
Archi
c21e4fbdb8 Merge branch 'main' of https://github.com/JustArchiNET/ArchiSteamFarm 2023-11-02 00:43:13 +01:00
Archi
ad4c81ac02 Closes #3058 2023-11-02 00:43:11 +01:00
Leet
17796c3466 Use gender neutral language (#3057) 2023-10-31 18:21:34 +01:00
Archi
09804a5032 Misc 2023-10-31 13:33:40 +01:00
ArchiBot
e4a650c8e0 Automatic translations update 2023-10-31 02:06:01 +00:00
Archi
5490539480 netf brain damage as usual 2023-10-30 22:46:43 +01:00
Archi
2a231982ed Misc 2023-10-30 22:44:07 +01:00
Archi
5219fc8958 Add additional bulletproofing 2023-10-30 22:41:45 +01:00
Archi
2f5fdf3185 Closes #3054 2023-10-30 22:33:06 +01:00
Archi
5a5a6e2074 Closes #2978 2023-10-30 22:17:16 +01:00
renovate[bot]
a1cf8291b4 Update dependency SteamKit2 to v2.5.0-Beta.2 (#3056)
* Update dependency SteamKit2 to v2.5.0-Beta.2

* Fix breaking changes

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Archi <JustArchi@JustArchi.net>
2023-10-30 22:11:33 +01:00
ArchiBot
1a16589c0a Automatic translations update 2023-10-30 02:06:28 +00:00
renovate[bot]
7372601064 Update wiki digest to ec80079 2023-10-29 13:29:20 +00:00
renovate[bot]
651d0bdb4b Update ASF-ui digest to 4068ece 2023-10-29 04:11:28 +00:00
ArchiBot
25e43d61e6 Automatic translations update 2023-10-29 02:06:37 +00:00
renovate[bot]
dfc3c0eef3 Update wiki digest to 7281ab4 2023-10-28 23:04:27 +00:00
Archi
c20b0c2c5b Bump 2023-10-29 01:04:07 +02:00
Archi
4a57e3a9c1 Merge branch 'main' of https://github.com/JustArchiNET/ArchiSteamFarm 2023-10-29 01:03:45 +02:00
Archi
1cde3afcbf Bump 2023-10-29 01:03:38 +02:00
ArchiBot
b6274958bd Automatic translations update 2023-10-28 02:04:04 +00:00
renovate[bot]
2023103ed6 Update ASF-ui digest to f2eefb1 2023-10-27 16:04:00 +00:00
renovate[bot]
d5cb0baf1f Update github/codeql-action action to v2.22.5 2023-10-27 10:39:05 +00:00
renovate[bot]
3b7a766ce4 Update ASF-ui digest to 7f6181c 2023-10-27 03:55:21 +00:00
ArchiBot
ac8fa21c90 Automatic translations update 2023-10-27 02:05:21 +00:00
renovate[bot]
4abf0388d6 Update ASF-ui digest to f726f34 2023-10-26 06:46:18 +00:00
ArchiBot
82c294bc85 Automatic translations update 2023-10-26 02:05:10 +00:00
renovate[bot]
f7ebc4acd5 Update ASF-ui digest to f39b7a7 2023-10-25 21:36:28 +00:00
renovate[bot]
910ec7ad29 Update crowdin/github-action action to v1.14.0 2023-10-25 14:17:47 +00:00
ArchiBot
6aaac31da0 Automatic translations update 2023-10-25 02:05:43 +00:00
renovate[bot]
0708199a59 Update ASF-ui digest to f54788e 2023-10-24 15:29:59 +00:00
renovate[bot]
907f6e4b33 Update actions/setup-node action to v4 (#3052)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-24 10:19:51 +02:00
renovate[bot]
78e30343f7 Update actions/setup-node action to v3.8.2 2023-10-23 18:09:05 +00:00
renovate[bot]
058e9fca77 Update ASF-ui digest to f55e29f 2023-10-23 15:24:00 +00:00
ArchiBot
e97c660b08 Automatic translations update 2023-10-23 02:05:53 +00:00
ArchiBot
b4dcc8621a Automatic translations update 2023-10-22 02:07:04 +00:00
renovate[bot]
a50bc1e13b Update dependency JustArchiNET.Madness to v3.17.0 2023-10-21 21:22:52 +00:00
Archi
502af043df Closes #3051 2023-10-21 22:42:11 +02:00
renovate[bot]
f3ef7d0397 Update wiki digest to 922617f 2023-10-21 16:23:52 +00:00
renovate[bot]
8d3edb7d60 Update ASF-ui digest to e56f7c0 2023-10-21 03:36:20 +00:00
ArchiBot
8bae812ae5 Automatic translations update 2023-10-21 02:04:24 +00:00
renovate[bot]
b4db6584e8 Update github/codeql-action action to v2.22.4 2023-10-20 16:31:22 +00:00
Archi
77f6fabbc7 Bump 2023-10-20 16:35:39 +02:00
Archi
7f4a11bb6a netf brain damage 2023-10-20 16:30:11 +02:00
Archi
93402cc6a0 Protect RefreshWebSession with a semaphore 2023-10-20 16:23:00 +02:00
Archi
1f2607149b Bump 2023-10-20 16:08:55 +02:00
Archi
02312dbee5 Add hotfix for empty access token with EResult.OK 2023-10-20 16:08:33 +02:00
renovate[bot]
aa1c344b03 Update ASF-ui digest to fceb2fb 2023-10-20 03:34:37 +00:00
ArchiBot
0df46a08da Automatic translations update 2023-10-20 02:05:48 +00:00
renovate[bot]
ce2693d407 Update wiki digest to b539db1 2023-10-19 13:04:53 +00:00
Archi
e1c3268cf4 Bump 2023-10-19 15:04:27 +02:00
Archi
6a0d428fa3 Fix reading encrypted secrets 2023-10-19 15:04:07 +02:00
Archi
776c5fe31c Bump 2023-10-19 14:36:26 +02:00
Archi
22177f1fe7 Fix RefreshToken handling for existing setups 2023-10-19 14:36:05 +02:00
Archi
ab1edae9c7 Bump 2023-10-19 13:43:58 +02:00
Archi
53d80345d3 Bump 2023-10-19 13:43:42 +02:00
Łukasz Domeradzki
d571cd9580 Closes #3043 (#3044)
* 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
2023-10-19 13:38:39 +02:00
ArchiBot
4106c5f41a Automatic translations update 2023-10-19 02:05:44 +00:00
Łukasz Domeradzki
71fa7560f7 Fix qodana for PRs (#3040)
* Fix qodana for PRs

* Update code-quality.yml
2023-10-18 23:51:35 +02:00
Łukasz Domeradzki
32196a53cd Closes #3039 (#3041) 2023-10-18 23:27:26 +02:00
Łukasz Domeradzki
5d467aca9a Add default bot implementation (#3038)
* Add default bot implementation

* Thought so

* Brain damage for netf
2023-10-18 22:57:58 +02:00
renovate[bot]
52eacaf577 Update ASF-ui digest to 1447dbe 2023-10-18 19:35:28 +00:00
Archi
d44c57a48f Misc 2023-10-18 10:56:07 +02:00
Archi
629a750df6 Bump 2023-10-18 10:47:42 +02:00
Archi
21b1a319ca Fix netf with new code 2023-10-18 10:35:44 +02:00
Archi
bb1f02e788 Closes #3029 2023-10-18 10:27:50 +02:00
renovate[bot]
5d8826b71b Update ASF-ui digest to 1d748b6 2023-10-18 03:48:39 +00:00
ArchiBot
829e5ac4fb Automatic translations update 2023-10-18 02:06:16 +00:00
renovate[bot]
feec46da32 Update wiki digest to bd2c085 2023-10-17 22:58:10 +00:00
renovate[bot]
4288f35e99 Update actions/checkout action to v4.1.1 2023-10-17 17:26:45 +00:00
ArchiBot
69004d20ce Automatic translations update 2023-10-17 02:06:30 +00:00
renovate[bot]
91ce53d283 Update wiki digest to 3408c8a 2023-10-16 23:00:59 +00:00
renovate[bot]
400ea64cfd Update dependency NLog.Web.AspNetCore to v5.3.5 2023-10-15 20:01:50 +00:00
ArchiBot
1b047dfbff Automatic translations update 2023-10-15 02:06:55 +00:00
renovate[bot]
f9cd805304 Update ASF-ui digest to 6fab369 2023-10-14 04:22:54 +00:00
renovate[bot]
74e2ad2f40 Update github/codeql-action action to v2.22.3 2023-10-13 16:10:15 +00:00
ArchiBot
0242ed9f10 Automatic translations update 2023-10-13 02:06:53 +00:00
renovate[bot]
b31c8266eb Update github/codeql-action action to v2.22.2 2023-10-12 10:41:00 +00:00
renovate[bot]
ddd55fd4d9 Update ASF-ui digest to b7537c4 2023-10-12 07:43:41 +00:00
ArchiBot
a324a69371 Automatic translations update 2023-10-12 02:05:09 +00:00
renovate[bot]
de67195185 Update wiki digest to 944296e 2023-10-11 19:10:30 +00:00
Archi
290f16b3fe Bump 2023-10-11 19:24:27 +02:00
Archi
324d208416 Kill old refresh token when getting Expired/Expired 2023-10-11 19:22:40 +02:00
renovate[bot]
5db85aa6f1 Update ASF-ui digest to be9b550 2023-10-11 03:03:04 +00:00
ArchiBot
4d46c4c4b1 Automatic translations update 2023-10-11 02:05:26 +00:00
renovate[bot]
f93a10bcd0 Update ASF-ui digest to 184ffad 2023-10-10 13:07:08 +00:00
renovate[bot]
56c6246a20 Update ad-m/github-push-action action to v0.8.0 2023-10-10 06:05:28 +00:00
renovate[bot]
2f4c82b563 Update ASF-ui digest to 0134a94 2023-10-10 03:45:05 +00:00
ArchiBot
8bd3c8b20d Automatic translations update 2023-10-10 02:05:49 +00:00
renovate[bot]
05c93aa82b Update JetBrains/qodana-action action to v2023.2.8 2023-10-09 16:48:38 +00:00
renovate[bot]
e8babfb329 Update github/codeql-action action to v2.22.1 2023-10-09 11:28:12 +00:00
renovate[bot]
fd2d1baa1f Update ASF-ui digest to a3f5b60 2023-10-09 03:24:00 +00:00
ArchiBot
4a5b8bbf3c Automatic translations update 2023-10-09 02:05:39 +00:00
renovate[bot]
5c8ebf7ec1 Update wiki digest to b40fb89 2023-10-08 12:02:33 +00:00
renovate[bot]
d972c93072 Update ASF-ui digest to 137b040 2023-10-07 04:46:42 +00:00
ArchiBot
aa75f4204e Automatic translations update 2023-10-07 02:05:01 +00:00
renovate[bot]
5ad1815ba0 Update github/codeql-action action to v2.22.0 2023-10-06 12:32:23 +00:00
renovate[bot]
0518a35fb4 Update ASF-ui digest to cf7f1a0 2023-10-06 07:20:40 +00:00
ArchiBot
4dbd9720f2 Automatic translations update 2023-10-06 02:05:38 +00:00
renovate[bot]
4d06104306 Update ASF-ui digest to 50cc9c7 2023-10-05 18:01:57 +00:00
ArchiBot
5c6229da1b Automatic translations update 2023-10-05 02:06:46 +00:00
ArchiBot
8b661874da Automatic translations update 2023-10-04 02:06:12 +00:00
Archi
24b4c9a2f1 Bump 2023-10-03 21:19:23 +02:00
Archi
9dd1dd227f Closes #3020 2023-10-03 21:18:59 +02:00
ArchiBot
d3b1213fc5 Automatic translations update 2023-10-03 02:05:07 +00:00
Archi
603663a43c Bump 2023-10-03 02:00:57 +02:00
Archi
57cb519c44 Misc 2023-10-02 20:37:25 +02:00
Łukasz Domeradzki
3eb6cbf491 Closes #3017 (#3018)
* Closes #3017

* Allow risky discovery for non-first badge page failures

* Skip excessive risky check once game is fully farmed

* Misc
2023-10-02 20:31:59 +02:00
Archi
0396687f50 Bump 2023-10-02 16:16:46 +02:00
Archi
714cc5ae9d Closes #3019 2023-10-02 16:16:19 +02:00
renovate[bot]
4f478d829d Update github/codeql-action action to v2.21.9 2023-09-27 15:32:46 +00:00
Archi
d81144d549 Bump 2023-09-27 13:50:59 +02:00
Archi
f2563c582c Closes #3014
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
2023-09-27 13:48:00 +02:00
renovate[bot]
927715188d Update ASF-ui digest to e510667 2023-09-27 03:32:16 +00:00
renovate[bot]
03beeb97dd Update ASF-ui digest to 6bb8251 2023-09-25 17:15:56 +00:00
Archi
a3dcb252c7 Bump 2023-09-25 12:11:31 +02:00
ArchiBot
52d4b6702c Automatic translations update 2023-09-25 02:05:43 +00:00
ArchiBot
4e5cd2e380 Automatic translations update 2023-09-24 02:05:33 +00:00
Archi
af6eb07607 Add support for bot.. and ..bot syntax 2023-09-24 02:45:06 +02:00
renovate[bot]
b5ecd31666 Update ASF-ui digest to 7a96c9c 2023-09-23 05:03:47 +00:00
ArchiBot
a2e5968a49 Automatic translations update 2023-09-23 02:03:42 +00:00
renovate[bot]
23da14e77e Update actions/checkout action to v4.1.0 2023-09-22 19:24:26 +00:00
Archi
b2a5daad2c Bump 2023-09-22 14:27:19 +02:00
renovate[bot]
6c9142132c Update ASF-ui digest to 79963aa 2023-09-22 03:31:51 +00:00
ArchiBot
cd9a939fb4 Automatic translations update 2023-09-22 02:05:14 +00:00
renovate[bot]
4f8b1a542a Update ASF-ui digest to 2676e37 2023-09-21 06:47:20 +00:00
ArchiBot
230332ca79 Automatic translations update 2023-09-21 02:05:17 +00:00
renovate[bot]
b082024d0b Update wiki digest to 4cc5e13 2023-09-20 11:53:36 +00:00
Łukasz Domeradzki
af4a64b99e Closes #3007 (#3008) 2023-09-20 13:53:15 +02:00
Rudokhvist
9163b57c3e Switch to different cat api in example plugin (#3006)
* switch to different cat api

* damn forgot json constructor

* check for empty array in responce

* revert meowresponse.cs

* use firstordefault()
2023-09-20 12:40:41 +02:00
renovate[bot]
69aaca3bad Update ASF-ui digest to 5875d1f 2023-09-20 03:45:24 +00:00
ArchiBot
8272a2b929 Automatic translations update 2023-09-20 02:05:25 +00:00
renovate[bot]
4d0ba1dbcc Update ASF-ui digest to 81b18fe 2023-09-19 22:01:00 +00:00
renovate[bot]
bf46c29ff6 Update github/codeql-action action to v2.21.8 2023-09-19 11:05:21 +00:00
ArchiBot
5fb3f88a52 Automatic translations update 2023-09-19 02:05:49 +00:00
renovate[bot]
74a193d3fe Update ASF-ui digest to 8cd89bc 2023-09-18 17:37:06 +00:00
renovate[bot]
283bb789bb Update crowdin/github-action action to v1.13.1 2023-09-18 07:48:42 +00:00
ArchiBot
515f96ea8e Automatic translations update 2023-09-18 02:05:11 +00:00
renovate[bot]
b9308810d6 Update ASF-ui digest to f77258e 2023-09-17 21:27:00 +00:00
ArchiBot
68be2d2442 Automatic translations update 2023-09-17 02:05:42 +00:00
renovate[bot]
d110259388 Update ASF-ui digest to 73cc7ef 2023-09-16 22:34:09 +00:00
ArchiBot
b03cf66882 Automatic translations update 2023-09-16 02:03:57 +00:00
ArchiBot
480abe95d1 Automatic translations update 2023-09-15 02:05:37 +00:00
renovate[bot]
9408a16a5c Update ASF-ui digest to 7b71bc2 2023-09-14 22:53:42 +00:00
renovate[bot]
a8c632f98d Update github/codeql-action action to v2.21.7 2023-09-14 17:36:10 +00:00
renovate[bot]
0016fc43a9 Update ASF-ui digest to c7922a4 2023-09-14 04:51:45 +00:00
ArchiBot
05006f0ba1 Automatic translations update 2023-09-14 02:04:43 +00:00
renovate[bot]
be0682e0f5 Update dependency JustArchiNET.Madness to v3.16.0 2023-09-13 20:25:32 +00:00
renovate[bot]
4b6c657ffc Update github/codeql-action action to v2.21.6 2023-09-13 17:26:15 +00:00
ArchiBot
a8bef43a30 Automatic translations update 2023-09-13 02:05:33 +00:00
renovate[bot]
6767c879c6 Update ASF-ui digest to ce59413 2023-09-12 15:26:45 +00:00
renovate[bot]
67fdaa8314 Update docker/setup-buildx-action action to v3 (#3004)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-09-12 13:52:14 +02:00
renovate[bot]
2aed6d6143 Update docker/login-action action to v3 (#3003)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-09-12 13:51:56 +02:00
renovate[bot]
94a18ed8dd Update docker/build-push-action action to v5 (#3002)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-09-12 13:51:29 +02:00
renovate[bot]
3c9bb23957 Update crowdin/github-action action to v1.13.0 2023-09-12 07:00:30 +00:00
ArchiBot
ca418b5570 Automatic translations update 2023-09-12 02:04:17 +00:00
renovate[bot]
954e811584 Update ASF-ui digest to 70d4a77 2023-09-10 18:47:35 +00:00
renovate[bot]
6beda3f404 Update crazy-max/ghaction-import-gpg action to v6 (#3001)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-09-10 20:47:19 +02:00
ArchiBot
0cc6f4c40a Automatic translations update 2023-09-10 02:05:42 +00:00
renovate[bot]
2fcc22768c Update ASF-ui digest to a949754 2023-09-09 02:14:09 +00:00
ArchiBot
29f70918c2 Automatic translations update 2023-09-09 02:03:21 +00:00
renovate[bot]
3f5d21758c Update docker/build-push-action action to v4.2.1 2023-09-08 16:32:25 +00:00
renovate[bot]
705e17c9bb Update docker/build-push-action action to v4.2.0 2023-09-08 11:21:02 +00:00
ArchiBot
2afc59f01a Automatic translations update 2023-09-08 02:05:04 +00:00
renovate[bot]
741df3c70f Update ASF-ui digest to ea8cb61 2023-09-07 04:09:56 +00:00
ArchiBot
6c4d6c1e75 Automatic translations update 2023-09-07 02:04:47 +00:00
renovate[bot]
52aa398ec7 Update dependency NLog.Web.AspNetCore to v5.3.4 2023-09-06 22:49:15 +00:00
renovate[bot]
72c14c3180 Update actions/upload-artifact action to v3.1.3 2023-09-06 20:40:38 +00:00
ArchiBot
78d2554e80 Automatic translations update 2023-09-05 02:04:09 +00:00
renovate[bot]
a46ec87252 Update ASF-ui digest to 81fcdcd 2023-09-04 17:20:07 +00:00
Archi
4a5c1c15ce Misc 2023-09-04 19:19:48 +02:00
renovate[bot]
7fc609653a Update actions/checkout action to v4 (#2996)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-09-04 19:16:07 +02:00
renovate[bot]
48077466ab Update JetBrains/qodana-action action to v2023.2.6 2023-09-04 12:52:26 +00:00
ArchiBot
4a76f84e3a Automatic translations update 2023-09-04 02:05:10 +00:00
renovate[bot]
3ab68cb574 Update ASF-ui digest to d5893d2 2023-09-03 20:33:29 +00:00
renovate[bot]
daf616c0b3 Update crazy-max/ghaction-import-gpg action to v5.4.0 2023-09-03 14:36:40 +00:00
ArchiBot
2ea8c89037 Automatic translations update 2023-09-03 02:05:37 +00:00
Archi
bf91dc77e8 Bump 2023-09-02 23:36:42 +02:00
Archi
7778618b75 Bump 2023-09-02 23:36:20 +02:00
Archi
6cc78e805e Closes #2993 2023-09-02 23:35:00 +02:00
ArchiBot
f5b4383cdf Automatic translations update 2023-09-02 02:02:58 +00:00
ArchiBot
ab5e950972 Automatic translations update 2023-09-01 02:06:45 +00:00
renovate[bot]
a3829f84b4 Update ASF-ui digest to 3ef126f 2023-08-31 02:02:29 +00:00
Archi
4e5fdabe0b Misc 2023-08-30 14:17:59 +02:00
renovate[bot]
1e786bdea3 Update dependency Markdig.Signed to v0.33.0 2023-08-30 06:14:25 +00:00
ArchiBot
27297f6f26 Automatic translations update 2023-08-30 02:04:44 +00:00
renovate[bot]
cfebe7bd50 Update dependency Microsoft.NET.Test.Sdk to v17.7.2 2023-08-29 09:45:50 +00:00
ArchiBot
8a2ee4e22b Automatic translations update 2023-08-29 02:34:32 +00:00
renovate[bot]
c66260954c Update github/codeql-action action to v2.21.5 2023-08-28 19:26:41 +00:00
renovate[bot]
adaa1416bb Update docker/setup-buildx-action action to v2.10.0 2023-08-28 08:04:20 +00:00
renovate[bot]
f3bc0162ef Update ASF-ui digest to 5d9525f 2023-08-27 03:26:06 +00:00
renovate[bot]
7a87131651 Update ASF-ui digest to 07b1fe2 2023-08-26 00:57:47 +00:00
renovate[bot]
c945aca1b7 Update ncipollo/release-action action to v1.13.0 2023-08-24 20:17:48 +00:00
renovate[bot]
874b66d3af Update actions/checkout action to v3.6.0 2023-08-24 15:29:46 +00:00
renovate[bot]
142347ad20 Update ASF-ui digest to f31aaee 2023-08-23 17:29:17 +00:00
ArchiBot
32dca1ce33 Automatic translations update 2023-08-21 02:03:31 +00:00
renovate[bot]
36e15eccf1 Update ASF-ui digest to 7b543ae 2023-08-19 07:31:10 +00:00
renovate[bot]
b00b297def Update ASF-ui digest to 6efaee8 2023-08-17 23:01:22 +00:00
renovate[bot]
0fbf31db89 Update actions/setup-node action to v3.8.1 2023-08-17 14:30:36 +00:00
renovate[bot]
5d7a1e33b6 Update dependency Microsoft.NET.Test.Sdk to v17.7.1 2023-08-16 15:35:29 +00:00
ArchiBot
3bcf09d42e Automatic translations update 2023-08-16 02:03:55 +00:00
ArchiBot
7d19111b42 Automatic translations update 2023-08-15 02:03:44 +00:00
renovate[bot]
3667b62bee Update ASF-ui digest to 135bbb3 2023-08-14 20:30:17 +00:00
renovate[bot]
d9ff9d7a97 Update github/codeql-action action to v2.21.4 2023-08-14 17:04:43 +00:00
renovate[bot]
7ae60bdb3c Update actions/setup-node action to v3.8.0 2023-08-14 13:32:22 +00:00
ArchiBot
ecbbca0215 Automatic translations update 2023-08-14 02:04:33 +00:00
renovate[bot]
534f84b181 Update wiki digest to e1c5a2a 2023-08-13 21:18:17 +00:00
ArchiBot
894587b965 Automatic translations update 2023-08-13 02:03:57 +00:00
renovate[bot]
8f335f4182 Update ASF-ui digest to 0964964 2023-08-11 19:19:30 +00:00
ArchiBot
750c56bf08 Automatic translations update 2023-08-11 02:03:06 +00:00
Archi
c84366f9ba Misc syntax improvements 2023-08-10 21:36:17 +02:00
renovate[bot]
837e4bde8f Update ASF-ui digest to bb92516 2023-08-10 05:09:49 +00:00
renovate[bot]
525a6f2d19 Update ASF-ui digest to dd93453 2023-08-09 21:07:13 +00:00
Archi
8719a2b98b Bump 2023-08-09 23:06:53 +02:00
Vita Chumakova
a768ec43a5 Fix addlicense (#2983) 2023-08-09 23:06:11 +02:00
renovate[bot]
385ea566aa Update ASF-ui digest to 0b812a7 2023-08-09 12:06:50 +00:00
renovate[bot]
df878185c2 Update github/codeql-action action to v2.21.3 2023-08-08 14:29:55 +00:00
ArchiBot
f834ffe612 Automatic translations update 2023-08-08 02:09:46 +00:00
renovate[bot]
4e032fa129 Update ASF-ui digest to 05ff07a 2023-08-07 23:23:13 +00:00
Archi
6b116b5163 Misc 2023-08-07 18:12:50 +02:00
Archi
7ee92cb5b9 Bump 2023-08-07 17:23:53 +02:00
Archi
7181359eca Archi pls 2023-08-07 17:23:25 +02:00
Archi
386dd64cbb Bump 2023-08-07 16:42:45 +02:00
Archi
44cbafa25b Misc 2023-08-07 16:39:44 +02:00
Archi
864617801f Add GetConfirmations() to ASF API 2023-08-07 16:34:32 +02:00
ArchiBot
cbc5b57c11 Automatic translations update 2023-08-07 02:11:15 +00:00
renovate[bot]
7d70c0fe25 Update dependency NLog.Web.AspNetCore to v5.3.3 2023-08-06 18:27:19 +00:00
ArchiBot
b72ff50979 Automatic translations update 2023-08-06 02:06:21 +00:00
ArchiBot
5440fdd78f Automatic translations update 2023-08-05 02:08:15 +00:00
renovate[bot]
df60c6d4d2 Update dependency Microsoft.NET.Test.Sdk to v17.7.0 2023-08-04 16:41:12 +00:00
renovate[bot]
927a55fe2c Update dependency Markdig.Signed to v0.32.0 2023-08-04 07:59:50 +00:00
ArchiBot
00c7bfef25 Automatic translations update 2023-08-04 02:10:03 +00:00
Archi
036a754d12 Correct balance output
Balance delayed is actually additional to current, not current + pending.
2023-08-03 16:16:58 +02:00
Archi
b863d58ff7 Bump 2023-08-03 15:19:09 +02:00
renovate[bot]
03dd28b82f Update ASF-ui digest to e01b29e 2023-08-03 03:40:11 +00:00
ArchiBot
c3bc080777 Automatic translations update 2023-08-03 02:09:24 +00:00
Archi
c7610a918d Use ProcSubset=pid in systemd 2023-08-02 11:59:48 +02:00
ArchiBot
abae12b1e1 Automatic translations update 2023-08-02 02:08:30 +00:00
renovate[bot]
98349dbad2 Update ASF-ui digest to a774592 2023-08-01 16:43:07 +00:00
renovate[bot]
8ba22e5bc6 Update wiki digest to b71437c 2023-08-01 10:06:21 +00:00
ArchiBot
ab42de57fc Automatic translations update 2023-08-01 02:17:16 +00:00
renovate[bot]
790a8e8a7e Update ASF-ui digest to bc56ab4 2023-07-31 10:15:35 +00:00
Archi
be9144c7f1 Bump 2023-07-31 12:15:17 +02:00
Archi
cc4532af83 Add support for WalletBalanceDelayed, refactor SharedLibraryLockStatusCallback 2023-07-31 12:00:41 +02:00
ArchiBot
b2d836ecc0 Automatic translations update 2023-07-30 02:09:55 +00:00
renovate[bot]
28cac01019 Update ASF-ui digest to 79948b8 2023-07-29 15:49:17 +00:00
ArchiBot
10bf1d7311 Automatic translations update 2023-07-29 02:08:09 +00:00
renovate[bot]
3e26b6e7a2 Update ASF-ui digest to 5186a55 2023-07-28 21:24:08 +00:00
renovate[bot]
ce904603e4 Update github/codeql-action action to v2.21.2 2023-07-28 16:37:23 +00:00
renovate[bot]
81614e7a70 Update ASF-ui digest to c2d2bbf 2023-07-28 13:49:35 +00:00
Archi
0b48518e2a Bump 2023-07-28 11:47:44 +02:00
Archi
35364e46ca Closes #2972 2023-07-28 11:46:23 +02:00
ArchiBot
f2dae15562 Automatic translations update 2023-07-28 02:11:06 +00:00
ArchiBot
e71552645e Automatic translations update 2023-07-27 02:07:57 +00:00
renovate[bot]
acce99bf2e Update github/codeql-action action to v2.21.1 2023-07-26 19:23:14 +00:00
ArchiBot
dd14257116 Automatic translations update 2023-07-26 02:17:10 +00:00
Archi
68c17b0af9 Bump 2023-07-25 11:27:50 +02:00
Archi
556e1282a3 Misc fix 2023-07-25 11:26:22 +02:00
renovate[bot]
fd1b44cadc Update ASF-ui digest to 578e8ea 2023-07-25 04:00:12 +00:00
ArchiBot
9c18564830 Automatic translations update 2023-07-25 02:20:37 +00:00
Łukasz Domeradzki
97e8aece92 Update CONTRIBUTING.md 2023-07-25 00:17:47 +02:00
renovate[bot]
030ab53062 Update crowdin/github-action action to v1.12.0 2023-07-24 10:32:40 +00:00
renovate[bot]
2c0aa2a36c Update ASF-ui digest to b89ca92 2023-07-24 04:13:45 +00:00
ArchiBot
7a91e82a57 Automatic translations update 2023-07-24 02:15:15 +00:00
Łukasz Domeradzki
88d2924bc2 Update cc.sh 2023-07-23 12:25:14 +02:00
renovate[bot]
bae91cda04 Update ASF-ui digest to a00a24a 2023-07-23 03:35:12 +00:00
ArchiBot
173a0f18b9 Automatic translations update 2023-07-23 02:17:32 +00:00
Łukasz Domeradzki
0e8e360c7e Update cc.sh 2023-07-22 21:25:23 +02:00
renovate[bot]
afce39ca9c Update ASF-ui digest to d61d8aa 2023-07-22 09:15:32 +00:00
renovate[bot]
605897b7c5 Update ASF-ui digest to b04b006 2023-07-20 04:52:31 +00:00
ArchiBot
fc31027f9c Automatic translations update 2023-07-20 02:13:50 +00:00
renovate[bot]
48f1492aed Update github/codeql-action action to v2.21.0 2023-07-19 13:59:28 +00:00
renovate[bot]
7c9c92cced Update JetBrains/qodana-action action to v2023.2.1 2023-07-19 02:16:07 +00:00
renovate[bot]
abd9256026 Update ASF-ui digest to f02bd30 2023-07-18 07:11:00 +00:00
renovate[bot]
2a439e186a Update ASF-ui digest to 8ec6253 2023-07-17 04:18:33 +00:00
ArchiBot
60fd7d79d8 Automatic translations update 2023-07-16 02:31:22 +00:00
renovate[bot]
e1595719d8 Update ASF-ui digest to 3973226 2023-07-14 21:40:19 +00:00
renovate[bot]
5549481746 Update github/codeql-action action to v2.20.4 2023-07-14 17:41:07 +00:00
renovate[bot]
2a89cd781d Update mstest monorepo to v3.1.1 2023-07-14 10:55:29 +00:00
Archi
52ae995296 Bump 2023-07-13 22:59:45 +02:00
Archi
bceef4f74e Fix Abry potato server 2023-07-13 22:47:42 +02:00
Archi
49a64b75cf Closes #2952 2023-07-13 22:33:52 +02:00
renovate[bot]
0130fa9ee6 Update ASF-ui digest to fa3d8fc 2023-07-12 23:02:31 +00:00
renovate[bot]
29145d823b Update docker/setup-buildx-action action to v2.9.1 2023-07-12 14:24:14 +00:00
renovate[bot]
a4f16a0aa3 Update ASF-ui digest to f93d011 2023-07-10 07:55:13 +00:00
ArchiBot
7c4c82e980 Automatic translations update 2023-07-10 02:24:34 +00:00
ArchiBot
4f87744fd5 Automatic translations update 2023-07-09 02:29:01 +00:00
ArchiBot
a13f6cd23d Automatic translations update 2023-07-08 02:23:19 +00:00
renovate[bot]
a4f70c8ff4 Update ASF-ui digest to 369c14f 2023-07-07 18:18:34 +00:00
renovate[bot]
e72e894863 Update docker/setup-buildx-action action to v2.9.0 2023-07-07 11:47:37 +00:00
renovate[bot]
b4f747f990 Update ASF-ui digest to 81a232b 2023-07-07 07:14:54 +00:00
ArchiBot
a3122b9bd9 Automatic translations update 2023-07-07 02:23:44 +00:00
renovate[bot]
e935ec8bc4 Update ASF-ui digest to 9000bc1 2023-07-06 20:06:43 +00:00
renovate[bot]
421005c341 Update github/codeql-action action to v2.20.3 2023-07-06 14:08:41 +00:00
renovate[bot]
38c19510b5 Update actions/setup-node action to v3.7.0 2023-07-05 15:38:34 +00:00
renovate[bot]
88eaa3a745 Update dependency JetBrains.Annotations to v2023 (#2944)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-05 09:26:55 +02:00
ArchiBot
8d71433059 Automatic translations update 2023-07-05 02:23:34 +00:00
renovate[bot]
8cce153f0b Update dependency NLog.Web.AspNetCore to v5.3.2 2023-07-04 22:08:01 +00:00
renovate[bot]
9e32d4a00c Update ASF-ui digest to 1ff5f2a 2023-07-04 13:47:33 +00:00
ArchiBot
29fea78ee9 Automatic translations update 2023-07-04 02:23:50 +00:00
renovate[bot]
4a8260beef Update github/codeql-action action to v2.20.2 2023-07-03 12:31:21 +00:00
ArchiBot
4c73a6bc8b Automatic translations update 2023-07-03 02:23:45 +00:00
renovate[bot]
4361be279f Update ASF-ui digest to 9dd90cf 2023-07-02 04:16:48 +00:00
ArchiBot
970307b0e6 Automatic translations update 2023-07-02 02:27:22 +00:00
renovate[bot]
c908ebe192 Update dependency NLog.Web.AspNetCore to v5.3.1 2023-07-01 21:52:05 +00:00
renovate[bot]
b95c5cc584 Update ASF-ui digest to f0d7c48 2023-07-01 07:18:15 +00:00
ArchiBot
1034e7e625 Automatic translations update 2023-07-01 02:26:28 +00:00
Archi
3144bf2a98 Closes #2938 2023-06-30 10:44:29 +02:00
Archi
2ef341fc73 Misc 2023-06-30 10:30:01 +02:00
ArchiBot
b7b8c4bc1c Automatic translations update 2023-06-30 02:22:17 +00:00
renovate[bot]
7dd7e06e38 Update wiki digest to a1000c7 2023-06-29 22:36:03 +00:00
Archi
088fa2e9f9 Bump 2023-06-29 22:41:06 +02:00
Archi
3f91b18a4d Refactor confirmations
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.
2023-06-29 22:34:26 +02:00
Archi
d7722fae84 Misc code cleanup 2023-06-29 21:56:05 +02:00
ArchiBot
267f44ded0 Automatic translations update 2023-06-29 02:23:10 +00:00
renovate[bot]
cbb0ac7f4f Update ASF-ui digest to 94befd8 2023-06-28 21:25:44 +00:00
renovate[bot]
f8abcf9726 Update docker/setup-buildx-action action to v2.8.0 2023-06-28 16:34:53 +00:00
Chr_
7882f709d9 add checkout.steampowered.com to known service (#2934) 2023-06-28 13:22:35 +02:00
Ryzhehvost
003bf0e2b3 Allow plugins to override blacklisted trades (#2933) 2023-06-28 11:29:40 +02:00
renovate[bot]
1fbf8b7daf Update ASF-ui digest to 38cf6f6 2023-06-28 03:45:40 +00:00
ArchiBot
c30f7f9f0c Automatic translations update 2023-06-28 02:23:42 +00:00
renovate[bot]
4af9364109 Update dependency Microsoft.NET.Test.Sdk to v17.6.3 2023-06-27 13:46:08 +00:00
ArchiBot
7d0d8b5b43 Automatic translations update 2023-06-26 02:26:13 +00:00
Archi
5abe69162d Revert "Revert "Remove obsolete ICustomMachineInfoProvider interface""
This reverts commit 19ffe6e761.
2023-06-25 12:54:40 +02:00
Archi
549bd2f0be Revert "Back to 5.4.7.3 for a second"
This reverts commit 40a23e3c99.
2023-06-25 12:54:36 +02:00
Archi
40a23e3c99 Back to 5.4.7.3 for a second 2023-06-25 12:54:17 +02:00
Archi
19ffe6e761 Revert "Remove obsolete ICustomMachineInfoProvider interface"
This reverts commit 9d837c15b3.
2023-06-25 12:53:42 +02:00
Vincent Jansen
9e1ba20783 Fix for addlicense (#2927)
Fixing #2925 based on suggestions on that issue
2023-06-25 12:52:33 +02:00
renovate[bot]
cf70b5ff6c Update ASF-ui digest to 34e52e3 2023-06-25 04:19:47 +00:00
ArchiBot
9bbe4e73c6 Automatic translations update 2023-06-25 02:31:26 +00:00
renovate[bot]
1001d3ce49 Update ASF-ui digest to f86fb28 2023-06-24 22:38:32 +00:00
ArchiBot
aeffa85b87 Automatic translations update 2023-06-24 02:23:20 +00:00
Archi
9d837c15b3 Remove obsolete ICustomMachineInfoProvider interface 2023-06-23 12:22:57 +02:00
Archi
ae7793193d Bump 2023-06-23 12:20:57 +02:00
ArchiBot
5c8976313f Automatic translations update 2023-06-23 02:23:50 +00:00
Jack Nolddor
5e53073e8f Blacklist Summer Sale 2023 appid (#2923) 2023-06-22 16:38:10 +02:00
renovate[bot]
b06aac0024 Update ASF-ui digest to c56f3a3 2023-06-22 02:32:07 +00:00
ArchiBot
54a0857429 Automatic translations update 2023-06-22 02:19:28 +00:00
renovate[bot]
eac12de92e Update github/codeql-action action to v2.20.1 2023-06-21 12:44:12 +00:00
renovate[bot]
ad1ce5be9c Update JetBrains/qodana-action action to v2023.1.5 2023-06-21 11:13:39 +00:00
Archi
b99d7bc78f Bump 2023-06-21 13:13:21 +02:00
Vita Chumakova
7132340ff7 Fix 2FA endpoints (#2921)
* Fix 2FA endpoints

* use fields in data classes

* optimize usings
2023-06-21 13:11:38 +02:00
renovate[bot]
327f988512 Update ASF-ui digest to 0dc9b31 2023-06-21 04:50:24 +00:00
renovate[bot]
8b0449889d Update peter-evans/dockerhub-description action to v3.4.2 2023-06-20 09:20:57 +00:00
ArchiBot
512fa86f83 Automatic translations update 2023-06-20 02:16:44 +00:00
ArchiBot
e7933257bc Automatic translations update 2023-06-19 02:19:27 +00:00
renovate[bot]
e57c041c33 Update ASF-ui digest to 42351b3 2023-06-18 18:07:55 +00:00
renovate[bot]
93c9c4a7d8 Update ASF-ui digest to 92587e8 2023-06-18 04:12:29 +00:00
ArchiBot
76cc33cabf Automatic translations update 2023-06-18 02:23:20 +00:00
renovate[bot]
707579a262 Update wiki digest to c906bac 2023-06-17 21:49:29 +00:00
renovate[bot]
fea70fd0cc Update ASF-ui digest to 83851d9 2023-06-17 05:04:50 +00:00
ArchiBot
9d8821908a Automatic translations update 2023-06-17 02:16:48 +00:00
renovate[bot]
af6602c217 Update ASF-ui digest to 6933718 2023-06-16 17:21:22 +00:00
renovate[bot]
2b84d411e7 Update crowdin/github-action action to v1.11.0 2023-06-16 11:07:09 +00:00
renovate[bot]
e0b26e002d Update ASF-ui digest to bca4a3e 2023-06-15 15:29:18 +00:00
renovate[bot]
ab85699104 Update ASF-ui digest to 30025b9 2023-06-15 05:13:50 +00:00
ArchiBot
d9c8acb67b Automatic translations update 2023-06-15 02:18:22 +00:00
renovate[bot]
46355fac5e Update ASF-ui digest to 4ce6c25 2023-06-14 21:25:42 +00:00
renovate[bot]
a9f6319272 Update ASF-ui digest to 58396d8 2023-06-14 14:33:19 +00:00
renovate[bot]
cfceb26ad5 Update github/codeql-action action to v2.20.0 2023-06-14 04:49:20 +00:00
ArchiBot
599dcfe104 Automatic translations update 2023-06-14 02:19:06 +00:00
renovate[bot]
9921a779cb Update docker/setup-buildx-action action to v2.7.0 2023-06-13 23:16:14 +00:00
renovate[bot]
934ffc71c9 Update wiki digest to 9e96334 2023-06-13 18:55:52 +00:00
renovate[bot]
0e2db4466b Update JetBrains/qodana-action action to v2023.1.4 2023-06-13 17:44:34 +00:00
renovate[bot]
e50b197c67 Update docker/build-push-action action to v4.1.1 2023-06-13 12:03:33 +00:00
renovate[bot]
b0c0beb042 Update crowdin/github-action action to v1.10.0 2023-06-13 07:36:27 +00:00
ArchiBot
cc08c4112a Automatic translations update 2023-06-13 02:18:57 +00:00
renovate[bot]
f26dedd7c9 Update dessant/lock-threads action to v4.0.1 2023-06-12 18:22:16 +00:00
ArchiBot
063a317783 Automatic translations update 2023-06-12 02:22:41 +00:00
renovate[bot]
17d3963c0f Update ASF-ui digest to 47a7a26 2023-06-10 18:23:15 +00:00
Archi
b0239fb291 Merge branch 'main' of https://github.com/JustArchiNET/ArchiSteamFarm 2023-06-10 20:22:46 +02:00
Archi
46fffb9d4a Add handling of EPurchaseResultDetail.NoWallet 2023-06-10 20:22:38 +02:00
ArchiBot
c55bc1d4c2 Automatic translations update 2023-06-10 02:20:19 +00:00
renovate[bot]
0c3411e2f6 Update actions/checkout action to v3.5.3 2023-06-09 16:48:51 +00:00
renovate[bot]
bccc2b49b3 Update docker/build-push-action action to v4.1.0 2023-06-09 13:11:51 +00:00
renovate[bot]
0ca413529d Update ASF-ui digest to 2087d17 2023-06-09 00:31:12 +00:00
renovate[bot]
54179d79b6 Update ASF-ui digest to c4cf465 2023-06-08 07:59:05 +00:00
renovate[bot]
aec802c192 Update ASF-ui digest to 3577b45 2023-06-08 02:59:52 +00:00
renovate[bot]
0814d68ad2 Update docker/setup-buildx-action action to v2.6.0 2023-06-07 19:37:13 +00:00
Archi
773d8e29d0 Bump 2023-06-07 21:36:50 +02:00
Archi
89bdac0692 Retry accepted/rejected trade offers
Very often, despite accepting the trade offer AND confirmation, the trade stays in active state for who knows what reason. ASF has built-in mechanism to not evaluate trade offers that it already did, which we do need and is good e.g. for ignored trade offers. Make it so accepted/rejected are retried as well, since they should not pop up again once we're done with them.
2023-06-07 21:28:04 +02:00
renovate[bot]
3316b15808 Update docker/login-action action to v2.2.0 2023-06-07 14:58:39 +00:00
renovate[bot]
b3016db56c Update dependency Microsoft.NET.Test.Sdk to v17.6.2 2023-06-06 15:14:35 +00:00
renovate[bot]
6d08fa9311 Update dependency AngleSharp.XPath to v2.0.3 2023-06-06 11:54:10 +00:00
renovate[bot]
7a9ec4852c Update ASF-ui digest to 355c868 2023-06-05 22:37:45 +00:00
renovate[bot]
65239d2c50 Update ASF-ui digest to 3929273 (#2909)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-05 09:18:02 +02:00
Archi
57ac7e2816 Bullet-proof against invalid Steam items 2023-06-04 16:39:16 +02:00
renovate[bot]
3143fc8f09 Update ASF-ui digest to 899f763 2023-06-04 07:46:30 +00:00
ArchiBot
ece24772df Automatic translations update 2023-06-04 02:30:59 +00:00
renovate[bot]
903c1cccaa Update ASF-ui digest to 0e1dac8 2023-06-03 18:10:11 +00:00
Archi
09ff465f4a Misc 2023-06-03 17:56:27 +02:00
Archi
665309a890 Bump 2023-06-03 17:52:58 +02:00
300 changed files with 9762 additions and 6371 deletions

View File

@@ -36,7 +36,7 @@ csharp_prefer_simple_default_expression = true:warning
csharp_prefer_simple_using_statement = true:warning
csharp_prefer_static_local_function = true:warning
csharp_preferred_modifier_order = public, protected, internal, private, static, extern, new, virtual, abstract, sealed, override, readonly, unsafe, volatile, async:warning
csharp_preferred_modifier_order = public, protected, internal, private, file, static, extern, new, virtual, abstract, sealed, override, readonly, unsafe, required, volatile, async:warning
csharp_preserve_single_line_blocks = true
csharp_preserve_single_line_statements = false
@@ -61,7 +61,6 @@ csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
csharp_space_between_method_declaration_name_and_open_parenthesis = false
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_parentheses = none
csharp_space_between_square_brackets = false
csharp_style_conditional_delegate_call = true:warning
@@ -79,15 +78,27 @@ csharp_style_expression_bodied_properties = true:warning
csharp_style_implicit_object_creation_when_type_is_apparent = true:warning
csharp_style_inlined_variable_declaration = true:warning
csharp_style_pattern_local_over_anonymous_function = true:warning
csharp_style_namespace_declarations = file_scoped:warning
csharp_style_pattern_matching_over_as_with_null_check = true:warning
csharp_style_pattern_matching_over_is_with_cast_check = true:warning
csharp_style_prefer_extended_property_pattern = true:warning
dotnet_style_prefer_foreach_explicit_cast_in_source = always:warning
csharp_style_prefer_index_operator = true:warning
csharp_style_prefer_local_over_anonymous_function = true:warning
csharp_style_prefer_method_group_conversion = true:warning
csharp_style_prefer_not_pattern = true:warning
csharp_style_prefer_null_check_over_type_check = true:warning
csharp_style_prefer_pattern_matching = true:warning
csharp_style_prefer_primary_constructors = true:warning
csharp_style_prefer_range_operator = true:warning
csharp_style_prefer_readonly_struct = true:warning
csharp_style_prefer_readonly_struct_member = true:warning
csharp_style_prefer_switch_expression = true:warning
csharp_style_prefer_top_level_statements = false:warning
csharp_style_prefer_tuple_swap = true:warning
csharp_style_prefer_utf8_string_literals = true:warning
csharp_style_throw_expression = true:warning
@@ -98,13 +109,12 @@ csharp_style_var_elsewhere = false:warning
csharp_style_var_for_built_in_types = false:warning
csharp_style_var_when_type_is_apparent = false:warning
csharp_using_directive_placement = outside_namespace
csharp_using_directive_placement = outside_namespace:warning
###############################
# .NET Coding Conventions #
###############################
[*.{cs,vb}]
dotnet_analyzer_diagnostic.severity = warning
dotnet_code_quality.ca3003.excluded_symbol_names = BotController
@@ -114,6 +124,7 @@ dotnet_code_quality_unused_parameters = all:warning
dotnet_diagnostic.ca1028.severity = silent
dotnet_diagnostic.ca1031.severity = silent
dotnet_diagnostic.ca1863.severity = silent
# Rule - almost everything
dotnet_naming_rule.almost_everything_must_be_pascal_case.severity = warning
@@ -185,6 +196,7 @@ dotnet_sort_system_directives_first = true
dotnet_style_coalesce_expression = true:warning
dotnet_style_collection_initializer = true:warning
dotnet_style_explicit_tuple_names = true:warning
dotnet_style_namespace_match_folder = true:warning
dotnet_style_null_propagation = true:warning
dotnet_style_object_initializer = true:warning
@@ -218,7 +230,7 @@ dotnet_style_require_accessibility_modifiers = always:warning
# JetBrains, IntelliJ/Rider #
###############################
[*.{csproj,props,xml}]
[*.{csproj,props,resx,xml}]
ij_xml_keep_blank_lines = 1
ij_xml_keep_line_breaks = false
ij_xml_keep_line_breaks_in_text = false

View File

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

View File

@@ -12,6 +12,8 @@ body:
required: true
- 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

View File

@@ -12,6 +12,8 @@ body:
required: true
- 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

4
.github/crowdin.yml vendored
View File

@@ -5,6 +5,7 @@
"source": "/ArchiSteamFarm/Localization/Strings.resx",
"translation": "/ArchiSteamFarm/Localization/Strings.%locale%.resx",
"translation_replace": {
".bs-BA.resx": ".bs-Latn.resx",
".lol-US.resx": ".qps-Ploc.resx",
".sr-CS.resx": ".sr-Latn.resx",
".zh-CN.resx": ".zh-Hans.resx",
@@ -16,6 +17,7 @@
"source": "/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Localization/Strings.resx",
"translation": "/ArchiSteamFarm.OfficialPlugins.ItemsMatcher/Localization/Strings.%locale%.resx",
"translation_replace": {
".bs-BA.resx": ".bs-Latn.resx",
".lol-US.resx": ".qps-Ploc.resx",
".sr-CS.resx": ".sr-Latn.resx",
".zh-CN.resx": ".zh-Hans.resx",
@@ -27,6 +29,7 @@
"source": "/ArchiSteamFarm.OfficialPlugins.MobileAuthenticator/Localization/Strings.resx",
"translation": "/ArchiSteamFarm.OfficialPlugins.MobileAuthenticator/Localization/Strings.%locale%.resx",
"translation_replace": {
".bs-BA.resx": ".bs-Latn.resx",
".lol-US.resx": ".qps-Ploc.resx",
".sr-CS.resx": ".sr-Latn.resx",
".zh-CN.resx": ".zh-Hans.resx",
@@ -38,6 +41,7 @@
"source": "/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/Localization/Strings.resx",
"translation": "/ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/Localization/Strings.%locale%.resx",
"translation_replace": {
".bs-BA.resx": ".bs-Latn.resx",
".lol-US.resx": ".qps-Ploc.resx",
".sr-CS.resx": ".sr-Latn.resx",
".zh-CN.resx": ".zh-Hans.resx",

View File

@@ -5,7 +5,9 @@ on: [push, pull_request]
env:
DOTNET_CLI_TELEMETRY_OPTOUT: true
DOTNET_NOLOGO: true
DOTNET_SDK_VERSION: 7.0.x
DOTNET_SDK_VERSION: 8.0
permissions: {}
jobs:
main:
@@ -19,12 +21,13 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v3.5.2
uses: actions/checkout@v4.1.1
with:
show-progress: false
submodules: recursive
- name: Setup .NET Core
uses: actions/setup-dotnet@v3.2.0
uses: actions/setup-dotnet@v4.0.0
with:
dotnet-version: ${{ env.DOTNET_SDK_VERSION }}
@@ -39,7 +42,7 @@ jobs:
- name: Upload latest strings for translation on Crowdin
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && matrix.configuration == 'Release' && startsWith(matrix.os, 'ubuntu-') }}
uses: crowdin/github-action@v1.9.0
uses: crowdin/github-action@v1.19.0
with:
crowdin_branch_name: main
config: '.github/crowdin.yml'

View File

@@ -6,22 +6,40 @@ env:
DOTNET_CLI_TELEMETRY_OPTOUT: true
DOTNET_NOLOGO: true
permissions:
checks: write
contents: write
pull-requests: write
security-events: write
jobs:
main:
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3.5.2
if: github.event_name != 'pull_request'
uses: actions/checkout@v4.1.1
with:
show-progress: false
- name: Checkout code (for PR)
if: github.event_name == 'pull_request'
uses: actions/checkout@v4.1.1
with:
fetch-depth: 100 # History is required for pull request analysis
ref: ${{ github.event.pull_request.head.sha }} # To check out the actual pull request commit, not the merge commit
show-progress: false
- name: Run Qodana scan
uses: JetBrains/qodana-action@v2023.1.0
uses: JetBrains/qodana-action@v2023.3.1
with:
args: --property=idea.headless.enable.statistics=false
env:
QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
- name: Report Qodana results to GitHub
uses: github/codeql-action/upload-sarif@v2.3.6
uses: github/codeql-action/upload-sarif@v3.24.5
with:
sarif_file: ${{ runner.temp }}/qodana/results/qodana.sarif.json

View File

@@ -5,6 +5,8 @@ on: [push, pull_request]
env:
PLATFORMS: linux/amd64,linux/arm,linux/arm64
permissions: {}
jobs:
main:
strategy:
@@ -17,15 +19,16 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v3.5.2
uses: actions/checkout@v4.1.1
with:
show-progress: false
submodules: recursive
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2.5.0
uses: docker/setup-buildx-action@v3.0.0
- name: Build ${{ matrix.configuration }} Docker image from ${{ matrix.file }}
uses: docker/build-push-action@v4.0.0
uses: docker/build-push-action@v5.1.0
with:
context: .
file: ${{ matrix.file }}

View File

@@ -9,28 +9,32 @@ env:
PLATFORMS: linux/amd64,linux/arm,linux/arm64
TAG: latest
permissions:
packages: write
jobs:
main:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3.5.2
uses: actions/checkout@v4.1.1
with:
show-progress: false
submodules: recursive
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2.5.0
uses: docker/setup-buildx-action@v3.0.0
- name: Login to ghcr.io
uses: docker/login-action@v2.1.0
uses: docker/login-action@v3.0.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Login to DockerHub
uses: docker/login-action@v2.1.0
uses: docker/login-action@v3.0.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
@@ -55,7 +59,7 @@ jobs:
echo "DH_REPOSITORY=$(echo ${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }} | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_ENV"
- name: Build and publish Docker image from Dockerfile.Service
uses: docker/build-push-action@v4.0.0
uses: docker/build-push-action@v5.1.0
with:
context: .
file: Dockerfile.Service

View File

@@ -10,28 +10,32 @@ env:
PLATFORMS: linux/amd64,linux/arm,linux/arm64
TAG: main
permissions:
packages: write
jobs:
main:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3.5.2
uses: actions/checkout@v4.1.1
with:
show-progress: false
submodules: recursive
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2.5.0
uses: docker/setup-buildx-action@v3.0.0
- name: Login to ghcr.io
uses: docker/login-action@v2.1.0
uses: docker/login-action@v3.0.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Login to DockerHub
uses: docker/login-action@v2.1.0
uses: docker/login-action@v3.0.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
@@ -55,7 +59,7 @@ jobs:
echo "DH_REPOSITORY=$(echo ${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }} | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_ENV"
- name: Build and publish Docker image from Dockerfile
uses: docker/build-push-action@v4.0.0
uses: docker/build-push-action@v5.1.0
with:
context: .
platforms: ${{ env.PLATFORMS }}
@@ -70,7 +74,7 @@ jobs:
push: true
- name: Update DockerHub repository description
uses: peter-evans/dockerhub-description@v3.4.1
uses: peter-evans/dockerhub-description@v4.0.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}

View File

@@ -10,28 +10,32 @@ env:
PLATFORMS: linux/amd64,linux/arm,linux/arm64
TAG: released
permissions:
packages: write
jobs:
main:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3.5.2
uses: actions/checkout@v4.1.1
with:
show-progress: false
submodules: recursive
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2.5.0
uses: docker/setup-buildx-action@v3.0.0
- name: Login to ghcr.io
uses: docker/login-action@v2.1.0
uses: docker/login-action@v3.0.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Login to DockerHub
uses: docker/login-action@v2.1.0
uses: docker/login-action@v3.0.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
@@ -56,7 +60,7 @@ jobs:
echo "DH_REPOSITORY=$(echo ${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }} | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_ENV"
- name: Build and publish Docker image from Dockerfile
uses: docker/build-push-action@v4.0.0
uses: docker/build-push-action@v5.1.0
with:
context: .
platforms: ${{ env.PLATFORMS }}

View File

@@ -3,13 +3,23 @@ name: ASF-lock-threads
on:
schedule:
- cron: '0 1 * * *'
workflow_dispatch:
permissions:
discussions: write
issues: write
pull-requests: write
concurrency:
group: lock-threads
jobs:
lock:
runs-on: ubuntu-latest
steps:
- name: Lock inactive threads
uses: dessant/lock-threads@v4.0.0
uses: dessant/lock-threads@v5.0.1
with:
discussion-inactive-days: 90
issue-inactive-days: 60
pr-inactive-days: 60

View File

@@ -4,24 +4,27 @@ on: [push, pull_request]
env:
CONFIGURATION: Release
DOTNET_SDK_VERSION: 7.0.x
NET_CORE_VERSION: net7.0
NET_FRAMEWORK_VERSION: net481
DOTNET_CLI_TELEMETRY_OPTOUT: true
DOTNET_NOLOGO: true
DOTNET_SDK_VERSION: 8.0
NODE_JS_VERSION: 'lts/*'
PLUGINS: ArchiSteamFarm.OfficialPlugins.ItemsMatcher ArchiSteamFarm.OfficialPlugins.MobileAuthenticator ArchiSteamFarm.OfficialPlugins.SteamTokenDumper
permissions: {}
jobs:
publish-asf-ui:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3.5.2
uses: actions/checkout@v4.1.1
with:
show-progress: false
submodules: recursive
- name: Setup Node.js with npm
uses: actions/setup-node@v3.6.0
uses: actions/setup-node@v4.0.2
with:
check-latest: true
node-version: ${{ env.NODE_JS_VERSION }}
@@ -39,7 +42,7 @@ jobs:
run: npm run-script deploy --no-progress --prefix ASF-ui
- name: Upload ASF-ui
uses: actions/upload-artifact@v3.1.2
uses: actions/upload-artifact@v4.3.1
with:
name: ASF-ui
path: ASF-ui/dist
@@ -53,8 +56,6 @@ jobs:
include:
- os: ubuntu-latest
variant: generic
- os: windows-latest
variant: generic-netf
- os: ubuntu-latest
variant: linux-arm
- os: ubuntu-latest
@@ -72,16 +73,14 @@ jobs:
runs-on: ${{ matrix.os }}
env:
DOTNET_CLI_TELEMETRY_OPTOUT: true
DOTNET_NOLOGO: true
steps:
- name: Checkout code
uses: actions/checkout@v3.5.2
uses: actions/checkout@v4.1.1
with:
show-progress: false
- name: Setup .NET Core
uses: actions/setup-dotnet@v3.2.0
uses: actions/setup-dotnet@v4.0.0
with:
dotnet-version: ${{ env.DOTNET_SDK_VERSION }}
@@ -89,7 +88,7 @@ jobs:
run: dotnet --info
- name: Download previously built ASF-ui
uses: actions/download-artifact@v3.0.2
uses: actions/download-artifact@v4.1.2
with:
name: ASF-ui
path: ASF-ui/dist
@@ -128,19 +127,15 @@ jobs:
- name: Prepare for publishing on Unix
if: startsWith(matrix.os, 'macos-') || startsWith(matrix.os, 'ubuntu-')
env:
TARGET_FRAMEWORK: ${{ (endsWith(matrix.variant, '-netf') && env.NET_FRAMEWORK_VERSION) || env.NET_CORE_VERSION }}
shell: bash
run: |
set -euo pipefail
dotnet restore
dotnet build ArchiSteamFarm -c "$CONFIGURATION" -f "$TARGET_FRAMEWORK" -p:ContinuousIntegrationBuild=true -p:TargetLatestRuntimePatch=false -p:UseAppHost=false --no-restore --nologo
dotnet build ArchiSteamFarm -c "$CONFIGURATION" -p:ContinuousIntegrationBuild=true -p:TargetLatestRuntimePatch=false -p:UseAppHost=false --nologo
- name: Prepare for publishing on Windows
if: startsWith(matrix.os, 'windows-')
env:
TARGET_FRAMEWORK: ${{ (endsWith(matrix.variant, '-netf') && env.NET_FRAMEWORK_VERSION) || env.NET_CORE_VERSION }}
shell: pwsh
run: |
Set-StrictMode -Version Latest
@@ -148,7 +143,16 @@ jobs:
$ProgressPreference = 'SilentlyContinue'
dotnet restore
dotnet build ArchiSteamFarm -c "$env:CONFIGURATION" -f "$env:TARGET_FRAMEWORK" -p:ContinuousIntegrationBuild=true -p:TargetLatestRuntimePatch=false -p:UseAppHost=false --no-restore --nologo
if ($LastExitCode -ne 0) {
throw "Last command failed."
}
dotnet build ArchiSteamFarm -c "$env:CONFIGURATION" -p:ContinuousIntegrationBuild=true -p:TargetLatestRuntimePatch=false -p:UseAppHost=false --nologo
if ($LastExitCode -ne 0) {
throw "Last command failed."
}
- name: Prepare ArchiSteamFarm.OfficialPlugins.SteamTokenDumper on Unix
if: startsWith(matrix.os, 'macos-') || startsWith(matrix.os, 'ubuntu-')
@@ -181,13 +185,12 @@ jobs:
if: startsWith(matrix.os, 'macos-') || startsWith(matrix.os, 'ubuntu-')
env:
MAX_JOBS: 4
TARGET_FRAMEWORK: ${{ (endsWith(matrix.variant, '-netf') && env.NET_FRAMEWORK_VERSION) || env.NET_CORE_VERSION }}
shell: bash
run: |
set -euo pipefail
publish() {
dotnet publish "$1" -c "$CONFIGURATION" -f "$TARGET_FRAMEWORK" -o "out/${1}/${TARGET_FRAMEWORK}" -p:ContinuousIntegrationBuild=true -p:TargetLatestRuntimePatch=false -p:UseAppHost=false --no-restore --nologo
dotnet publish "$1" -c "$CONFIGURATION" -o "out/${1}" -p:ContinuousIntegrationBuild=true -p:TargetLatestRuntimePatch=false -p:UseAppHost=false --no-restore --nologo
}
for plugin in $PLUGINS; do
@@ -204,7 +207,6 @@ jobs:
if: startsWith(matrix.os, 'windows-')
env:
MAX_JOBS: 4
TARGET_FRAMEWORK: ${{ (endsWith(matrix.variant, '-netf') && env.NET_FRAMEWORK_VERSION) || env.NET_CORE_VERSION }}
shell: pwsh
run: |
Set-StrictMode -Version Latest
@@ -220,7 +222,7 @@ jobs:
Set-Location "$env:GITHUB_WORKSPACE"
dotnet publish "$plugin" -c "$env:CONFIGURATION" -f "$env:TARGET_FRAMEWORK" -o "out\$plugin\$env:TARGET_FRAMEWORK" -p:ContinuousIntegrationBuild=true -p:TargetLatestRuntimePatch=false -p:UseAppHost=false --no-restore --nologo
dotnet publish "$plugin" -c "$env:CONFIGURATION" -o "out\$plugin" -p:ContinuousIntegrationBuild=true -p:TargetLatestRuntimePatch=false -p:UseAppHost=false --no-restore --nologo
if ($LastExitCode -ne 0) {
throw "Last command failed."
@@ -245,7 +247,6 @@ jobs:
- name: Publish ASF-${{ matrix.variant }} on Unix
if: startsWith(matrix.os, 'macos-') || startsWith(matrix.os, 'ubuntu-')
env:
TARGET_FRAMEWORK: ${{ (endsWith(matrix.variant, '-netf') && env.NET_FRAMEWORK_VERSION) || env.NET_CORE_VERSION }}
VARIANT: ${{ matrix.variant }}
shell: bash
run: |
@@ -257,13 +258,13 @@ jobs:
variantArgs="-p:PublishSingleFile=true -p:PublishTrimmed=true -r $VARIANT --self-contained"
fi
dotnet publish ArchiSteamFarm -c "$CONFIGURATION" -f "$TARGET_FRAMEWORK" -o "out/${VARIANT}" "-p:ASFVariant=${VARIANT}" -p:ContinuousIntegrationBuild=true --no-restore --nologo $variantArgs
dotnet publish ArchiSteamFarm -c "$CONFIGURATION" -o "out/${VARIANT}" "-p:ASFVariant=${VARIANT}" -p:ContinuousIntegrationBuild=true --nologo $variantArgs
# If we're including official plugins for this framework, copy them to output directory
for plugin in $PLUGINS; do
if [ -d "out/${plugin}/${TARGET_FRAMEWORK}" ]; then
if [ -d "out/${plugin}" ]; then
mkdir -p "out/${VARIANT}/plugins/${plugin}"
cp -pR "out/${plugin}/${TARGET_FRAMEWORK}/"* "out/${VARIANT}/plugins/${plugin}"
cp -pR "out/${plugin}/"* "out/${VARIANT}/plugins/${plugin}"
fi
done
@@ -325,7 +326,6 @@ jobs:
- name: Publish ASF-${{ matrix.variant }} on Windows
if: startsWith(matrix.os, 'windows-')
env:
TARGET_FRAMEWORK: ${{ (endsWith(matrix.variant, '-netf') && env.NET_FRAMEWORK_VERSION) || env.NET_CORE_VERSION }}
VARIANT: ${{ matrix.variant }}
shell: pwsh
run: |
@@ -339,7 +339,7 @@ jobs:
$variantArgs = '-p:PublishSingleFile=true', '-p:PublishTrimmed=true', '-r', "$env:VARIANT", '--self-contained'
}
dotnet publish ArchiSteamFarm -c "$env:CONFIGURATION" -f "$env:TARGET_FRAMEWORK" -o "out\$env:VARIANT" "-p:ASFVariant=$env:VARIANT" -p:ContinuousIntegrationBuild=true --no-restore --nologo $variantArgs
dotnet publish ArchiSteamFarm -c "$env:CONFIGURATION" -o "out\$env:VARIANT" "-p:ASFVariant=$env:VARIANT" -p:ContinuousIntegrationBuild=true --nologo $variantArgs
if ($LastExitCode -ne 0) {
throw "Last command failed."
@@ -347,12 +347,12 @@ jobs:
# If we're including official plugins for this framework, copy them to output directory
foreach ($plugin in $env:PLUGINS.Split([char[]] $null, [System.StringSplitOptions]::RemoveEmptyEntries)) {
if (Test-Path "out\$plugin\$env:TARGET_FRAMEWORK" -PathType Container) {
if (Test-Path "out\$plugin" -PathType Container) {
if (!(Test-Path "out\$env:VARIANT\plugins\$plugin" -PathType Container)) {
New-Item -ItemType Directory -Path "out\$env:VARIANT\plugins\$plugin" > $null
}
Copy-Item "out\$plugin\$env:TARGET_FRAMEWORK\*" "out\$env:VARIANT\plugins\$plugin" -Recurse
Copy-Item "out\$plugin\*" "out\$env:VARIANT\plugins\$plugin" -Recurse
}
}
@@ -405,7 +405,7 @@ jobs:
}
- name: Upload ASF-${{ matrix.variant }}
uses: actions/upload-artifact@v3.1.2
uses: actions/upload-artifact@v4.3.1
with:
name: ${{ matrix.os }}_ASF-${{ matrix.variant }}
path: out/ASF-${{ matrix.variant }}.zip
@@ -415,66 +415,65 @@ jobs:
needs: publish-asf
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout code
uses: actions/checkout@v3.5.2
uses: actions/checkout@v4.1.1
with:
show-progress: false
- name: Download ASF-generic artifact from ubuntu-latest
uses: actions/download-artifact@v3.0.2
uses: actions/download-artifact@v4.1.2
with:
name: ubuntu-latest_ASF-generic
path: out
- name: Download ASF-generic-netf artifact from windows-latest
uses: actions/download-artifact@v3.0.2
with:
name: windows-latest_ASF-generic-netf
path: out
- name: Download ASF-linux-arm artifact from ubuntu-latest
uses: actions/download-artifact@v3.0.2
uses: actions/download-artifact@v4.1.2
with:
name: ubuntu-latest_ASF-linux-arm
path: out
- name: Download ASF-linux-arm64 artifact from ubuntu-latest
uses: actions/download-artifact@v3.0.2
uses: actions/download-artifact@v4.1.2
with:
name: ubuntu-latest_ASF-linux-arm64
path: out
- name: Download ASF-linux-x64 artifact from ubuntu-latest
uses: actions/download-artifact@v3.0.2
uses: actions/download-artifact@v4.1.2
with:
name: ubuntu-latest_ASF-linux-x64
path: out
- name: Download ASF-osx-arm64 artifact from macos-latest
uses: actions/download-artifact@v3.0.2
uses: actions/download-artifact@v4.1.2
with:
name: macos-latest_ASF-osx-arm64
path: out
- name: Download ASF-osx-x64 artifact from macos-latest
uses: actions/download-artifact@v3.0.2
uses: actions/download-artifact@v4.1.2
with:
name: macos-latest_ASF-osx-x64
path: out
- name: Download ASF-win-arm64 artifact from windows-latest
uses: actions/download-artifact@v3.0.2
uses: actions/download-artifact@v4.1.2
with:
name: windows-latest_ASF-win-arm64
path: out
- name: Download ASF-win-x64 artifact from windows-latest
uses: actions/download-artifact@v3.0.2
uses: actions/download-artifact@v4.1.2
with:
name: windows-latest_ASF-win-x64
path: out
- name: Import GPG key for signing
uses: crazy-max/ghaction-import-gpg@v5.3.0
uses: crazy-max/ghaction-import-gpg@v6.1.0
with:
gpg_private_key: ${{ secrets.ARCHIBOT_GPG_PRIVATE_KEY }}
@@ -491,19 +490,19 @@ jobs:
)
- name: Upload SHA512SUMS
uses: actions/upload-artifact@v3.1.2
uses: actions/upload-artifact@v4.3.1
with:
name: SHA512SUMS
path: out/SHA512SUMS
- name: Upload SHA512SUMS.sign
uses: actions/upload-artifact@v3.1.2
uses: actions/upload-artifact@v4.3.1
with:
name: SHA512SUMS.sign
path: out/SHA512SUMS.sign
- name: Create ArchiSteamFarm GitHub release
uses: ncipollo/release-action@v1.12.0
uses: ncipollo/release-action@v1.14.0
with:
artifacts: "out/*"
bodyFile: .github/RELEASE_TEMPLATE.md

View File

@@ -4,14 +4,18 @@ on:
schedule:
- cron: '55 1 * * *'
permissions:
contents: write
jobs:
update:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3.5.2
uses: actions/checkout@v4.1.1
with:
show-progress: false
submodules: recursive
token: ${{ secrets.ARCHIBOT_GITHUB_TOKEN }}
@@ -26,7 +30,7 @@ jobs:
git reset --hard origin/master
- name: Download latest translations from Crowdin
uses: crowdin/github-action@v1.9.0
uses: crowdin/github-action@v1.19.0
with:
upload_sources: false
download_translations: true
@@ -38,7 +42,7 @@ jobs:
token: ${{ secrets.ASF_CROWDIN_API_TOKEN }}
- name: Import GPG key for signing
uses: crazy-max/ghaction-import-gpg@v5.3.0
uses: crazy-max/ghaction-import-gpg@v6.1.0
with:
gpg_private_key: ${{ secrets.ARCHIBOT_GPG_PRIVATE_KEY }}
git_config_global: true
@@ -59,7 +63,7 @@ jobs:
fi
- name: Push changes to wiki
uses: ad-m/github-push-action@v0.6.0
uses: ad-m/github-push-action@v0.8.0
with:
github_token: ${{ secrets.ARCHIBOT_GITHUB_TOKEN }}
branch: master
@@ -78,7 +82,7 @@ jobs:
fi
- name: Push changes to ASF
uses: ad-m/github-push-action@v0.6.0
uses: ad-m/github-push-action@v0.8.0
with:
github_token: ${{ secrets.ARCHIBOT_GITHUB_TOKEN }}
branch: ${{ github.ref }}

2
ASF-ui

Submodule ASF-ui updated: 65d1088c8b...f916fb3405

View File

@@ -6,18 +6,10 @@
<ItemGroup>
<PackageReference Include="ConfigureAwaitChecker.Analyzer" PrivateAssets="all" />
<PackageReference Include="JetBrains.Annotations" PrivateAssets="all" />
<PackageReference Include="Newtonsoft.Json" IncludeAssets="compile" />
<PackageReference Include="SteamKit2" IncludeAssets="compile" />
<PackageReference Include="System.Composition.AttributedModel" IncludeAssets="compile" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net481' OR '$(TargetFramework)' == 'netstandard2.1'">
<!-- Madness is already included in netf build of ASF, so we don't need to emit it ourselves -->
<PackageReference Update="JustArchiNET.Madness" IncludeAssets="compile" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" IncludeAssets="compile" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ArchiSteamFarm\ArchiSteamFarm.csproj" ExcludeAssets="all" Private="false" />
</ItemGroup>

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,6 +20,9 @@
// limitations under the License.
using System;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using ArchiSteamFarm.Web;
using ArchiSteamFarm.Web.Responses;
@@ -30,15 +33,15 @@ namespace ArchiSteamFarm.CustomPlugins.ExamplePlugin;
// You've always wanted from your ASF to post cats, right? Now is your chance!
// P.S. The code is almost 1:1 copy from the one I use in ArchiBot, you can thank me later
internal static class CatAPI {
private const string URL = "https://aws.random.cat";
private const string URL = "https://api.thecatapi.com";
internal static async Task<Uri?> GetRandomCatURL(WebBrowser webBrowser) {
internal static async Task<Uri?> GetRandomCatURL(WebBrowser webBrowser, CancellationToken cancellationToken = default) {
ArgumentNullException.ThrowIfNull(webBrowser);
Uri request = new($"{URL}/meow");
Uri request = new($"{URL}/v1/images/search");
ObjectResponse<MeowResponse>? response = await webBrowser.UrlGetToJsonObject<MeowResponse>(request).ConfigureAwait(false);
ObjectResponse<ImmutableList<MeowResponse>>? response = await webBrowser.UrlGetToJsonObject<ImmutableList<MeowResponse>>(request, cancellationToken: cancellationToken).ConfigureAwait(false);
return response?.Content?.URL;
return response?.Content?.FirstOrDefault()?.URL;
}
}

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,6 +21,7 @@
using System;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using ArchiSteamFarm.Core;
using ArchiSteamFarm.IPC.Controllers.Api;
@@ -38,14 +39,16 @@ public sealed class CatController : ArchiController {
/// Fetches URL of a random cat picture.
/// </summary>
[HttpGet]
[ProducesResponseType(typeof(GenericResponse<Uri>), (int) HttpStatusCode.OK)]
[ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.ServiceUnavailable)]
[ProducesResponseType<GenericResponse<Uri>>((int) HttpStatusCode.OK)]
[ProducesResponseType<GenericResponse>((int) HttpStatusCode.ServiceUnavailable)]
public async Task<ActionResult<GenericResponse>> CatGet() {
if (ASF.WebBrowser == null) {
throw new InvalidOperationException(nameof(ASF.WebBrowser));
}
Uri? url = await CatAPI.GetRandomCatURL(ASF.WebBrowser).ConfigureAwait(false);
CancellationToken cancellationToken = HttpContext.RequestAborted;
Uri? url = await CatAPI.GetRandomCatURL(ASF.WebBrowser, cancellationToken).ConfigureAwait(false);
return url != null ? Ok(new GenericResponse<Uri>(url)) : StatusCode((int) HttpStatusCode.ServiceUnavailable, new GenericResponse(false));
}

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,15 +21,17 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Composition;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using ArchiSteamFarm.Core;
using ArchiSteamFarm.Plugins.Interfaces;
using ArchiSteamFarm.Steam;
using ArchiSteamFarm.Steam.Data;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using SteamKit2;
namespace ArchiSteamFarm.CustomPlugins.ExamplePlugin;
@@ -40,34 +42,39 @@ namespace ArchiSteamFarm.CustomPlugins.ExamplePlugin;
// 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
[SuppressMessage("ReSharper", "MemberCanBeFileLocal")]
internal sealed class ExamplePlugin : IASF, IBot, IBotCommand2, IBotConnection, IBotFriendRequest, IBotMessage, IBotModules, IBotTradeOffer {
// 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]
public string Name => 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
[JsonInclude]
[Required]
public Version Version => typeof(ExamplePlugin).Assembly.GetName().Version ?? throw new InvalidOperationException(nameof(Version));
// Plugins can expose custom properties for our GET /Api/Plugins API call, simply annotate them with [JsonProperty] (or keep public)
[JsonProperty]
public bool CustomIsEnabledField { get; private set; } = true;
[JsonInclude]
[Required]
public bool CustomIsEnabledField { get; private init; } = true;
// 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
public Task OnASFInit(IReadOnlyDictionary<string, JToken>? additionalConfigProperties = null) {
public Task OnASFInit(IReadOnlyDictionary<string, JsonElement>? additionalConfigProperties = null) {
if (additionalConfigProperties == null) {
return Task.CompletedTask;
}
foreach ((string configProperty, JToken configValue) in additionalConfigProperties) {
foreach ((string configProperty, JsonElement configValue) in additionalConfigProperties) {
// 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
switch (configProperty) {
case $"{nameof(ExamplePlugin)}TestProperty" when configValue.Type == JTokenType.Boolean:
bool exampleBooleanValue = configValue.Value<bool>();
ASF.ArchiLogger.LogGenericInfo($"{nameof(ExamplePlugin)}TestProperty boolean property has been found with a value of: {exampleBooleanValue}");
case $"{nameof(ExamplePlugin)}TestProperty" when configValue.ValueKind == JsonValueKind.True:
ASF.ArchiLogger.LogGenericInfo($"{nameof(ExamplePlugin)}TestProperty boolean property has been found with a value of true");
break;
}
@@ -131,9 +138,9 @@ internal sealed class ExamplePlugin : IASF, IBot, IBotCommand2, IBotConnection,
// 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
public async Task OnBotInitModules(Bot bot, IReadOnlyDictionary<string, JToken>? additionalConfigProperties = null) {
public async Task OnBotInitModules(Bot bot, IReadOnlyDictionary<string, JsonElement>? additionalConfigProperties = null) {
// 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");

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,15 +21,17 @@
using System;
using System.Diagnostics.CodeAnalysis;
using Newtonsoft.Json;
using System.Text.Json.Serialization;
namespace ArchiSteamFarm.CustomPlugins.ExamplePlugin;
#pragma warning disable CA1812 // False positive, the class is used during json deserialization
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
internal sealed class MeowResponse {
[JsonProperty("file", Required = Required.Always)]
internal readonly Uri URL = null!;
[JsonInclude]
[JsonPropertyName("url")]
[JsonRequired]
internal Uri URL { get; private init; } = null!;
[JsonConstructor]
private MeowResponse() { }

View File

@@ -9,11 +9,6 @@
<PackageReference Include="System.Composition.AttributedModel" IncludeAssets="compile" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net481' OR '$(TargetFramework)' == 'netstandard2.1'">
<!-- Madness is already included in netf build of ASF, so we don't need to emit it ourselves -->
<PackageReference Update="JustArchiNET.Madness" IncludeAssets="compile" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ArchiSteamFarm\ArchiSteamFarm.csproj" ExcludeAssets="all" Private="false" />
</ItemGroup>

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,8 +20,10 @@
// limitations under the License.
using System;
using System.ComponentModel.DataAnnotations;
using System.Composition;
using System.Runtime;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using ArchiSteamFarm.Core;
@@ -38,8 +40,12 @@ internal sealed class PeriodicGCPlugin : IPlugin {
private static readonly object LockObject = new();
private static readonly Timer PeriodicGCTimer = new(PerformGC);
[JsonInclude]
[Required]
public string Name => nameof(PeriodicGCPlugin);
[JsonInclude]
[Required]
public Version Version => typeof(PeriodicGCPlugin).Assembly.GetName().Version ?? throw new InvalidOperationException(nameof(Version));
public Task OnLoaded() {

View File

@@ -7,21 +7,9 @@
<PackageReference Include="AngleSharp.XPath" IncludeAssets="compile" />
<PackageReference Include="ConfigureAwaitChecker.Analyzer" PrivateAssets="all" />
<PackageReference Include="JetBrains.Annotations" PrivateAssets="all" />
<PackageReference Include="Newtonsoft.Json" IncludeAssets="compile" />
<PackageReference Include="System.Composition.AttributedModel" IncludeAssets="compile" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net481' OR '$(TargetFramework)' == 'netstandard2.1'">
<!-- Madness is already included in netf build of ASF, so we don't need to emit it ourselves -->
<PackageReference Update="JustArchiNET.Madness" IncludeAssets="compile" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" IncludeAssets="compile" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net481'">
<Reference Include="System.Net.Http" HintPath="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8.1\System.Net.Http.dll" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ArchiSteamFarm\ArchiSteamFarm.csproj" ExcludeAssets="all" Private="false" />
</ItemGroup>

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,11 +20,12 @@
// limitations under the License.
using System;
using Newtonsoft.Json;
using System.Text.Json.Serialization;
namespace ArchiSteamFarm.CustomPlugins.SignInWithSteam.Data;
public sealed class SignInWithSteamRequest {
[JsonProperty(Required = Required.Always)]
public Uri RedirectURL { get; private set; } = null!;
[JsonInclude]
[JsonRequired]
public Uri RedirectURL { get; private init; } = null!;
}

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,13 +20,18 @@
// limitations under the License.
using System;
using Newtonsoft.Json;
using System.Text.Json.Serialization;
namespace ArchiSteamFarm.CustomPlugins.SignInWithSteam.Data;
public sealed class SignInWithSteamResponse {
[JsonProperty(Required = Required.Always)]
public Uri ReturnURL { get; private set; }
[JsonInclude]
[JsonRequired]
public Uri ReturnURL { get; private init; }
internal SignInWithSteamResponse(Uri returnURL) => ReturnURL = returnURL ?? throw new ArgumentNullException(nameof(returnURL));
internal SignInWithSteamResponse(Uri returnURL) {
ArgumentNullException.ThrowIfNull(returnURL);
ReturnURL = returnURL;
}
}

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -41,14 +41,11 @@ namespace ArchiSteamFarm.CustomPlugins.SignInWithSteam;
[Route("/Api/Bot/{botName:required}/SignInWithSteam")]
public sealed class SignInWithSteamController : ArchiController {
[HttpPost]
[ProducesResponseType(typeof(GenericResponse<SignInWithSteamResponse>), (int) HttpStatusCode.OK)]
[ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)]
[ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.ServiceUnavailable)]
[ProducesResponseType<GenericResponse<SignInWithSteamResponse>>((int) HttpStatusCode.OK)]
[ProducesResponseType<GenericResponse>((int) HttpStatusCode.BadRequest)]
[ProducesResponseType<GenericResponse>((int) HttpStatusCode.ServiceUnavailable)]
public async Task<ActionResult<GenericResponse>> Post(string botName, [FromBody] SignInWithSteamRequest request) {
if (string.IsNullOrEmpty(botName)) {
throw new ArgumentNullException(nameof(botName));
}
ArgumentException.ThrowIfNullOrEmpty(botName);
ArgumentNullException.ThrowIfNull(request);
Bot? bot = Bot.GetBot(botName);

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,7 +20,9 @@
// limitations under the License.
using System;
using System.ComponentModel.DataAnnotations;
using System.Composition;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using ArchiSteamFarm.Core;
using ArchiSteamFarm.Plugins.Interfaces;
@@ -31,8 +33,12 @@ namespace ArchiSteamFarm.CustomPlugins.SignInWithSteam;
[Export(typeof(IPlugin))]
[UsedImplicitly]
internal sealed class SignInWithSteamPlugin : IPlugin {
[JsonInclude]
[Required]
public string Name => nameof(SignInWithSteamPlugin);
[JsonInclude]
[Required]
public Version Version => typeof(SignInWithSteamPlugin).Assembly.GetName().Version ?? throw new InvalidOperationException(nameof(Version));
public Task OnLoaded() {

View File

@@ -6,22 +6,12 @@
<ItemGroup>
<PackageReference Include="ConfigureAwaitChecker.Analyzer" PrivateAssets="all" />
<PackageReference Include="JetBrains.Annotations" PrivateAssets="all" />
<PackageReference Include="Newtonsoft.Json" IncludeAssets="compile" />
<PackageReference Include="SteamKit2" IncludeAssets="compile" />
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" IncludeAssets="compile" />
<PackageReference Include="System.Composition.AttributedModel" IncludeAssets="compile" />
<PackageReference Include="System.Linq.Async" IncludeAssets="compile" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net481' OR '$(TargetFramework)' == 'netstandard2.1'">
<!-- Madness is already included in netf build of ASF, so we don't need to emit it ourselves -->
<PackageReference Update="JustArchiNET.Madness" IncludeAssets="compile" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net481'">
<Reference Include="System.Net.Http" HintPath="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8.1\System.Net.Http.dll" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ArchiSteamFarm\ArchiSteamFarm.csproj" ExcludeAssets="all" Private="false" />
</ItemGroup>

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,7 +22,10 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using ArchiSteamFarm.Core;
using ArchiSteamFarm.IPC.Responses;
@@ -38,45 +41,96 @@ using SteamKit2;
namespace ArchiSteamFarm.OfficialPlugins.ItemsMatcher;
internal static class Backend {
internal static async Task<BasicResponse?> AnnounceForListing(ulong steamID, WebBrowser webBrowser, IReadOnlyList<AssetForListing> inventory, IReadOnlyCollection<Asset.EType> acceptedMatchableTypes, uint totalInventoryCount, bool matchEverything, string tradeToken, string? nickname = null, string? avatarHash = null) {
internal static async Task<ObjectResponse<GenericResponse<BackgroundTaskResponse>>?> AnnounceDiffForListing(WebBrowser webBrowser, ulong steamID, IReadOnlyCollection<AssetForListing> inventory, string inventoryChecksum, IReadOnlyCollection<Asset.EType> acceptedMatchableTypes, uint totalInventoryCount, bool matchEverything, string tradeToken, IReadOnlyCollection<AssetForListing> inventoryRemoved, string? previousInventoryChecksum, string? nickname = null, string? avatarHash = null) {
ArgumentNullException.ThrowIfNull(webBrowser);
if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) {
throw new ArgumentOutOfRangeException(nameof(steamID));
}
ArgumentNullException.ThrowIfNull(webBrowser);
if ((inventory == null) || (inventory.Count == 0)) {
throw new ArgumentNullException(nameof(inventory));
}
ArgumentNullException.ThrowIfNull(inventory);
ArgumentException.ThrowIfNullOrEmpty(inventoryChecksum);
if ((acceptedMatchableTypes == null) || (acceptedMatchableTypes.Count == 0)) {
throw new ArgumentNullException(nameof(acceptedMatchableTypes));
}
if (totalInventoryCount == 0) {
throw new ArgumentOutOfRangeException(nameof(totalInventoryCount));
}
if (string.IsNullOrEmpty(tradeToken)) {
throw new ArgumentNullException(nameof(tradeToken));
}
ArgumentOutOfRangeException.ThrowIfZero(totalInventoryCount);
ArgumentException.ThrowIfNullOrEmpty(tradeToken);
if (tradeToken.Length != BotConfig.SteamTradeTokenLength) {
throw new ArgumentOutOfRangeException(nameof(tradeToken));
}
Uri request = new(ArchiNet.URL, "/Api/Listing/Announce/v3");
ArgumentNullException.ThrowIfNull(inventoryRemoved);
ArgumentException.ThrowIfNullOrEmpty(previousInventoryChecksum);
AnnouncementRequest data = new(ASF.GlobalDatabase?.Identifier ?? Guid.NewGuid(), steamID, tradeToken, inventory, acceptedMatchableTypes, totalInventoryCount, matchEverything, ASF.GlobalConfig?.MaxTradeHoldDuration ?? GlobalConfig.DefaultMaxTradeHoldDuration, nickname, avatarHash);
Uri request = new(ArchiNet.URL, "/Api/Listing/AnnounceDiff/v2");
return await webBrowser.UrlPost(request, data: data, requestOptions: WebBrowser.ERequestOptions.ReturnRedirections | WebBrowser.ERequestOptions.ReturnClientErrors | WebBrowser.ERequestOptions.CompressRequest).ConfigureAwait(false);
AnnouncementDiffRequest data = new(ASF.GlobalDatabase?.Identifier ?? Guid.NewGuid(), steamID, inventory, inventoryChecksum, acceptedMatchableTypes, totalInventoryCount, matchEverything, ASF.GlobalConfig?.MaxTradeHoldDuration ?? GlobalConfig.DefaultMaxTradeHoldDuration, tradeToken, inventoryRemoved, previousInventoryChecksum, nickname, avatarHash);
return await webBrowser.UrlPostToJsonObject<GenericResponse<BackgroundTaskResponse>, AnnouncementDiffRequest>(request, data: data, requestOptions: WebBrowser.ERequestOptions.ReturnRedirections | WebBrowser.ERequestOptions.ReturnClientErrors | WebBrowser.ERequestOptions.AllowInvalidBodyOnErrors | WebBrowser.ERequestOptions.CompressRequest).ConfigureAwait(false);
}
internal static async Task<ObjectResponse<GenericResponse<BackgroundTaskResponse>>?> AnnounceForListing(WebBrowser webBrowser, ulong steamID, IReadOnlyCollection<AssetForListing> inventory, string inventoryChecksum, IReadOnlyCollection<Asset.EType> acceptedMatchableTypes, uint totalInventoryCount, bool matchEverything, string tradeToken, string? nickname = null, string? avatarHash = null) {
ArgumentNullException.ThrowIfNull(webBrowser);
if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) {
throw new ArgumentOutOfRangeException(nameof(steamID));
}
if ((inventory == null) || (inventory.Count == 0)) {
throw new ArgumentNullException(nameof(inventory));
}
ArgumentException.ThrowIfNullOrEmpty(inventoryChecksum);
if ((acceptedMatchableTypes == null) || (acceptedMatchableTypes.Count == 0)) {
throw new ArgumentNullException(nameof(acceptedMatchableTypes));
}
ArgumentOutOfRangeException.ThrowIfZero(totalInventoryCount);
ArgumentException.ThrowIfNullOrEmpty(tradeToken);
if (tradeToken.Length != BotConfig.SteamTradeTokenLength) {
throw new ArgumentOutOfRangeException(nameof(tradeToken));
}
Uri request = new(ArchiNet.URL, "/Api/Listing/Announce/v5");
AnnouncementRequest data = new(ASF.GlobalDatabase?.Identifier ?? Guid.NewGuid(), steamID, inventory, inventoryChecksum, acceptedMatchableTypes, totalInventoryCount, matchEverything, ASF.GlobalConfig?.MaxTradeHoldDuration ?? GlobalConfig.DefaultMaxTradeHoldDuration, tradeToken, nickname, avatarHash);
return await webBrowser.UrlPostToJsonObject<GenericResponse<BackgroundTaskResponse>, AnnouncementRequest>(request, data: data, requestOptions: WebBrowser.ERequestOptions.ReturnRedirections | WebBrowser.ERequestOptions.ReturnClientErrors | WebBrowser.ERequestOptions.AllowInvalidBodyOnErrors | WebBrowser.ERequestOptions.CompressRequest).ConfigureAwait(false);
}
internal static string GenerateChecksumFor(IList<AssetForListing> assetsForListings) {
if ((assetsForListings == null) || (assetsForListings.Count == 0)) {
throw new ArgumentNullException(nameof(assetsForListings));
}
string text = string.Join('|', assetsForListings.Select(static asset => asset.BackendHashCode));
byte[] bytes = Encoding.UTF8.GetBytes(text);
return Utilities.GenerateChecksumFor(bytes);
}
internal static async Task<HttpStatusCode?> GetLicenseStatus(Guid licenseID, WebBrowser webBrowser) {
ArgumentOutOfRangeException.ThrowIfEqual(licenseID, Guid.Empty);
ArgumentNullException.ThrowIfNull(webBrowser);
Uri request = new(ArchiNet.URL, "/Api/Licenses/Status");
Dictionary<string, string> headers = new(1, StringComparer.Ordinal) {
{ "X-License-Key", licenseID.ToString("N") }
};
ObjectResponse<GenericResponse>? response = await webBrowser.UrlGetToJsonObject<GenericResponse>(request, headers, requestOptions: WebBrowser.ERequestOptions.ReturnClientErrors | WebBrowser.ERequestOptions.AllowInvalidBodyOnErrors).ConfigureAwait(false);
return response?.StatusCode;
}
internal static async Task<(HttpStatusCode StatusCode, ImmutableHashSet<ListedUser> Users)?> GetListedUsersForMatching(Guid licenseID, Bot bot, WebBrowser webBrowser, IReadOnlyCollection<Asset> inventory, IReadOnlyCollection<Asset.EType> acceptedMatchableTypes) {
if (licenseID == Guid.Empty) {
throw new ArgumentOutOfRangeException(nameof(licenseID));
}
ArgumentOutOfRangeException.ThrowIfEqual(licenseID, Guid.Empty);
ArgumentNullException.ThrowIfNull(bot);
ArgumentNullException.ThrowIfNull(webBrowser);
@@ -105,6 +159,28 @@ internal static class Backend {
return (response.StatusCode, response.Content?.Result ?? ImmutableHashSet<ListedUser>.Empty);
}
internal static async Task<ObjectResponse<GenericResponse<ImmutableHashSet<SetPart>>>?> GetSetParts(WebBrowser webBrowser, ulong steamID, IReadOnlyCollection<Asset.EType> matchableTypes, IReadOnlyCollection<uint> realAppIDs, CancellationToken cancellationToken = default) {
ArgumentNullException.ThrowIfNull(webBrowser);
if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) {
throw new ArgumentOutOfRangeException(nameof(steamID));
}
if ((matchableTypes == null) || (matchableTypes.Count == 0)) {
throw new ArgumentNullException(nameof(matchableTypes));
}
if ((realAppIDs == null) || (realAppIDs.Count == 0)) {
throw new ArgumentNullException(nameof(realAppIDs));
}
Uri request = new(ArchiNet.URL, "/Api/SetParts/Request");
SetPartsRequest data = new(ASF.GlobalDatabase?.Identifier ?? Guid.NewGuid(), steamID, matchableTypes, realAppIDs);
return await webBrowser.UrlPostToJsonObject<GenericResponse<ImmutableHashSet<SetPart>>, SetPartsRequest>(request, data: data, requestOptions: WebBrowser.ERequestOptions.ReturnRedirections | WebBrowser.ERequestOptions.ReturnClientErrors | WebBrowser.ERequestOptions.AllowInvalidBodyOnErrors | WebBrowser.ERequestOptions.CompressRequest, cancellationToken: cancellationToken).ConfigureAwait(false);
}
internal static async Task<BasicResponse?> HeartBeatForListing(Bot bot, WebBrowser webBrowser) {
ArgumentNullException.ThrowIfNull(bot);
ArgumentNullException.ThrowIfNull(webBrowser);
@@ -115,4 +191,22 @@ internal static class Backend {
return await webBrowser.UrlPost(request, data: data, requestOptions: WebBrowser.ERequestOptions.ReturnRedirections | WebBrowser.ERequestOptions.ReturnClientErrors | WebBrowser.ERequestOptions.CompressRequest).ConfigureAwait(false);
}
internal static async Task<ObjectResponse<GenericResponse<BackgroundTaskResponse>>?> PollResult(WebBrowser webBrowser, ulong steamID, Guid requestID) {
ArgumentNullException.ThrowIfNull(webBrowser);
if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) {
throw new ArgumentOutOfRangeException(nameof(steamID));
}
ArgumentOutOfRangeException.ThrowIfEqual(requestID, Guid.Empty);
if (SharedInfo.BuildInfo.IsCustomBuild) {
return null;
}
Uri request = new(ArchiNet.URL, $"/Api/Listing/PollResult/{steamID}/{requestID:N}");
return await webBrowser.UrlGetToJsonObject<GenericResponse<BackgroundTaskResponse>>(request, requestOptions: WebBrowser.ERequestOptions.ReturnRedirections | WebBrowser.ERequestOptions.ReturnClientErrors | WebBrowser.ERequestOptions.AllowInvalidBodyOnErrors).ConfigureAwait(false);
}
}

View File

@@ -0,0 +1,166 @@
// _ _ _ ____ _ _____
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// |
// http://www.apache.org/licenses/LICENSE-2.0
// |
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System;
using System.Globalization;
using System.IO;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using ArchiSteamFarm.Collections;
using ArchiSteamFarm.Core;
using ArchiSteamFarm.Helpers;
using ArchiSteamFarm.Helpers.Json;
using ArchiSteamFarm.Localization;
using ArchiSteamFarm.OfficialPlugins.ItemsMatcher.Data;
using JetBrains.Annotations;
namespace ArchiSteamFarm.OfficialPlugins.ItemsMatcher;
internal sealed class BotCache : SerializableFile {
[JsonDisallowNull]
[JsonInclude]
internal ConcurrentList<AssetForListing> LastAnnouncedAssetsForListing { get; private init; } = [];
internal string? LastAnnouncedTradeToken {
get => BackingLastAnnouncedTradeToken;
set {
if (BackingLastAnnouncedTradeToken == value) {
return;
}
BackingLastAnnouncedTradeToken = value;
Utilities.InBackground(Save);
}
}
internal string? LastInventoryChecksumBeforeDeduplication {
get => BackingLastInventoryChecksumBeforeDeduplication;
set {
if (BackingLastInventoryChecksumBeforeDeduplication == value) {
return;
}
BackingLastInventoryChecksumBeforeDeduplication = value;
Utilities.InBackground(Save);
}
}
internal DateTime? LastRequestAt {
get => BackingLastRequestAt;
set {
if (BackingLastRequestAt == value) {
return;
}
BackingLastRequestAt = value;
Utilities.InBackground(Save);
}
}
[JsonInclude]
private string? BackingLastAnnouncedTradeToken { get; set; }
[JsonInclude]
private string? BackingLastInventoryChecksumBeforeDeduplication { get; set; }
[JsonInclude]
private DateTime? BackingLastRequestAt { get; set; }
private BotCache(string filePath) : this() {
ArgumentException.ThrowIfNullOrEmpty(filePath);
FilePath = filePath;
}
[JsonConstructor]
private BotCache() => LastAnnouncedAssetsForListing.OnModified += OnObjectModified;
[UsedImplicitly]
public bool ShouldSerializeBackingLastAnnouncedTradeToken() => !string.IsNullOrEmpty(BackingLastAnnouncedTradeToken);
[UsedImplicitly]
public bool ShouldSerializeBackingLastInventoryChecksumBeforeDeduplication() => !string.IsNullOrEmpty(BackingLastInventoryChecksumBeforeDeduplication);
[UsedImplicitly]
public bool ShouldSerializeBackingLastRequestAt() => BackingLastRequestAt.HasValue;
[UsedImplicitly]
public bool ShouldSerializeLastAnnouncedAssetsForListing() => LastAnnouncedAssetsForListing.Count > 0;
protected override void Dispose(bool disposing) {
if (disposing) {
// Events we registered
LastAnnouncedAssetsForListing.OnModified -= OnObjectModified;
}
// Base dispose
base.Dispose(disposing);
}
protected override Task Save() => Save(this);
internal static async Task<BotCache> CreateOrLoad(string filePath) {
ArgumentException.ThrowIfNullOrEmpty(filePath);
if (!File.Exists(filePath)) {
return new BotCache(filePath);
}
BotCache? botCache;
try {
string json = await File.ReadAllTextAsync(filePath).ConfigureAwait(false);
if (string.IsNullOrEmpty(json)) {
ASF.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(json)));
return new BotCache(filePath);
}
botCache = json.ToJsonObject<BotCache>();
} catch (Exception e) {
ASF.ArchiLogger.LogGenericException(e);
return new BotCache(filePath);
}
if (botCache == null) {
ASF.ArchiLogger.LogNullError(botCache);
return new BotCache(filePath);
}
botCache.FilePath = filePath;
return botCache;
}
private async void OnObjectModified(object? sender, EventArgs e) {
if (string.IsNullOrEmpty(FilePath)) {
return;
}
await Save().ConfigureAwait(false);
}
}

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -80,12 +80,20 @@ internal static class Commands {
return access > EAccess.None ? bot.Commands.FormatBotResponse(Strings.ErrorAccessDenied) : null;
}
if (!bot.IsConnectedAndLoggedOn) {
return bot.Commands.FormatBotResponse(Strings.BotNotConnected);
}
if (bot.BotConfig.TradingPreferences.HasFlag(BotConfig.ETradingPreferences.MatchEverything)) {
return bot.Commands.FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, nameof(BotConfig.ETradingPreferences.MatchEverything)));
}
if ((ASF.GlobalConfig?.LicenseID == null) || (ASF.GlobalConfig.LicenseID == Guid.Empty)) {
return bot.Commands.FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, nameof(ASF.GlobalConfig.LicenseID)));
}
if (!bot.BotConfig.TradingPreferences.HasFlag(BotConfig.ETradingPreferences.MatchActively) || !ItemsMatcherPlugin.RemoteCommunications.TryGetValue(bot, out RemoteCommunication? remoteCommunication)) {
return bot.Commands.FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, nameof(BotConfig.ETradingPreferences.MatchActively)));
if (!ItemsMatcherPlugin.RemoteCommunications.TryGetValue(bot, out RemoteCommunication? remoteCommunication)) {
return bot.Commands.FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, nameof(remoteCommunication)));
}
remoteCommunication.TriggerMatchActivelyEarlier();
@@ -98,9 +106,7 @@ internal static class Commands {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
if (string.IsNullOrEmpty(botNames)) {
throw new ArgumentNullException(nameof(botNames));
}
ArgumentException.ThrowIfNullOrEmpty(botNames);
if ((steamID != 0) && !new SteamID(steamID).IsIndividualAccount) {
throw new ArgumentOutOfRangeException(nameof(steamID));
@@ -114,7 +120,7 @@ internal static class Commands {
IList<string?> results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => ResponseMatch(Steam.Interaction.Commands.GetProxyAccess(bot, access, steamID), bot)))).ConfigureAwait(false);
List<string> responses = new(results.Where(static result => !string.IsNullOrEmpty(result))!);
List<string> responses = [..results.Where(static result => !string.IsNullOrEmpty(result))];
return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null;
}

View File

@@ -0,0 +1,68 @@
// _ _ _ ____ _ _____
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// |
// http://www.apache.org/licenses/LICENSE-2.0
// |
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Text.Json.Serialization;
using ArchiSteamFarm.Steam.Data;
using ArchiSteamFarm.Steam.Storage;
using SteamKit2;
namespace ArchiSteamFarm.OfficialPlugins.ItemsMatcher.Data;
internal sealed class AnnouncementDiffRequest : AnnouncementRequest {
[JsonInclude]
[JsonRequired]
private ImmutableHashSet<AssetForListing> InventoryRemoved { get; init; }
[JsonInclude]
[JsonRequired]
private string PreviousInventoryChecksum { get; init; }
internal AnnouncementDiffRequest(Guid guid, ulong steamID, IReadOnlyCollection<AssetForListing> inventory, string inventoryChecksum, IReadOnlyCollection<Asset.EType> matchableTypes, uint totalInventoryCount, bool matchEverything, byte maxTradeHoldDuration, string tradeToken, IReadOnlyCollection<AssetForListing> inventoryRemoved, string previousInventoryChecksum, string? nickname = null, string? avatarHash = null) : base(guid, steamID, inventory, inventoryChecksum, matchableTypes, totalInventoryCount, matchEverything, maxTradeHoldDuration, tradeToken, nickname, avatarHash) {
ArgumentOutOfRangeException.ThrowIfEqual(guid, Guid.Empty);
if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) {
throw new ArgumentOutOfRangeException(nameof(steamID));
}
ArgumentNullException.ThrowIfNull(inventory);
ArgumentException.ThrowIfNullOrEmpty(inventoryChecksum);
if ((matchableTypes == null) || (matchableTypes.Count == 0)) {
throw new ArgumentNullException(nameof(matchableTypes));
}
ArgumentOutOfRangeException.ThrowIfZero(totalInventoryCount);
ArgumentException.ThrowIfNullOrEmpty(tradeToken);
if (tradeToken.Length != BotConfig.SteamTradeTokenLength) {
throw new ArgumentOutOfRangeException(nameof(tradeToken));
}
ArgumentNullException.ThrowIfNull(inventoryRemoved);
ArgumentException.ThrowIfNullOrEmpty(previousInventoryChecksum);
InventoryRemoved = inventoryRemoved.ToImmutableHashSet();
PreviousInventoryChecksum = previousInventoryChecksum;
}
}

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,78 +22,83 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Text.Json.Serialization;
using ArchiSteamFarm.Steam.Data;
using ArchiSteamFarm.Steam.Storage;
using JetBrains.Annotations;
using Newtonsoft.Json;
using SteamKit2;
namespace ArchiSteamFarm.OfficialPlugins.ItemsMatcher.Data;
internal sealed class AnnouncementRequest {
[JsonProperty]
private readonly string? AvatarHash;
internal class AnnouncementRequest {
[JsonInclude]
private string? AvatarHash { get; init; }
[JsonProperty(Required = Required.Always)]
private readonly Guid Guid;
[JsonInclude]
[JsonRequired]
private Guid Guid { get; init; }
[JsonProperty(Required = Required.Always)]
private readonly ImmutableList<AssetForListing> Inventory;
[JsonInclude]
[JsonRequired]
private ImmutableHashSet<AssetForListing> Inventory { get; init; }
[JsonProperty(Required = Required.Always)]
private readonly ImmutableHashSet<Asset.EType> MatchableTypes;
[JsonInclude]
[JsonRequired]
private string InventoryChecksum { get; init; }
[JsonProperty(Required = Required.Always)]
private readonly bool MatchEverything;
[JsonInclude]
[JsonRequired]
private ImmutableHashSet<Asset.EType> MatchableTypes { get; init; }
[JsonProperty(Required = Required.Always)]
private readonly byte MaxTradeHoldDuration;
[JsonInclude]
[JsonRequired]
private bool MatchEverything { get; init; }
[JsonProperty]
private readonly string? Nickname;
[JsonInclude]
[JsonRequired]
private byte MaxTradeHoldDuration { get; init; }
[JsonProperty(Required = Required.Always)]
private readonly ulong SteamID;
[JsonInclude]
private string? Nickname { get; init; }
[JsonProperty(Required = Required.Always)]
private readonly uint TotalInventoryCount;
[JsonInclude]
[JsonRequired]
private ulong SteamID { get; init; }
[JsonProperty(Required = Required.Always)]
private readonly string TradeToken;
[JsonInclude]
[JsonRequired]
private uint TotalInventoryCount { get; init; }
internal AnnouncementRequest(Guid guid, ulong steamID, string tradeToken, IReadOnlyList<AssetForListing> inventory, IReadOnlyCollection<Asset.EType> matchableTypes, uint totalInventoryCount, bool matchEverything, byte maxTradeHoldDuration, string? nickname = null, string? avatarHash = null) {
if (guid == Guid.Empty) {
throw new ArgumentOutOfRangeException(nameof(guid));
}
[JsonInclude]
[JsonRequired]
private string TradeToken { get; init; }
internal AnnouncementRequest(Guid guid, ulong steamID, IReadOnlyCollection<AssetForListing> inventory, string inventoryChecksum, IReadOnlyCollection<Asset.EType> matchableTypes, uint totalInventoryCount, bool matchEverything, byte maxTradeHoldDuration, string tradeToken, string? nickname = null, string? avatarHash = null) {
ArgumentOutOfRangeException.ThrowIfEqual(guid, Guid.Empty);
if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) {
throw new ArgumentOutOfRangeException(nameof(steamID));
}
if (string.IsNullOrEmpty(tradeToken)) {
throw new ArgumentNullException(nameof(tradeToken));
}
if (tradeToken.Length != BotConfig.SteamTradeTokenLength) {
throw new ArgumentOutOfRangeException(nameof(tradeToken));
}
if ((inventory == null) || (inventory.Count == 0)) {
throw new ArgumentNullException(nameof(inventory));
}
ArgumentNullException.ThrowIfNull(inventory);
ArgumentException.ThrowIfNullOrEmpty(inventoryChecksum);
if ((matchableTypes == null) || (matchableTypes.Count == 0)) {
throw new ArgumentNullException(nameof(matchableTypes));
}
if (totalInventoryCount == 0) {
throw new ArgumentOutOfRangeException(nameof(totalInventoryCount));
ArgumentOutOfRangeException.ThrowIfZero(totalInventoryCount);
ArgumentException.ThrowIfNullOrEmpty(tradeToken);
if (tradeToken.Length != BotConfig.SteamTradeTokenLength) {
throw new ArgumentOutOfRangeException(nameof(tradeToken));
}
Guid = guid;
SteamID = steamID;
TradeToken = tradeToken;
Inventory = inventory.ToImmutableList();
Inventory = inventory.ToImmutableHashSet();
InventoryChecksum = inventoryChecksum;
MatchableTypes = matchableTypes.ToImmutableHashSet();
MatchEverything = matchEverything;
MaxTradeHoldDuration = maxTradeHoldDuration;

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,18 +20,31 @@
// limitations under the License.
using System;
using System.Text.Json.Serialization;
using ArchiSteamFarm.Steam.Data;
using Newtonsoft.Json;
namespace ArchiSteamFarm.OfficialPlugins.ItemsMatcher.Data;
internal sealed class AssetForListing : AssetInInventory {
[JsonProperty("l", Required = Required.Always)]
internal readonly ulong PreviousAssetID;
internal string BackendHashCode => $"{Index}-{PreviousAssetID}-{AssetID}-{ClassID}-{Rarity}-{RealAppID}-{Tradable}-{Type}-{Amount}";
internal AssetForListing(Asset asset, ulong previousAssetID) : base(asset) {
[JsonInclude]
[JsonPropertyName("i")]
[JsonRequired]
internal uint Index { get; private init; }
[JsonInclude]
[JsonPropertyName("l")]
[JsonRequired]
internal ulong PreviousAssetID { get; private init; }
internal AssetForListing(Asset asset, uint index, ulong previousAssetID) : base(asset) {
ArgumentNullException.ThrowIfNull(asset);
Index = index;
PreviousAssetID = previousAssetID;
}
[JsonConstructor]
private AssetForListing() { }
}

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,29 +20,44 @@
// limitations under the License.
using System;
using System.Text.Json.Serialization;
using ArchiSteamFarm.Steam.Data;
using Newtonsoft.Json;
namespace ArchiSteamFarm.OfficialPlugins.ItemsMatcher.Data;
internal class AssetForMatching {
[JsonProperty("a", Required = Required.Always)]
internal readonly uint Amount;
[JsonInclude]
[JsonPropertyName("a")]
[JsonRequired]
internal uint Amount { get; set; }
[JsonProperty("c", Required = Required.Always)]
internal readonly ulong ClassID;
[JsonInclude]
[JsonPropertyName("c")]
[JsonRequired]
internal ulong ClassID { get; private init; }
[JsonProperty("r", Required = Required.Always)]
internal readonly Asset.ERarity Rarity;
[JsonInclude]
[JsonPropertyName("r")]
[JsonRequired]
internal Asset.ERarity Rarity { get; private init; }
[JsonProperty("e", Required = Required.Always)]
internal readonly uint RealAppID;
[JsonInclude]
[JsonPropertyName("e")]
[JsonRequired]
internal uint RealAppID { get; private init; }
[JsonProperty("t", Required = Required.Always)]
internal readonly bool Tradable;
[JsonInclude]
[JsonPropertyName("t")]
[JsonRequired]
internal bool Tradable { get; private init; }
[JsonProperty("p", Required = Required.Always)]
internal readonly Asset.EType Type;
[JsonInclude]
[JsonPropertyName("p")]
[JsonRequired]
internal Asset.EType Type { get; private init; }
[JsonConstructor]
protected AssetForMatching() { }
internal AssetForMatching(Asset asset) {
ArgumentNullException.ThrowIfNull(asset);
@@ -56,7 +71,4 @@ internal class AssetForMatching {
Type = asset.Type;
Rarity = asset.Rarity;
}
[JsonConstructor]
protected AssetForMatching() { }
}

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,14 +20,19 @@
// limitations under the License.
using System;
using System.Text.Json.Serialization;
using ArchiSteamFarm.Steam.Data;
using Newtonsoft.Json;
namespace ArchiSteamFarm.OfficialPlugins.ItemsMatcher.Data;
internal class AssetInInventory : AssetForMatching {
[JsonProperty("d", Required = Required.Always)]
internal readonly ulong AssetID;
[JsonInclude]
[JsonPropertyName("d")]
[JsonRequired]
internal ulong AssetID { get; private init; }
[JsonConstructor]
protected AssetInInventory() { }
internal AssetInInventory(Asset asset) : base(asset) {
ArgumentNullException.ThrowIfNull(asset);
@@ -35,8 +40,5 @@ internal class AssetInInventory : AssetForMatching {
AssetID = asset.AssetID;
}
[JsonConstructor]
private AssetInInventory() { }
internal Asset ToAsset() => new(Asset.SteamAppID, Asset.SteamCommunityContextID, ClassID, Amount, tradable: Tradable, assetID: AssetID, realAppID: RealAppID, type: Type, rarity: Rarity);
}

View File

@@ -0,0 +1,42 @@
// _ _ _ ____ _ _____
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// |
// http://www.apache.org/licenses/LICENSE-2.0
// |
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System;
using System.Diagnostics.CodeAnalysis;
using System.Text.Json.Serialization;
namespace ArchiSteamFarm.OfficialPlugins.ItemsMatcher.Data;
#pragma warning disable CA1812 // False positive, the class is used during json deserialization
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
internal sealed class BackgroundTaskResponse {
[JsonInclude]
[JsonRequired]
internal bool Finished { get; private init; }
[JsonInclude]
[JsonRequired]
internal Guid RequestID { get; private init; }
[JsonConstructor]
private BackgroundTaskResponse() { }
}
#pragma warning restore CA1812 // False positive, the class is used during json deserialization

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,22 +20,22 @@
// limitations under the License.
using System;
using Newtonsoft.Json;
using System.Text.Json.Serialization;
using SteamKit2;
namespace ArchiSteamFarm.OfficialPlugins.ItemsMatcher.Data;
internal sealed class HeartBeatRequest {
[JsonProperty(Required = Required.Always)]
internal readonly Guid Guid;
[JsonInclude]
[JsonRequired]
internal Guid Guid { get; private init; }
[JsonProperty(Required = Required.Always)]
internal readonly ulong SteamID;
[JsonInclude]
[JsonRequired]
internal ulong SteamID { get; private init; }
internal HeartBeatRequest(Guid guid, ulong steamID) {
if (guid == Guid.Empty) {
throw new ArgumentOutOfRangeException(nameof(guid));
}
ArgumentOutOfRangeException.ThrowIfEqual(guid, Guid.Empty);
if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) {
throw new ArgumentOutOfRangeException(nameof(steamID));

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -23,29 +23,31 @@ using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text.Json.Serialization;
using ArchiSteamFarm.Steam.Data;
using Newtonsoft.Json;
using SteamKit2;
namespace ArchiSteamFarm.OfficialPlugins.ItemsMatcher.Data;
internal sealed class InventoriesRequest {
[JsonProperty(Required = Required.Always)]
internal readonly Guid Guid;
[JsonInclude]
[JsonRequired]
internal Guid Guid { get; private init; }
[JsonProperty(Required = Required.Always)]
internal readonly ImmutableHashSet<AssetForMatching> Inventory;
[JsonInclude]
[JsonRequired]
internal ImmutableHashSet<AssetForMatching> Inventory { get; private init; }
[JsonProperty(Required = Required.Always)]
internal readonly ImmutableHashSet<Asset.EType> MatchableTypes;
[JsonInclude]
[JsonRequired]
internal ImmutableHashSet<Asset.EType> MatchableTypes { get; private init; }
[JsonProperty(Required = Required.Always)]
internal readonly ulong SteamID;
[JsonInclude]
[JsonRequired]
internal ulong SteamID { get; private init; }
internal InventoriesRequest(Guid guid, ulong steamID, IReadOnlyCollection<Asset> inventory, IReadOnlyCollection<Asset.EType> matchableTypes) {
if (guid == Guid.Empty) {
throw new ArgumentOutOfRangeException(nameof(guid));
}
ArgumentOutOfRangeException.ThrowIfEqual(guid, Guid.Empty);
if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) {
throw new ArgumentOutOfRangeException(nameof(steamID));

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,47 +21,48 @@
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Text.Json.Serialization;
using ArchiSteamFarm.Steam.Data;
using Newtonsoft.Json;
namespace ArchiSteamFarm.OfficialPlugins.ItemsMatcher.Data;
#pragma warning disable CA1812 // False positive, the class is used during json deserialization
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
internal sealed class ListedUser {
[JsonProperty(Required = Required.Always)]
internal readonly ImmutableHashSet<AssetInInventory> Assets = ImmutableHashSet<AssetInInventory>.Empty;
[JsonInclude]
[JsonRequired]
internal ImmutableHashSet<AssetInInventory> Assets { get; private init; } = ImmutableHashSet<AssetInInventory>.Empty;
[JsonProperty(Required = Required.Always)]
internal readonly ImmutableHashSet<Asset.EType> MatchableTypes = ImmutableHashSet<Asset.EType>.Empty;
[JsonInclude]
[JsonRequired]
internal ImmutableHashSet<Asset.EType> MatchableTypes { get; private init; } = ImmutableHashSet<Asset.EType>.Empty;
#pragma warning disable CS0649 // False positive, the field is used during json deserialization
[JsonProperty(Required = Required.Always)]
internal readonly bool MatchEverything;
#pragma warning restore CS0649 // False positive, the field is used during json deserialization
[JsonInclude]
[JsonRequired]
internal bool MatchEverything { get; private init; }
#pragma warning disable CS0649 // False positive, the field is used during json deserialization
[JsonProperty(Required = Required.Always)]
internal readonly byte MaxTradeHoldDuration;
#pragma warning restore CS0649 // False positive, the field is used during json deserialization
[JsonInclude]
[JsonRequired]
internal byte MaxTradeHoldDuration { get; private init; }
#pragma warning disable CS0649 // False positive, the field is used during json deserialization
[JsonProperty(Required = Required.AllowNull)]
internal readonly string? Nickname;
#pragma warning restore CS0649 // False positive, the field is used during json deserialization
[JsonInclude]
internal string? Nickname { get; private init; }
#pragma warning disable CS0649 // False positive, the field is used during json deserialization
[JsonProperty(Required = Required.Always)]
internal readonly ulong SteamID;
#pragma warning restore CS0649 // False positive, the field is used during json deserialization
[JsonInclude]
[JsonRequired]
internal ulong SteamID { get; private init; }
#pragma warning disable CS0649 // False positive, the field is used during json deserialization
[JsonProperty(Required = Required.Always)]
internal readonly uint TotalInventoryCount;
#pragma warning restore CS0649 // False positive, the field is used during json deserialization
[JsonInclude]
[JsonRequired]
internal uint TotalGamesCount { get; private init; }
[JsonProperty(Required = Required.Always)]
internal readonly string TradeToken = "";
[JsonInclude]
[JsonRequired]
internal uint TotalInventoryCount { get; private init; }
[JsonInclude]
[JsonRequired]
internal string TradeToken { get; private init; } = "";
[JsonConstructor]
private ListedUser() { }

View File

@@ -0,0 +1,54 @@
// _ _ _ ____ _ _____
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// |
// http://www.apache.org/licenses/LICENSE-2.0
// |
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System.Diagnostics.CodeAnalysis;
using System.Text.Json.Serialization;
using ArchiSteamFarm.Steam.Data;
namespace ArchiSteamFarm.OfficialPlugins.ItemsMatcher.Data;
#pragma warning disable CA1812 // False positive, the class is used during json deserialization
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
internal sealed class SetPart {
[JsonInclude]
[JsonPropertyName("c")]
[JsonRequired]
internal ulong ClassID { get; private init; }
[JsonInclude]
[JsonPropertyName("r")]
[JsonRequired]
internal Asset.ERarity Rarity { get; private init; }
[JsonInclude]
[JsonPropertyName("e")]
[JsonRequired]
internal uint RealAppID { get; private init; }
[JsonInclude]
[JsonPropertyName("p")]
[JsonRequired]
internal Asset.EType Type { get; private init; }
[JsonConstructor]
private SetPart() { }
}
#pragma warning restore CA1812 // False positive, the class is used during json deserialization

View File

@@ -0,0 +1,68 @@
// _ _ _ ____ _ _____
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// |
// http://www.apache.org/licenses/LICENSE-2.0
// |
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Text.Json.Serialization;
using ArchiSteamFarm.Steam.Data;
using SteamKit2;
namespace ArchiSteamFarm.OfficialPlugins.ItemsMatcher.Data;
internal sealed class SetPartsRequest {
[JsonInclude]
[JsonRequired]
internal Guid Guid { get; private init; }
[JsonInclude]
[JsonRequired]
internal ImmutableHashSet<Asset.EType> MatchableTypes { get; private init; }
[JsonInclude]
[JsonRequired]
internal ImmutableHashSet<uint> RealAppIDs { get; private init; }
[JsonInclude]
[JsonRequired]
internal ulong SteamID { get; private init; }
internal SetPartsRequest(Guid guid, ulong steamID, IReadOnlyCollection<Asset.EType> matchableTypes, IReadOnlyCollection<uint> realAppIDs) {
ArgumentOutOfRangeException.ThrowIfEqual(guid, Guid.Empty);
if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) {
throw new ArgumentOutOfRangeException(nameof(steamID));
}
if ((matchableTypes == null) || (matchableTypes.Count == 0)) {
throw new ArgumentNullException(nameof(matchableTypes));
}
if ((realAppIDs == null) || (realAppIDs.Count == 0)) {
throw new ArgumentNullException(nameof(realAppIDs));
}
Guid = guid;
SteamID = steamID;
MatchableTypes = matchableTypes.ToImmutableHashSet();
RealAppIDs = realAppIDs.ToImmutableHashSet();
}
}

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -23,8 +23,11 @@ using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Composition;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using ArchiSteamFarm.Core;
using ArchiSteamFarm.OfficialPlugins.ItemsMatcher.Localization;
@@ -33,8 +36,6 @@ using ArchiSteamFarm.Plugins.Interfaces;
using ArchiSteamFarm.Steam;
using ArchiSteamFarm.Steam.Exchange;
using ArchiSteamFarm.Steam.Integration.Callbacks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using SteamKit2;
namespace ArchiSteamFarm.OfficialPlugins.ItemsMatcher;
@@ -43,10 +44,12 @@ namespace ArchiSteamFarm.OfficialPlugins.ItemsMatcher;
internal sealed class ItemsMatcherPlugin : OfficialPlugin, IBot, IBotCommand2, IBotIdentity, IBotModules, IBotTradeOfferResults, IBotUserNotifications {
internal static readonly ConcurrentDictionary<Bot, RemoteCommunication> RemoteCommunications = new();
[JsonProperty]
[JsonInclude]
[Required]
public override string Name => nameof(ItemsMatcherPlugin);
[JsonProperty]
[JsonInclude]
[Required]
public override Version Version => typeof(ItemsMatcherPlugin).Assembly.GetName().Version ?? throw new InvalidOperationException(nameof(Version));
public async Task<string?> OnBotCommand(Bot bot, EAccess access, string message, string[] args, ulong steamID = 0) {
@@ -56,9 +59,7 @@ internal sealed class ItemsMatcherPlugin : OfficialPlugin, IBot, IBotCommand2, I
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
if (string.IsNullOrEmpty(message)) {
throw new ArgumentNullException(nameof(message));
}
ArgumentException.ThrowIfNullOrEmpty(message);
if ((args == null) || (args.Length == 0)) {
throw new ArgumentNullException(nameof(args));
@@ -85,7 +86,7 @@ internal sealed class ItemsMatcherPlugin : OfficialPlugin, IBot, IBotCommand2, I
return Task.CompletedTask;
}
public async Task OnBotInitModules(Bot bot, IReadOnlyDictionary<string, JToken>? additionalConfigProperties = null) {
public async Task OnBotInitModules(Bot bot, IReadOnlyDictionary<string, JsonElement>? additionalConfigProperties = null) {
ArgumentNullException.ThrowIfNull(bot);
if (RemoteCommunications.TryRemove(bot, out RemoteCommunication? remoteCommunication)) {

View File

@@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns="" id="root">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string"/>
<xsd:attribute name="type" type="xsd:string"/>
<xsd:attribute name="mimetype" type="xsd:string"/>
<xsd:attribute ref="xml:space"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string"/>
<xsd:attribute name="name" type="xsd:string"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
<xsd:attribute ref="xml:space"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089
</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089
</value>
</resheader>
<data name="ActivelyMatchingSomeConfirmationsFailed" xml:space="preserve">
<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>
</data>
</root>

View File

@@ -67,7 +67,7 @@
<comment>{0} will be replaced by number of sets traded</comment>
</data>
<data name="ListingAnnouncing" xml:space="preserve">
<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 +75,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>
</data>
<data name="TradeOfferFailed" xml:space="preserve">
<value>Fehler beim Senden eines Handelsangebots an Bot {0} ({1}), überspringe...</value>
<value>Fehler beim Senden eines Handelsangebots an Bot {0} ({1}); überspringe...</value>
<comment>{0} will be replaced by steam ID (number), {1} will be replaced by user's nickname'</comment>
</data>
<data name="ActivelyMatchingSomeConfirmationsFailed" xml:space="preserve">
<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>
</data>
</root>

View File

@@ -67,7 +67,7 @@
<comment>{0} will be replaced by number of sets traded</comment>
</data>
<data name="ListingAnnouncing" xml:space="preserve">
<value>Ilmoitetaan {0} ({1}), jonka inventaariossa on yhteensä {2} kohdetta listattuna...</value>
<value>Ilmoitetaan {0} ({1}), jonka inventoryssa on yhteensä {2} kohdetta listattuna...</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">

View File

@@ -66,4 +66,8 @@
<value>{0} sets ont été matché pendant ce round.</value>
<comment>{0} will be replaced by number of sets traded</comment>
</data>
<data name="ActivelyMatchingSomeConfirmationsFailed" xml:space="preserve">
<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>
</data>
</root>

View File

@@ -63,7 +63,7 @@
</value>
</resheader>
<data name="ActivelyMatchingItemsRound" xml:space="preserve">
<value>התאימו בסך הכל {0} ערכות בסיבוב זה.</value>
<value>הותאמו בסך הכל {0} ערכות בסיבוב זה.</value>
<comment>{0} will be replaced by number of sets traded</comment>
</data>
<data name="ListingAnnouncing" xml:space="preserve">
@@ -78,4 +78,8 @@
<value>נכשל שליחת הצעת סחר לבוט {0} ({1}), ממשיך הלאה...</value>
<comment>{0} will be replaced by steam ID (number), {1} will be replaced by user's nickname'</comment>
</data>
<data name="ActivelyMatchingSomeConfirmationsFailed" xml:space="preserve">
<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>
</data>
</root>

View File

@@ -66,4 +66,16 @@
<value>Abbinati un totale di {0} set questo round.</value>
<comment>{0} will be replaced by number of sets traded</comment>
</data>
<data name="MatchingFound" xml:space="preserve">
<value>Abbinato un totale di {0} elementi tramite bot {1} ({2}), inviando un'offerta commerciale...</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>
</data>
<data name="TradeOfferFailed" xml:space="preserve">
<value>Impossibile inviare un'offerta di scambio al bot {0} ({1}), proseguendo...</value>
<comment>{0} will be replaced by steam ID (number), {1} will be replaced by user's nickname'</comment>
</data>
<data name="ActivelyMatchingSomeConfirmationsFailed" xml:space="preserve">
<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>
</data>
</root>

View File

@@ -0,0 +1,85 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns="" id="root">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string"/>
<xsd:attribute name="type" type="xsd:string"/>
<xsd:attribute name="mimetype" type="xsd:string"/>
<xsd:attribute ref="xml:space"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string"/>
<xsd:attribute name="name" type="xsd:string"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
<xsd:attribute ref="xml:space"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089
</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089
</value>
</resheader>
<data name="ActivelyMatchingItemsRound" xml:space="preserve">
<value>Matchet totalt {0} sett denne runden.</value>
<comment>{0} will be replaced by number of sets traded</comment>
</data>
<data name="ListingAnnouncing" xml:space="preserve">
<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>
</data>
<data name="TradeOfferFailed" xml:space="preserve">
<value>Kunne ikke sende en byttehandel til bot {0}({1}), fortsetter...</value>
<comment>{0} will be replaced by steam ID (number), {1} will be replaced by user's nickname'</comment>
</data>
<data name="ActivelyMatchingSomeConfirmationsFailed" xml:space="preserve">
<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>
</data>
</root>

View File

@@ -66,4 +66,20 @@
<value>Celkovo porovnaných {0} sad karet.</value>
<comment>{0} will be replaced by number of sets traded</comment>
</data>
<data name="ListingAnnouncing" xml:space="preserve">
<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>
</data>
<data name="TradeOfferFailed" xml:space="preserve">
<value>Nepodarilo se odoslať obchodnú ponuku pre bota {0} ({1}), pokračujem...</value>
<comment>{0} will be replaced by steam ID (number), {1} will be replaced by user's nickname'</comment>
</data>
<data name="ActivelyMatchingSomeConfirmationsFailed" xml:space="preserve">
<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>
</data>
</root>

View File

@@ -71,7 +71,7 @@
<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>與 Bot {1}{2}匹配到共 {0} 個物品,正在發送交易提案…</value>
<value>與 Bot {1}{2}比對到共 {0} 個物品,正在發送交易提案…</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>
</data>
<data name="TradeOfferFailed" xml:space="preserve">

View File

@@ -6,16 +6,10 @@
<ItemGroup>
<PackageReference Include="ConfigureAwaitChecker.Analyzer" PrivateAssets="all" />
<PackageReference Include="JetBrains.Annotations" PrivateAssets="all" />
<PackageReference Include="Newtonsoft.Json" IncludeAssets="compile" />
<PackageReference Include="SteamKit2" IncludeAssets="compile" />
<PackageReference Include="System.Composition.AttributedModel" IncludeAssets="compile" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net481' OR '$(TargetFramework)' == 'netstandard2.1'">
<!-- Madness is already included in netf build of ASF, so we don't need to emit it ourselves -->
<PackageReference Update="JustArchiNET.Madness" IncludeAssets="compile" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ArchiSteamFarm\ArchiSteamFarm.csproj" ExcludeAssets="all" Private="false" />
</ItemGroup>

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -27,9 +27,9 @@ using System.IO;
using System.Linq;
using System.Threading.Tasks;
using ArchiSteamFarm.Core;
using ArchiSteamFarm.Helpers.Json;
using ArchiSteamFarm.Localization;
using ArchiSteamFarm.Steam;
using Newtonsoft.Json;
using SteamKit2;
using SteamKit2.Internal;
@@ -45,9 +45,7 @@ internal static class Commands {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
if (string.IsNullOrEmpty(message)) {
throw new ArgumentNullException(nameof(message));
}
ArgumentException.ThrowIfNullOrEmpty(message);
if ((args == null) || (args.Length == 0)) {
throw new ArgumentNullException(nameof(args));
@@ -95,10 +93,7 @@ internal static class Commands {
}
ArgumentNullException.ThrowIfNull(bot);
if (string.IsNullOrEmpty(activationCode)) {
throw new ArgumentNullException(nameof(activationCode));
}
ArgumentException.ThrowIfNullOrEmpty(activationCode);
if (access < EAccess.Master) {
return access > EAccess.None ? bot.Commands.FormatBotResponse(Strings.ErrorAccessDenied) : null;
@@ -133,7 +128,7 @@ internal static class Commands {
return bot.Commands.FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(json)));
}
Steam.Security.MobileAuthenticator? mobileAuthenticator = JsonConvert.DeserializeObject<Steam.Security.MobileAuthenticator>(json);
Steam.Security.MobileAuthenticator? mobileAuthenticator = json.ToJsonObject<Steam.Security.MobileAuthenticator>();
if (mobileAuthenticator == null) {
return bot.Commands.FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(json)));
@@ -162,8 +157,7 @@ internal static class Commands {
return bot.Commands.FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, nameof(mobileAuthenticator.GenerateTokenForTime)));
}
// ReSharper disable once RedundantSuppressNullableWarningExpression - required for .NET Framework
CTwoFactor_FinalizeAddAuthenticator_Response? response = await mobileAuthenticatorHandler.FinalizeAuthenticator(bot.SteamID, activationCode, code!, steamTime).ConfigureAwait(false);
CTwoFactor_FinalizeAddAuthenticator_Response? response = await mobileAuthenticatorHandler.FinalizeAuthenticator(bot.SteamID, activationCode, code, steamTime).ConfigureAwait(false);
if (response == null) {
return bot.Commands.FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, nameof(mobileAuthenticatorHandler.FinalizeAuthenticator)));
@@ -211,13 +205,8 @@ internal static class Commands {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
if (string.IsNullOrEmpty(botNames)) {
throw new ArgumentNullException(nameof(botNames));
}
if (string.IsNullOrEmpty(activationCode)) {
throw new ArgumentNullException(nameof(activationCode));
}
ArgumentException.ThrowIfNullOrEmpty(botNames);
ArgumentException.ThrowIfNullOrEmpty(activationCode);
if ((steamID != 0) && !new SteamID(steamID).IsIndividualAccount) {
throw new ArgumentOutOfRangeException(nameof(steamID));
@@ -231,7 +220,7 @@ internal static class Commands {
IList<string?> results = await Utilities.InParallel(bots.Select(bot => ResponseTwoFactorFinalize(Steam.Interaction.Commands.GetProxyAccess(bot, access, steamID), bot, activationCode))).ConfigureAwait(false);
List<string> responses = new(results.Where(static result => !string.IsNullOrEmpty(result))!);
List<string> responses = [..results.Where(static result => !string.IsNullOrEmpty(result))];
return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null;
}
@@ -272,7 +261,7 @@ internal static class Commands {
return bot.Commands.FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(json)));
}
Steam.Security.MobileAuthenticator? mobileAuthenticator = JsonConvert.DeserializeObject<Steam.Security.MobileAuthenticator>(json);
Steam.Security.MobileAuthenticator? mobileAuthenticator = json.ToJsonObject<Steam.Security.MobileAuthenticator>();
if (mobileAuthenticator == null) {
return bot.Commands.FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(json)));
@@ -310,9 +299,7 @@ internal static class Commands {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
if (string.IsNullOrEmpty(botNames)) {
throw new ArgumentNullException(nameof(botNames));
}
ArgumentException.ThrowIfNullOrEmpty(botNames);
if ((steamID != 0) && !new SteamID(steamID).IsIndividualAccount) {
throw new ArgumentOutOfRangeException(nameof(steamID));
@@ -326,7 +313,7 @@ internal static class Commands {
IList<string?> results = await Utilities.InParallel(bots.Select(bot => ResponseTwoFactorFinalized(Steam.Interaction.Commands.GetProxyAccess(bot, access, steamID), bot, activationCode))).ConfigureAwait(false);
List<string> responses = new(results.Where(static result => !string.IsNullOrEmpty(result))!);
List<string> responses = [..results.Where(static result => !string.IsNullOrEmpty(result))];
return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null;
}
@@ -370,10 +357,10 @@ internal static class Commands {
return bot.Commands.FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, result));
}
MaFileData maFileData = new(response, deviceID);
MaFileData maFileData = new(response, bot.SteamID, deviceID);
string maFilePendingPath = $"{bot.GetFilePath(Bot.EFileType.MobileAuthenticator)}.PENDING";
string json = JsonConvert.SerializeObject(maFileData, Formatting.Indented);
string json = maFileData.ToJsonText(true);
try {
await File.WriteAllTextAsync(maFilePendingPath, json).ConfigureAwait(false);
@@ -391,9 +378,7 @@ internal static class Commands {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
if (string.IsNullOrEmpty(botNames)) {
throw new ArgumentNullException(nameof(botNames));
}
ArgumentException.ThrowIfNullOrEmpty(botNames);
if ((steamID != 0) && !new SteamID(steamID).IsIndividualAccount) {
throw new ArgumentOutOfRangeException(nameof(steamID));
@@ -407,7 +392,7 @@ internal static class Commands {
IList<string?> results = await Utilities.InParallel(bots.Select(bot => ResponseTwoFactorInit(Steam.Interaction.Commands.GetProxyAccess(bot, access, steamID), bot))).ConfigureAwait(false);
List<string> responses = new(results.Where(static result => !string.IsNullOrEmpty(result))!);
List<string> responses = [..results.Where(static result => !string.IsNullOrEmpty(result))];
return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null;
}

View File

@@ -0,0 +1,65 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns="" id="root">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string"/>
<xsd:attribute name="type" type="xsd:string"/>
<xsd:attribute name="mimetype" type="xsd:string"/>
<xsd:attribute ref="xml:space"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string"/>
<xsd:attribute name="name" type="xsd:string"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
<xsd:attribute ref="xml:space"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089
</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089
</value>
</resheader>
</root>

View File

@@ -0,0 +1,65 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns="" id="root">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string"/>
<xsd:attribute name="type" type="xsd:string"/>
<xsd:attribute name="mimetype" type="xsd:string"/>
<xsd:attribute ref="xml:space"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string"/>
<xsd:attribute name="name" type="xsd:string"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
<xsd:attribute ref="xml:space"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089
</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089
</value>
</resheader>
</root>

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,52 +20,81 @@
// limitations under the License.
using System;
using Newtonsoft.Json;
using System.Text.Json.Serialization;
using SteamKit2;
using SteamKit2.Internal;
namespace ArchiSteamFarm.OfficialPlugins.MobileAuthenticator;
internal sealed class MaFileData {
[JsonProperty("account_name", Required = Required.Always)]
internal readonly string AccountName;
[JsonInclude]
[JsonPropertyName("account_name")]
[JsonRequired]
internal string AccountName { get; private init; }
[JsonProperty("device_id", Required = Required.Always)]
internal readonly string DeviceID;
[JsonInclude]
[JsonPropertyName("device_id")]
[JsonRequired]
internal string DeviceID { get; private init; }
[JsonProperty("identity_secret", Required = Required.Always)]
internal readonly string IdentitySecret;
[JsonInclude]
[JsonPropertyName("identity_secret")]
[JsonRequired]
internal string IdentitySecret { get; private init; }
[JsonProperty("revocation_code", Required = Required.Always)]
internal readonly string RevocationCode;
[JsonInclude]
[JsonPropertyName("revocation_code")]
[JsonRequired]
internal string RevocationCode { get; private init; }
[JsonProperty("secret_1", Required = Required.Always)]
internal readonly string Secret1;
[JsonInclude]
[JsonPropertyName("secret_1")]
[JsonRequired]
internal string Secret1 { get; private init; }
[JsonProperty("serial_number", Required = Required.Always)]
internal readonly ulong SerialNumber;
[JsonInclude]
[JsonPropertyName("serial_number")]
[JsonRequired]
internal ulong SerialNumber { get; private init; }
[JsonProperty("server_time", Required = Required.Always)]
internal readonly ulong ServerTime;
[JsonInclude]
[JsonPropertyName("server_time")]
[JsonRequired]
internal ulong ServerTime { get; private init; }
[JsonProperty("shared_secret", Required = Required.Always)]
internal readonly string SharedSecret;
[JsonInclude]
[JsonRequired]
internal MaFileSessionData Session { get; private init; }
[JsonProperty("status", Required = Required.Always)]
internal readonly int Status;
[JsonInclude]
[JsonPropertyName("shared_secret")]
[JsonRequired]
internal string SharedSecret { get; private init; }
[JsonProperty("token_gid", Required = Required.Always)]
internal readonly string TokenGid;
[JsonInclude]
[JsonPropertyName("status")]
[JsonRequired]
internal int Status { get; private init; }
[JsonProperty("uri", Required = Required.Always)]
internal readonly string Uri;
[JsonInclude]
[JsonPropertyName("token_gid")]
[JsonRequired]
internal string TokenGid { get; private init; }
internal MaFileData(CTwoFactor_AddAuthenticator_Response data, string deviceID) {
[JsonInclude]
[JsonPropertyName("uri")]
[JsonRequired]
internal string Uri { get; private init; }
internal MaFileData(CTwoFactor_AddAuthenticator_Response data, ulong steamID, string deviceID) {
ArgumentNullException.ThrowIfNull(data);
if (string.IsNullOrEmpty(deviceID)) {
throw new ArgumentNullException(nameof(deviceID));
if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) {
throw new ArgumentOutOfRangeException(nameof(steamID));
}
ArgumentException.ThrowIfNullOrEmpty(deviceID);
AccountName = data.account_name;
DeviceID = deviceID;
IdentitySecret = Convert.ToBase64String(data.identity_secret);
@@ -73,6 +102,7 @@ internal sealed class MaFileData {
Secret1 = Convert.ToBase64String(data.secret_1);
SerialNumber = data.serial_number;
ServerTime = data.server_time;
Session = new MaFileSessionData(steamID);
SharedSecret = Convert.ToBase64String(data.shared_secret);
Status = data.status;
TokenGid = data.token_gid;

View File

@@ -0,0 +1,40 @@
// _ _ _ ____ _ _____
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// |
// http://www.apache.org/licenses/LICENSE-2.0
// |
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System;
using System.Text.Json.Serialization;
using SteamKit2;
namespace ArchiSteamFarm.OfficialPlugins.MobileAuthenticator;
internal sealed class MaFileSessionData {
[JsonInclude]
[JsonRequired]
internal ulong SteamID { get; private init; }
internal MaFileSessionData(ulong steamID) {
if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) {
throw new ArgumentOutOfRangeException(nameof(steamID));
}
SteamID = steamID;
}
}

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -33,9 +33,10 @@ internal sealed class MobileAuthenticatorHandler : ClientMsgHandler {
private readonly SteamUnifiedMessages.UnifiedService<ITwoFactor> UnifiedTwoFactorService;
internal MobileAuthenticatorHandler(ArchiLogger archiLogger, SteamUnifiedMessages steamUnifiedMessages) {
ArgumentNullException.ThrowIfNull(archiLogger);
ArgumentNullException.ThrowIfNull(steamUnifiedMessages);
ArchiLogger = archiLogger ?? throw new ArgumentNullException(nameof(archiLogger));
ArchiLogger = archiLogger;
UnifiedTwoFactorService = steamUnifiedMessages.CreateService<ITwoFactor>();
}
@@ -46,9 +47,7 @@ internal sealed class MobileAuthenticatorHandler : ClientMsgHandler {
throw new ArgumentOutOfRangeException(nameof(steamID));
}
if (string.IsNullOrEmpty(deviceID)) {
throw new ArgumentNullException(nameof(deviceID));
}
ArgumentException.ThrowIfNullOrEmpty(deviceID);
if (Client == null) {
throw new InvalidOperationException(nameof(Client));
@@ -90,17 +89,9 @@ internal sealed class MobileAuthenticatorHandler : ClientMsgHandler {
throw new ArgumentOutOfRangeException(nameof(steamID));
}
if (string.IsNullOrEmpty(activationCode)) {
throw new ArgumentNullException(nameof(activationCode));
}
if (string.IsNullOrEmpty(authenticatorCode)) {
throw new ArgumentNullException(nameof(authenticatorCode));
}
if (authenticatorTime <= 0) {
throw new ArgumentOutOfRangeException(nameof(authenticatorTime));
}
ArgumentException.ThrowIfNullOrEmpty(activationCode);
ArgumentException.ThrowIfNullOrEmpty(authenticatorCode);
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(authenticatorTime);
if (Client == null) {
throw new InvalidOperationException(nameof(Client));

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,24 +22,29 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Composition;
using System.Diagnostics.CodeAnalysis;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using ArchiSteamFarm.Core;
using ArchiSteamFarm.OfficialPlugins.MobileAuthenticator.Localization;
using ArchiSteamFarm.Plugins;
using ArchiSteamFarm.Plugins.Interfaces;
using ArchiSteamFarm.Steam;
using Newtonsoft.Json;
using SteamKit2;
namespace ArchiSteamFarm.OfficialPlugins.MobileAuthenticator;
[Export(typeof(IPlugin))]
[SuppressMessage("ReSharper", "MemberCanBeFileLocal")]
internal sealed class MobileAuthenticatorPlugin : OfficialPlugin, IBotCommand2, IBotSteamClient {
[JsonProperty]
[JsonInclude]
[Required]
public override string Name => nameof(MobileAuthenticatorPlugin);
[JsonProperty]
[JsonInclude]
[Required]
public override Version Version => typeof(MobileAuthenticatorPlugin).Assembly.GetName().Version ?? throw new InvalidOperationException(nameof(Version));
public async Task<string?> OnBotCommand(Bot bot, EAccess access, string message, string[] args, ulong steamID = 0) {
@@ -49,9 +54,7 @@ internal sealed class MobileAuthenticatorPlugin : OfficialPlugin, IBotCommand2,
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
if (string.IsNullOrEmpty(message)) {
throw new ArgumentNullException(nameof(message));
}
ArgumentException.ThrowIfNullOrEmpty(message);
if ((args == null) || (args.Length == 0)) {
throw new ArgumentNullException(nameof(args));

View File

@@ -6,17 +6,11 @@
<ItemGroup>
<PackageReference Include="ConfigureAwaitChecker.Analyzer" PrivateAssets="all" />
<PackageReference Include="JetBrains.Annotations" PrivateAssets="all" />
<PackageReference Include="Newtonsoft.Json" IncludeAssets="compile" />
<PackageReference Include="SteamKit2" IncludeAssets="compile" />
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" IncludeAssets="compile" />
<PackageReference Include="System.Composition.AttributedModel" IncludeAssets="compile" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net481' OR '$(TargetFramework)' == 'netstandard2.1'">
<!-- Madness is already included in netf build of ASF, so we don't need to emit it ourselves -->
<PackageReference Update="JustArchiNET.Madness" IncludeAssets="compile" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ArchiSteamFarm\ArchiSteamFarm.csproj" ExcludeAssets="all" Private="false" />
</ItemGroup>

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -23,36 +23,52 @@ using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Globalization;
using System.Text.Json.Serialization;
using ArchiSteamFarm.Core;
using Newtonsoft.Json;
using SteamKit2;
namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper.Data;
internal sealed class SubmitRequest {
[JsonProperty("guid", Required = Required.Always)]
private static string Guid => ASF.GlobalDatabase?.Identifier.ToString("N") ?? throw new InvalidOperationException(nameof(ASF.GlobalDatabase.Identifier));
[JsonProperty("token", Required = Required.Always)]
private static string Token => SharedInfo.Token;
[JsonProperty("v", Required = Required.Always)]
private static byte Version => SharedInfo.ApiVersion;
[JsonProperty("apps", Required = Required.Always)]
private readonly ImmutableDictionary<string, string> Apps;
[JsonProperty("depots", Required = Required.Always)]
private readonly ImmutableDictionary<string, string> Depots;
private readonly ulong SteamID;
[JsonProperty("subs", Required = Required.Always)]
private readonly ImmutableDictionary<string, string> Subs;
#pragma warning disable CA1822 // We can't make it static, STJ doesn't serialize it otherwise
[JsonInclude]
[JsonPropertyName("guid")]
private string Guid => ASF.GlobalDatabase?.Identifier.ToString("N") ?? throw new InvalidOperationException(nameof(ASF.GlobalDatabase.Identifier));
#pragma warning restore CA1822 // We can't make it static, STJ doesn't serialize it otherwise
[JsonProperty("steamid", Required = Required.Always)]
[JsonInclude]
[JsonPropertyName("steamid")]
private string SteamIDText => new SteamID(SteamID).Render();
#pragma warning disable CA1822 // We can't make it static, STJ doesn't serialize it otherwise
[JsonInclude]
[JsonPropertyName("token")]
private string Token => SharedInfo.Token;
#pragma warning restore CA1822 // We can't make it static, STJ doesn't serialize it otherwise
#pragma warning disable CA1822 // We can't make it static, STJ doesn't serialize it otherwise
[JsonInclude]
[JsonPropertyName("v")]
private byte Version => SharedInfo.ApiVersion;
#pragma warning restore CA1822 // We can't make it static, STJ doesn't serialize it otherwise
[JsonInclude]
[JsonPropertyName("apps")]
[JsonRequired]
private ImmutableDictionary<string, string> Apps { get; init; }
[JsonInclude]
[JsonPropertyName("depots")]
[JsonRequired]
private ImmutableDictionary<string, string> Depots { get; init; }
[JsonInclude]
[JsonPropertyName("subs")]
[JsonRequired]
private ImmutableDictionary<string, string> Subs { get; init; }
internal SubmitRequest(ulong steamID, IReadOnlyCollection<KeyValuePair<uint, ulong>> apps, IReadOnlyCollection<KeyValuePair<uint, ulong>> accessTokens, IReadOnlyCollection<KeyValuePair<uint, string>> depots) {
if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) {
throw new ArgumentOutOfRangeException(nameof(steamID));

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,22 +20,21 @@
// limitations under the License.
using System.Diagnostics.CodeAnalysis;
using Newtonsoft.Json;
using System.Text.Json.Serialization;
namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper.Data;
#pragma warning disable CA1812 // False positive, the class is used during json deserialization
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
internal sealed class SubmitResponse {
#pragma warning disable CS0649 // False positive, the field is used during json deserialization
[JsonProperty("data", Required = Required.DisallowNull)]
internal readonly SubmitResponseData? Data;
#pragma warning restore CS0649 // False positive, the field is used during json deserialization
[JsonInclude]
[JsonPropertyName("data")]
internal SubmitResponseData? Data { get; private init; }
#pragma warning disable CS0649 // False positive, the field is used during json deserialization
[JsonProperty("success", Required = Required.Always)]
internal readonly bool Success;
#pragma warning restore CS0649 // False positive, the field is used during json deserialization
[JsonInclude]
[JsonPropertyName("success")]
[JsonRequired]
internal bool Success { get; private init; }
[JsonConstructor]
private SubmitResponse() { }

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,28 +20,40 @@
// limitations under the License.
using System.Collections.Immutable;
using Newtonsoft.Json;
using System.Text.Json.Serialization;
namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper.Data;
#pragma warning disable CA1812 // False positive, the class is used during json deserialization
internal sealed class SubmitResponseData {
[JsonProperty("new_apps", Required = Required.Always)]
internal readonly ImmutableHashSet<uint> NewApps = ImmutableHashSet<uint>.Empty;
[JsonInclude]
[JsonPropertyName("new_apps")]
[JsonRequired]
internal ImmutableHashSet<uint> NewApps { get; private init; } = ImmutableHashSet<uint>.Empty;
[JsonProperty("new_depots", Required = Required.Always)]
internal readonly ImmutableHashSet<uint> NewDepots = ImmutableHashSet<uint>.Empty;
[JsonInclude]
[JsonPropertyName("new_depots")]
[JsonRequired]
internal ImmutableHashSet<uint> NewDepots { get; private init; } = ImmutableHashSet<uint>.Empty;
[JsonProperty("new_subs", Required = Required.Always)]
internal readonly ImmutableHashSet<uint> NewPackages = ImmutableHashSet<uint>.Empty;
[JsonInclude]
[JsonPropertyName("new_subs")]
[JsonRequired]
internal ImmutableHashSet<uint> NewPackages { get; private init; } = ImmutableHashSet<uint>.Empty;
[JsonProperty("verified_apps", Required = Required.Always)]
internal readonly ImmutableHashSet<uint> VerifiedApps = ImmutableHashSet<uint>.Empty;
[JsonInclude]
[JsonPropertyName("verified_apps")]
[JsonRequired]
internal ImmutableHashSet<uint> VerifiedApps { get; private init; } = ImmutableHashSet<uint>.Empty;
[JsonProperty("verified_depots", Required = Required.Always)]
internal readonly ImmutableHashSet<uint> VerifiedDepots = ImmutableHashSet<uint>.Empty;
[JsonInclude]
[JsonPropertyName("verified_depots")]
[JsonRequired]
internal ImmutableHashSet<uint> VerifiedDepots { get; private init; } = ImmutableHashSet<uint>.Empty;
[JsonProperty("verified_subs", Required = Required.Always)]
internal readonly ImmutableHashSet<uint> VerifiedPackages = ImmutableHashSet<uint>.Empty;
[JsonInclude]
[JsonPropertyName("verified_subs")]
[JsonRequired]
internal ImmutableHashSet<uint> VerifiedPackages { get; private init; } = ImmutableHashSet<uint>.Empty;
}
#pragma warning restore CA1812 // False positive, the class is used during json deserialization

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,51 +21,57 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using ArchiSteamFarm.Core;
using ArchiSteamFarm.Helpers;
using ArchiSteamFarm.Helpers.Json;
using ArchiSteamFarm.OfficialPlugins.SteamTokenDumper.Localization;
using ArchiSteamFarm.Web.Responses;
using JetBrains.Annotations;
using Newtonsoft.Json;
using SteamKit2;
namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper;
internal sealed class GlobalCache : SerializableFile {
internal static readonly ArchiCacheable<ImmutableHashSet<uint>> KnownDepotIDs = new(ResolveKnownDepotIDs, TimeSpan.FromDays(7));
internal static readonly ArchiCacheable<FrozenSet<uint>> KnownDepotIDs = new(ResolveKnownDepotIDs, TimeSpan.FromDays(7));
private static string SharedFilePath => Path.Combine(ArchiSteamFarm.SharedInfo.ConfigDirectory, $"{nameof(SteamTokenDumper)}.cache");
[JsonProperty(Required = Required.DisallowNull)]
private readonly ConcurrentDictionary<uint, uint> AppChangeNumbers = new();
[JsonProperty(Required = Required.DisallowNull)]
private readonly ConcurrentDictionary<uint, ulong> AppTokens = new();
[JsonProperty(Required = Required.DisallowNull)]
private readonly ConcurrentDictionary<uint, string> DepotKeys = new();
[JsonProperty(Required = Required.DisallowNull)]
private readonly ConcurrentDictionary<uint, ulong> PackageTokens = new();
[JsonProperty(Required = Required.DisallowNull)]
private readonly ConcurrentDictionary<uint, ulong> SubmittedApps = new();
[JsonProperty(Required = Required.DisallowNull)]
private readonly ConcurrentDictionary<uint, string> SubmittedDepots = new();
[JsonProperty(Required = Required.DisallowNull)]
private readonly ConcurrentDictionary<uint, ulong> SubmittedPackages = new();
[JsonProperty(Required = Required.DisallowNull)]
[JsonInclude]
internal uint LastChangeNumber { get; private set; }
[JsonDisallowNull]
[JsonInclude]
private ConcurrentDictionary<uint, uint> AppChangeNumbers { get; init; } = new();
[JsonDisallowNull]
[JsonInclude]
private ConcurrentDictionary<uint, ulong> AppTokens { get; init; } = new();
[JsonDisallowNull]
[JsonInclude]
private ConcurrentDictionary<uint, string> DepotKeys { get; init; } = new();
[JsonDisallowNull]
[JsonInclude]
private ConcurrentDictionary<uint, ulong> SubmittedApps { get; init; } = new();
[JsonDisallowNull]
[JsonInclude]
private ConcurrentDictionary<uint, string> SubmittedDepots { get; init; } = new();
[JsonDisallowNull]
[JsonInclude]
private ConcurrentDictionary<uint, ulong> SubmittedPackages { get; init; } = new();
[JsonConstructor]
internal GlobalCache() => FilePath = SharedFilePath;
[UsedImplicitly]
@@ -80,9 +86,6 @@ internal sealed class GlobalCache : SerializableFile {
[UsedImplicitly]
public bool ShouldSerializeLastChangeNumber() => LastChangeNumber > 0;
[UsedImplicitly]
public bool ShouldSerializePackageTokens() => !PackageTokens.IsEmpty;
[UsedImplicitly]
public bool ShouldSerializeSubmittedApps() => !SubmittedApps.IsEmpty;
@@ -92,11 +95,20 @@ internal sealed class GlobalCache : SerializableFile {
[UsedImplicitly]
public bool ShouldSerializeSubmittedPackages() => !SubmittedPackages.IsEmpty;
protected override Task Save() => Save(this);
internal ulong GetAppToken(uint appID) => AppTokens[appID];
internal Dictionary<uint, ulong> GetAppTokensForSubmission() => AppTokens.Where(appToken => (SteamTokenDumperPlugin.Config?.SecretAppIDs.Contains(appToken.Key) == false) && (appToken.Value > 0) && (!SubmittedApps.TryGetValue(appToken.Key, out ulong token) || (appToken.Value != token))).ToDictionary(static appToken => appToken.Key, static appToken => appToken.Value);
internal Dictionary<uint, string> GetDepotKeysForSubmission() => DepotKeys.Where(depotKey => (SteamTokenDumperPlugin.Config?.SecretDepotIDs.Contains(depotKey.Key) == false) && !string.IsNullOrEmpty(depotKey.Value) && (!SubmittedDepots.TryGetValue(depotKey.Key, out string? key) || (depotKey.Value != key))).ToDictionary(static depotKey => depotKey.Key, static depotKey => depotKey.Value);
internal Dictionary<uint, ulong> GetPackageTokensForSubmission() => PackageTokens.Where(packageToken => (SteamTokenDumperPlugin.Config?.SecretPackageIDs.Contains(packageToken.Key) == false) && (packageToken.Value > 0) && (!SubmittedPackages.TryGetValue(packageToken.Key, out ulong token) || (packageToken.Value != token))).ToDictionary(static packageToken => packageToken.Key, static packageToken => packageToken.Value);
internal Dictionary<uint, ulong> GetAppTokensForSubmission() => AppTokens.Where(appToken => (SteamTokenDumperPlugin.Config?.SecretAppIDs.Contains(appToken.Key) != true) && (appToken.Value > 0) && (!SubmittedApps.TryGetValue(appToken.Key, out ulong token) || (appToken.Value != token))).ToDictionary(static appToken => appToken.Key, static appToken => appToken.Value);
internal Dictionary<uint, string> GetDepotKeysForSubmission() => DepotKeys.Where(depotKey => (SteamTokenDumperPlugin.Config?.SecretDepotIDs.Contains(depotKey.Key) != true) && !string.IsNullOrEmpty(depotKey.Value) && (!SubmittedDepots.TryGetValue(depotKey.Key, out string? key) || (depotKey.Value != key))).ToDictionary(static depotKey => depotKey.Key, static depotKey => depotKey.Value);
internal Dictionary<uint, ulong> GetPackageTokensForSubmission() {
if (ASF.GlobalDatabase == null) {
throw new InvalidOperationException(nameof(ASF.GlobalDatabase));
}
return ASF.GlobalDatabase.PackageAccessTokensReadOnly.Where(packageToken => (SteamTokenDumperPlugin.Config?.SecretPackageIDs.Contains(packageToken.Key) != true) && (packageToken.Value > 0) && (!SubmittedPackages.TryGetValue(packageToken.Key, out ulong token) || (packageToken.Value != token))).ToDictionary(static packageToken => packageToken.Key, static packageToken => packageToken.Value);
}
internal static async Task<GlobalCache?> Load() {
if (!File.Exists(SharedFilePath)) {
@@ -116,7 +128,7 @@ internal sealed class GlobalCache : SerializableFile {
return null;
}
globalCache = JsonConvert.DeserializeObject<GlobalCache>(json);
globalCache = json.ToJsonObject<GlobalCache>();
} catch (Exception e) {
ASF.ArchiLogger.LogGenericException(e);
@@ -141,10 +153,7 @@ internal sealed class GlobalCache : SerializableFile {
}
internal void OnPICSChanges(uint currentChangeNumber, IReadOnlyCollection<KeyValuePair<uint, SteamApps.PICSChangesCallback.PICSChangeData>> appChanges) {
if (currentChangeNumber == 0) {
throw new ArgumentOutOfRangeException(nameof(currentChangeNumber));
}
ArgumentOutOfRangeException.ThrowIfZero(currentChangeNumber);
ArgumentNullException.ThrowIfNull(appChanges);
if (currentChangeNumber <= LastChangeNumber) {
@@ -165,9 +174,7 @@ internal sealed class GlobalCache : SerializableFile {
}
internal void OnPICSChangesRestart(uint currentChangeNumber) {
if (currentChangeNumber == 0) {
throw new ArgumentOutOfRangeException(nameof(currentChangeNumber));
}
ArgumentOutOfRangeException.ThrowIfZero(currentChangeNumber);
if (currentChangeNumber <= LastChangeNumber) {
return;
@@ -184,7 +191,6 @@ internal sealed class GlobalCache : SerializableFile {
if (clear) {
AppTokens.Clear();
DepotKeys.Clear();
PackageTokens.Clear();
}
Utilities.InBackground(Save);
@@ -265,25 +271,6 @@ internal sealed class GlobalCache : SerializableFile {
Utilities.InBackground(Save);
}
internal void UpdatePackageTokens(IReadOnlyCollection<KeyValuePair<uint, ulong>> packageTokens) {
ArgumentNullException.ThrowIfNull(packageTokens);
bool save = false;
foreach ((uint packageID, ulong packageToken) in packageTokens) {
if (PackageTokens.TryGetValue(packageID, out ulong previousPackageToken) && (previousPackageToken == packageToken)) {
continue;
}
PackageTokens[packageID] = packageToken;
save = true;
}
if (save) {
Utilities.InBackground(Save);
}
}
internal void UpdateSubmittedData(IReadOnlyDictionary<uint, ulong> apps, IReadOnlyDictionary<uint, ulong> packages, IReadOnlyDictionary<uint, string> depots) {
ArgumentNullException.ThrowIfNull(apps);
ArgumentNullException.ThrowIfNull(packages);
@@ -324,21 +311,19 @@ internal sealed class GlobalCache : SerializableFile {
}
private static bool IsValidDepotKey(string depotKey) {
if (string.IsNullOrEmpty(depotKey)) {
throw new ArgumentNullException(nameof(depotKey));
}
ArgumentException.ThrowIfNullOrEmpty(depotKey);
return (depotKey.Length == 64) && Utilities.IsValidHexadecimalText(depotKey);
}
private static async Task<(bool Success, ImmutableHashSet<uint>? Result)> ResolveKnownDepotIDs() {
private static async Task<(bool Success, FrozenSet<uint>? Result)> ResolveKnownDepotIDs(CancellationToken cancellationToken = default) {
if (ASF.WebBrowser == null) {
throw new InvalidOperationException(nameof(ASF.WebBrowser));
}
Uri request = new($"{SharedInfo.ServerURL}/knowndepots.csv");
StreamResponse? response = await ASF.WebBrowser.UrlGetToStream(request).ConfigureAwait(false);
StreamResponse? response = await ASF.WebBrowser.UrlGetToStream(request, cancellationToken: cancellationToken).ConfigureAwait(false);
if (response?.Content == null) {
return (false, null);
@@ -348,19 +333,19 @@ internal sealed class GlobalCache : SerializableFile {
try {
using StreamReader reader = new(response.Content);
string? countText = await reader.ReadLineAsync().ConfigureAwait(false);
string? countText = await reader.ReadLineAsync(cancellationToken).ConfigureAwait(false);
if (string.IsNullOrEmpty(countText) || !int.TryParse(countText, out int count) || (count <= 0)) {
ASF.ArchiLogger.LogNullError(nameof(countText));
ASF.ArchiLogger.LogNullError(countText);
return (false, null);
}
HashSet<uint> result = new(count);
while (await reader.ReadLineAsync().ConfigureAwait(false) is { Length: > 0 } line) {
while (await reader.ReadLineAsync(cancellationToken).ConfigureAwait(false) is { Length: > 0 } line) {
if (!uint.TryParse(line, out uint depotID) || (depotID == 0)) {
ASF.ArchiLogger.LogNullError(nameof(depotID));
ASF.ArchiLogger.LogNullError(depotID);
continue;
}
@@ -368,7 +353,7 @@ internal sealed class GlobalCache : SerializableFile {
result.Add(depotID);
}
return (result.Count > 0, result.ToImmutableHashSet());
return (result.Count > 0, result.ToFrozenSet());
} catch (Exception e) {
ASF.ArchiLogger.LogGenericWarningException(e);

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,16 +19,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
using Newtonsoft.Json;
using System.Text.Json.Serialization;
namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper;
public sealed class GlobalConfigExtension {
[JsonProperty]
public SteamTokenDumperConfig? SteamTokenDumperPlugin { get; private set; }
[JsonInclude]
public SteamTokenDumperConfig? SteamTokenDumperPlugin { get; private init; }
[JsonProperty(Required = Required.DisallowNull)]
public bool SteamTokenDumperPluginEnabled { get; private set; }
[JsonInclude]
public bool SteamTokenDumperPluginEnabled { get; private init; }
[JsonConstructor]
internal GlobalConfigExtension() { }

View File

@@ -109,7 +109,10 @@
<value>Приключи събирането на {0} информация за играта или приложението.</value>
<comment>{0} will be replaced by the number (count this batch) of app infos retrieved</comment>
</data>
<data name="BotFinishedRetrievingDepotKeys" xml:space="preserve">
<value>Успешно извлечени {0} от {1} депо ключове.</value>
<comment>{0} will be replaced by the number (count this batch) of depot keys that were successfully retrieved, {1} will be replaced by the number (count this batch) of depot keys that were supposed to be retrieved</comment>
</data>
<data name="BotFinishedRetrievingTotalDepots" xml:space="preserve">
<value>Приключи събирането на всички ключове за депа за общо {0} игри или проложения.</value>
<comment>{0} will be replaced by the number (total count) of apps retrieved</comment>

View File

@@ -0,0 +1,115 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns="" id="root">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string"/>
<xsd:attribute name="type" type="xsd:string"/>
<xsd:attribute name="mimetype" type="xsd:string"/>
<xsd:attribute ref="xml:space"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string"/>
<xsd:attribute name="name" type="xsd:string"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
<xsd:attribute ref="xml:space"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089
</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089
</value>
</resheader>
<data name="PluginDisabledMissingBuildToken" xml:space="preserve">
<value>{0} je onemogućen zbog nedostajućih tokena za izgradnju</value>
<comment>{0} will be replaced by the name of the plugin (e.g. "SteamTokenDumperPlugin")</comment>
</data>
<data name="PluginDisabledInConfig" xml:space="preserve">
<value>{0} je trenutno onemogućen po vašoj konfiguraciji. Ako želite pomoći SteamDB u dostavljanju podataka, molimo pogledajte naš wiki.</value>
<comment>{0} will be replaced by the name of the plugin (e.g. "SteamTokenDumperPlugin")</comment>
</data>
<data name="BotRetrievingAppInfos" xml:space="preserve">
<value>Preuzimanje informacija o {0} aplikaciji...</value>
<comment>{0} will be replaced by the number (count this batch) of app infos being retrieved</comment>
</data>
<data name="SubmissionSuccessfulNewApps" xml:space="preserve">
<value>Nova aplikacija: {0}</value>
<comment>{0} will be replaced by list of the apps (IDs, numbers), separated by a comma</comment>
</data>
<data name="SubmissionSuccessfulVerifiedApps" xml:space="preserve">
<value>Verifikovana aplikacija: {0}</value>
<comment>{0} will be replaced by list of the apps (IDs, numbers), separated by a comma</comment>
</data>
<data name="SubmissionSuccessfulNewPackages" xml:space="preserve">
<value>Novi paketi: {0}</value>
<comment>{0} will be replaced by list of the packages (IDs, numbers), separated by a comma</comment>
</data>
<data name="SubmissionSuccessfulVerifiedPackages" xml:space="preserve">
<value>Verifikovani paketi: {0}</value>
<comment>{0} will be replaced by list of the packages (IDs, numbers), separated by a comma</comment>
</data>
</root>

View File

@@ -109,7 +109,10 @@
<value>Récupération de {0} infos d'application terminée.</value>
<comment>{0} will be replaced by the number (count this batch) of app infos retrieved</comment>
</data>
<data name="BotFinishedRetrievingDepotKeys" xml:space="preserve">
<value>Récupération réussie de {0} sur {1} clés de dépôts.</value>
<comment>{0} will be replaced by the number (count this batch) of depot keys that were successfully retrieved, {1} will be replaced by the number (count this batch) of depot keys that were supposed to be retrieved</comment>
</data>
<data name="BotFinishedRetrievingTotalDepots" xml:space="preserve">
<value>Fin de la récupération de toutes les clés de dépôts pour un total de {0} applications.</value>
<comment>{0} will be replaced by the number (total count) of apps retrieved</comment>

View File

@@ -109,7 +109,10 @@
<value>Hai completato il recupero di {0} informazioni app.</value>
<comment>{0} will be replaced by the number (count this batch) of app infos retrieved</comment>
</data>
<data name="BotFinishedRetrievingDepotKeys" xml:space="preserve">
<value>Recuperate {0} chiavi depot su {1} con successo.</value>
<comment>{0} will be replaced by the number (count this batch) of depot keys that were successfully retrieved, {1} will be replaced by the number (count this batch) of depot keys that were supposed to be retrieved</comment>
</data>
<data name="BotFinishedRetrievingTotalDepots" xml:space="preserve">
<value>Finito il recupero di tutte le chiavi del deposito per un totale di {0} applicazioni.</value>
<comment>{0} will be replaced by the number (total count) of apps retrieved</comment>

View File

@@ -97,7 +97,10 @@
<value>Pabeidza iegūt kopumā {0} aplikāciju piekļuves marķierus.</value>
<comment>{0} will be replaced by the number (total count) of app access tokens retrieved</comment>
</data>
<data name="BotRetrievingTotalDepots" xml:space="preserve">
<value>Notiek visu depot atslēgu izgūšana, kopā {0} lietotnēm...</value>
<comment>{0} will be replaced by the number (total count) of apps being retrieved</comment>
</data>
<data name="BotRetrievingAppInfos" xml:space="preserve">
<value>Iegūst {0} aplikāciju informāciju...</value>
<comment>{0} will be replaced by the number (count this batch) of app infos being retrieved</comment>
@@ -106,13 +109,33 @@
<value>Pabeidza iegūt {0} aplikāciju informāciju.</value>
<comment>{0} will be replaced by the number (count this batch) of app infos retrieved</comment>
</data>
<data name="BotFinishedRetrievingDepotKeys" xml:space="preserve">
<value>Veiksmīgi izgūtas {0} no {1} depot atslēgām.</value>
<comment>{0} will be replaced by the number (count this batch) of depot keys that were successfully retrieved, {1} will be replaced by the number (count this batch) of depot keys that were supposed to be retrieved</comment>
</data>
<data name="BotFinishedRetrievingTotalDepots" xml:space="preserve">
<value>Pabeigta visu depot atslēgu izgūšana, kopā {0} lietotnēm.</value>
<comment>{0} will be replaced by the number (total count) of apps retrieved</comment>
</data>
<data name="SubmissionNoNewData" xml:space="preserve">
<value>Nav jaunu datu, ko iesniegt, viss ir atjaunināts.</value>
</data>
<data name="SubmissionNoContributorSet" xml:space="preserve">
<value>Nevarēja iesniegt datus, jo nav derīgas SteamID kopas, ko mēs varētu klasificēt kā atbalstītāju. Apsveriet iespēju iestatīt {0} rekvizītu.</value>
<comment>{0} will be replaced by the name of the config property (e.g. "SteamOwnerID") that the user is expected to set</comment>
</data>
<data name="SubmissionInProgress" xml:space="preserve">
<value>Iesniedz reģistrētās lietotnes/pakotnes/depot, kopumā: {0}/{1}/{2}...</value>
<comment>{0} will be replaced by the number of app access tokens being submitted, {1} will be replaced by the number of package access tokens being submitted, {2} will be replaced by the number of depot keys being submitted</comment>
</data>
<data name="SubmissionFailedTooManyRequests" xml:space="preserve">
<value>Iesniegšana neizdevās, jo tika nosūtīti pārāk daudz pieprasījumi. Mēs mēģināsim vēlreiz pēc aptuveni {0} no šī brīža.</value>
<comment>{0} will be replaced by translated TimeSpan string (such as "53 minutes")</comment>
</data>
<data name="SubmissionSuccessful" xml:space="preserve">
<value>Dati ir veiksmīgi iesniegti. Serveris ir reģistrējis pavisam jaunas lietotnes/pakotnes/depots: {0} ({1} verificēts)/{2} ({3} verificēts)/{4} ({5} verificēts).</value>
<comment>{0} will be replaced by the number of new app access tokens that the server has registered, {1} will be replaced by the number of verified app access tokens that the server has registered, {2} will be replaced by the number of new package access tokens that the server has registered, {3} will be replaced by the number of verified package access tokens that the server has registered, {4} will be replaced by the number of new depot keys that the server has registered, {5} will be replaced by the number of verified depot keys that the server has registered</comment>
</data>
<data name="SubmissionSuccessfulNewApps" xml:space="preserve">
<value>Jaunas aplikācijas: {0}</value>
<comment>{0} will be replaced by list of the apps (IDs, numbers), separated by a comma</comment>
@@ -129,10 +152,25 @@
<value>Pārbaudītas pakas: {0}</value>
<comment>{0} will be replaced by list of the packages (IDs, numbers), separated by a comma</comment>
</data>
<data name="SubmissionSuccessfulNewDepots" xml:space="preserve">
<value>Jaunas aplikācijas: {0}</value>
<comment>{0} will be replaced by list of the depots (IDs, numbers), separated by a comma</comment>
</data>
<data name="SubmissionSuccessfulVerifiedDepots" xml:space="preserve">
<value>Pārbaudītas aplikācijas: {0}</value>
<comment>{0} will be replaced by list of the depots (IDs, numbers), separated by a comma</comment>
</data>
<data name="PluginSecretListInitialized" xml:space="preserve">
<value>{0} inicializēts, spraudnis neatrisinās nevienu no šiem: {1}.</value>
<comment>{0} will be replaced by the name of the config property (e.g. "SecretPackageIDs"), {1} will be replaced by list of the objects (IDs, numbers), separated by a comma</comment>
</data>
<data name="LoadingGlobalCache" xml:space="preserve">
<value>Notiek STD globālās kešatmiņas ielāde...</value>
</data>
<data name="ValidatingGlobalCacheIntegrity" xml:space="preserve">
<value>Notiek STD globālās kešatmiņas integritātes apstiprināšana...</value>
</data>
<data name="GlobalCacheIntegrityValidationFailed" xml:space="preserve">
<value>Neizdevās pārbaudīt STD globālās kešatmiņas integritāti. Tas liecina par iespējamu faila/atmiņas bojājumu, tā vietā tiks inicializēta jauna instance.</value>
</data>
</root>

View File

@@ -0,0 +1,94 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns="" id="root">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string"/>
<xsd:attribute name="type" type="xsd:string"/>
<xsd:attribute name="mimetype" type="xsd:string"/>
<xsd:attribute ref="xml:space"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string"/>
<xsd:attribute name="name" type="xsd:string"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
<xsd:attribute ref="xml:space"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089
</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089
</value>
</resheader>
</root>

View File

@@ -109,7 +109,10 @@
<value>Dokončené získavanie informácií {0} aplikácií.</value>
<comment>{0} will be replaced by the number (count this batch) of app infos retrieved</comment>
</data>
<data name="BotFinishedRetrievingDepotKeys" xml:space="preserve">
<value>Úspešne načítaných {0} z {1} depot kľúčov.</value>
<comment>{0} will be replaced by the number (count this batch) of depot keys that were successfully retrieved, {1} will be replaced by the number (count this batch) of depot keys that were supposed to be retrieved</comment>
</data>
<data name="BotFinishedRetrievingTotalDepots" xml:space="preserve">
<value>Dokončené získanie všetkých kľúčov položiek {0} aplikácií.</value>
<comment>{0} will be replaced by the number (total count) of apps retrieved</comment>
@@ -161,7 +164,13 @@
<value>{0} inicializovaných, modul nebude pracovať so žiadnym: {1}.</value>
<comment>{0} will be replaced by the name of the config property (e.g. "SecretPackageIDs"), {1} will be replaced by list of the objects (IDs, numbers), separated by a comma</comment>
</data>
<data name="LoadingGlobalCache" xml:space="preserve">
<value>Načítanie globálnej vyrovnávacej pamäti STD...</value>
</data>
<data name="ValidatingGlobalCacheIntegrity" xml:space="preserve">
<value>Overovanie globálnej vyrovnávacej pamäti STD...</value>
</data>
<data name="GlobalCacheIntegrityValidationFailed" xml:space="preserve">
<value>Overovanie globálnej vyrovnávacej pamäti STD sa nepodarilo. To naznačuje, že mohlo dôjsť k poškodeniu súboru/pamäti, miesto toho bude inicializovaná nová inštancia.</value>
</data>
</root>

View File

@@ -165,12 +165,12 @@
<comment>{0} will be replaced by the name of the config property (e.g. "SecretPackageIDs"), {1} will be replaced by list of the objects (IDs, numbers), separated by a comma</comment>
</data>
<data name="LoadingGlobalCache" xml:space="preserve">
<value>STD küresel önbelleği yükleniyor...</value>
<value>STD genel önbelleği yükleniyor...</value>
</data>
<data name="ValidatingGlobalCacheIntegrity" xml:space="preserve">
<value>STD küresel önbellek bütünlüğü doğrulanıyor...</value>
<value>STD genel önbellek bütünlüğü doğrulama...</value>
</data>
<data name="GlobalCacheIntegrityValidationFailed" xml:space="preserve">
<value>STD küresel önbellek bütünlüğü doğrulanamadı. Bu, olası bir dosya/bellek bozulması olduğunu gösterir, bunun yerine yeni bir örnek başlatılacaktır.</value>
<value>STD genel önbellek bütünlüğü doğrulanamadı. Bu, olası bir dosya / bellek bozulmasına işaret eder, bunun yerine yeni bir örnek başlatılır.</value>
</data>
</root>

View File

@@ -93,7 +93,10 @@
<value>Завершено отримання {0} токенів доступу.</value>
<comment>{0} will be replaced by the number (count this batch) of app access tokens retrieved</comment>
</data>
<data name="BotFinishedRetrievingTotalAppAccessTokens" xml:space="preserve">
<value>Закінчено отримання всього {0} токенів доступу.</value>
<comment>{0} will be replaced by the number (total count) of app access tokens retrieved</comment>
</data>

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,29 +20,33 @@
// limitations under the License.
using System.Collections.Immutable;
using System.Text.Json.Serialization;
using ArchiSteamFarm.Helpers.Json;
using ArchiSteamFarm.IPC.Integration;
using Newtonsoft.Json;
namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper;
public sealed class SteamTokenDumperConfig {
[JsonProperty(Required = Required.DisallowNull)]
[JsonInclude]
public bool Enabled { get; internal set; }
[JsonProperty(Required = Required.DisallowNull)]
[JsonDisallowNull]
[JsonInclude]
[SwaggerItemsMinMax(MinimumUint = 1, MaximumUint = uint.MaxValue)]
public ImmutableHashSet<uint> SecretAppIDs { get; private set; } = ImmutableHashSet<uint>.Empty;
public ImmutableHashSet<uint> SecretAppIDs { get; private init; } = ImmutableHashSet<uint>.Empty;
[JsonProperty(Required = Required.DisallowNull)]
[JsonDisallowNull]
[JsonInclude]
[SwaggerItemsMinMax(MinimumUint = 1, MaximumUint = uint.MaxValue)]
public ImmutableHashSet<uint> SecretDepotIDs { get; private set; } = ImmutableHashSet<uint>.Empty;
public ImmutableHashSet<uint> SecretDepotIDs { get; private init; } = ImmutableHashSet<uint>.Empty;
[JsonProperty(Required = Required.DisallowNull)]
[JsonDisallowNull]
[JsonInclude]
[SwaggerItemsMinMax(MinimumUint = 1, MaximumUint = uint.MaxValue)]
public ImmutableHashSet<uint> SecretPackageIDs { get; private set; } = ImmutableHashSet<uint>.Empty;
public ImmutableHashSet<uint> SecretPackageIDs { get; private init; } = ImmutableHashSet<uint>.Empty;
[JsonProperty(Required = Required.DisallowNull)]
public bool SkipAutoGrantPackages { get; private set; } = true;
[JsonInclude]
public bool SkipAutoGrantPackages { get; private init; } = true;
[JsonConstructor]
internal SteamTokenDumperConfig() { }

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -29,7 +29,7 @@ namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper;
[Route("Api/SteamTokenDumperPlugin")]
public sealed class SteamTokenDumperController : ArchiController {
[HttpGet(nameof(GlobalConfigExtension))]
[ProducesResponseType(typeof(GlobalConfigExtension), (int) HttpStatusCode.OK)]
[SwaggerOperation(Tags = new[] { nameof(GlobalConfigExtension) })]
[ProducesResponseType<GlobalConfigExtension>((int) HttpStatusCode.OK)]
[SwaggerOperation(Tags = [nameof(GlobalConfigExtension)])]
public ActionResult<GlobalConfigExtension> Get() => Ok(new GlobalConfigExtension());
}

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,27 +21,30 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Composition;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using ArchiSteamFarm.Core;
using ArchiSteamFarm.Helpers;
using ArchiSteamFarm.Helpers.Json;
using ArchiSteamFarm.OfficialPlugins.SteamTokenDumper.Data;
using ArchiSteamFarm.OfficialPlugins.SteamTokenDumper.Localization;
using ArchiSteamFarm.Plugins;
using ArchiSteamFarm.Plugins.Interfaces;
using ArchiSteamFarm.Steam;
using ArchiSteamFarm.Steam.Interaction;
using ArchiSteamFarm.Storage;
using ArchiSteamFarm.Web;
using ArchiSteamFarm.Web.Responses;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using SteamKit2;
namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper;
@@ -50,26 +53,27 @@ namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper;
internal sealed class SteamTokenDumperPlugin : OfficialPlugin, IASF, IBot, IBotCommand2, IBotSteamClient, ISteamPICSChanges {
private const ushort DepotsRateLimitingDelay = 500;
[JsonProperty]
internal static SteamTokenDumperConfig? Config { get; private set; }
private static readonly ConcurrentDictionary<Bot, IDisposable> BotSubscriptions = new();
private static readonly ConcurrentDictionary<Bot, (SemaphoreSlim RefreshSemaphore, Timer RefreshTimer)> BotSynchronizations = new();
private static readonly SemaphoreSlim SubmissionSemaphore = new(1, 1);
private static readonly Timer SubmissionTimer = new(SubmitData);
private static readonly Timer SubmissionTimer = new(OnSubmissionTimer);
private static GlobalCache? GlobalCache;
private static DateTimeOffset LastUploadAt = DateTimeOffset.MinValue;
[JsonProperty]
[JsonInclude]
[Required]
public override string Name => nameof(SteamTokenDumperPlugin);
[JsonProperty]
[JsonInclude]
[Required]
public override Version Version => typeof(SteamTokenDumperPlugin).Assembly.GetName().Version ?? throw new InvalidOperationException(nameof(Version));
public Task<uint> GetPreferredChangeNumberToStartFrom() => Task.FromResult(Config?.Enabled == true ? GlobalCache?.LastChangeNumber ?? 0 : 0);
public Task<uint> GetPreferredChangeNumberToStartFrom() => Task.FromResult(GlobalCache?.LastChangeNumber ?? 0);
public async Task OnASFInit(IReadOnlyDictionary<string, JToken>? additionalConfigProperties = null) {
public async Task OnASFInit(IReadOnlyDictionary<string, JsonElement>? additionalConfigProperties = null) {
if (!SharedInfo.HasValidToken) {
ASF.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.PluginDisabledMissingBuildToken, nameof(SteamTokenDumperPlugin)));
@@ -80,15 +84,19 @@ internal sealed class SteamTokenDumperPlugin : OfficialPlugin, IASF, IBot, IBotC
SteamTokenDumperConfig? config = null;
if (additionalConfigProperties != null) {
foreach ((string configProperty, JToken configValue) in additionalConfigProperties) {
foreach ((string configProperty, JsonElement configValue) in additionalConfigProperties) {
try {
switch (configProperty) {
case nameof(GlobalConfigExtension.SteamTokenDumperPlugin):
config = configValue.ToObject<SteamTokenDumperConfig>();
config = configValue.ToJsonObject<SteamTokenDumperConfig>();
break;
case nameof(GlobalConfigExtension.SteamTokenDumperPluginEnabled):
isEnabled = configValue.Value<bool>();
case nameof(GlobalConfigExtension.SteamTokenDumperPluginEnabled) when configValue.ValueKind == JsonValueKind.False:
isEnabled = false;
break;
case nameof(GlobalConfigExtension.SteamTokenDumperPluginEnabled) when configValue.ValueKind == JsonValueKind.True:
isEnabled = true;
break;
}
@@ -101,30 +109,6 @@ internal sealed class SteamTokenDumperPlugin : OfficialPlugin, IASF, IBot, IBotC
}
}
config ??= new SteamTokenDumperConfig();
if (isEnabled) {
config.Enabled = true;
}
if (!config.Enabled) {
ASF.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.PluginDisabledInConfig, nameof(SteamTokenDumperPlugin)));
return;
}
if (!config.SecretAppIDs.IsEmpty) {
ASF.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.PluginSecretListInitialized, nameof(config.SecretAppIDs), string.Join(", ", config.SecretAppIDs)));
}
if (!config.SecretPackageIDs.IsEmpty) {
ASF.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.PluginSecretListInitialized, nameof(config.SecretPackageIDs), string.Join(", ", config.SecretPackageIDs)));
}
if (!config.SecretDepotIDs.IsEmpty) {
ASF.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.PluginSecretListInitialized, nameof(config.SecretDepotIDs), string.Join(", ", config.SecretDepotIDs)));
}
if (GlobalCache == null) {
GlobalCache? globalCache = await GlobalCache.Load().ConfigureAwait(false);
@@ -137,8 +121,40 @@ internal sealed class SteamTokenDumperPlugin : OfficialPlugin, IASF, IBot, IBotC
}
}
if (!isEnabled && (config == null)) {
ASF.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.PluginDisabledInConfig, nameof(SteamTokenDumperPlugin)));
return;
}
config ??= new SteamTokenDumperConfig();
if (isEnabled) {
config.Enabled = true;
}
if (!config.Enabled) {
ASF.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.PluginDisabledInConfig, nameof(SteamTokenDumperPlugin)));
}
if (!config.SecretAppIDs.IsEmpty) {
ASF.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.PluginSecretListInitialized, nameof(config.SecretAppIDs), string.Join(", ", config.SecretAppIDs)));
}
if (!config.SecretPackageIDs.IsEmpty) {
ASF.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.PluginSecretListInitialized, nameof(config.SecretPackageIDs), string.Join(", ", config.SecretPackageIDs)));
}
if (!config.SecretDepotIDs.IsEmpty) {
ASF.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.PluginSecretListInitialized, nameof(config.SecretDepotIDs), string.Join(", ", config.SecretDepotIDs)));
}
Config = config;
if (!config.Enabled) {
return;
}
#pragma warning disable CA5394 // This call isn't used in a security-sensitive manner
TimeSpan startIn = TimeSpan.FromMinutes(Random.Shared.Next(SharedInfo.MinimumMinutesBeforeFirstUpload, SharedInfo.MaximumMinutesBeforeFirstUpload));
#pragma warning restore CA5394 // This call isn't used in a security-sensitive manner
@@ -162,29 +178,24 @@ internal sealed class SteamTokenDumperPlugin : OfficialPlugin, IASF, IBot, IBotC
throw new ArgumentNullException(nameof(args));
}
switch (args[0].ToUpperInvariant()) {
case "STD" when access >= EAccess.Owner:
if (Config is not { Enabled: true }) {
return Task.FromResult((string?) string.Format(CultureInfo.CurrentCulture, ArchiSteamFarm.Localization.Strings.WarningFailedWithError, nameof(Config)));
switch (args.Length) {
case 1:
switch (args[0].ToUpperInvariant()) {
case "STD":
return Task.FromResult(ResponseRefreshManually(access, bot));
}
TimeSpan minimumTimeBetweenUpload = TimeSpan.FromMinutes(SharedInfo.MinimumMinutesBetweenUploads);
if (LastUploadAt + minimumTimeBetweenUpload > DateTimeOffset.UtcNow) {
return Task.FromResult((string?) string.Format(CultureInfo.CurrentCulture, Strings.SubmissionFailedTooManyRequests, minimumTimeBetweenUpload.ToHumanReadable()));
}
// ReSharper disable once SuspiciousLockOverSynchronizationPrimitive - this is not a mistake, we need extra synchronization, and we can re-use the semaphore object for that
lock (SubmissionSemaphore) {
SubmissionTimer.Change(TimeSpan.Zero, TimeSpan.FromHours(SharedInfo.HoursBetweenUploads));
}
return Task.FromResult((string?) ArchiSteamFarm.Localization.Strings.Done);
case "STD" when access > EAccess.None:
return Task.FromResult((string?) ArchiSteamFarm.Localization.Strings.ErrorAccessDenied);
break;
default:
return Task.FromResult((string?) null);
switch (args[0].ToUpperInvariant()) {
case "STD":
return Task.FromResult(ResponseRefreshManually(access, Utilities.GetArgsAsText(args, 1, ","), steamID));
}
break;
}
return Task.FromResult((string?) null);
}
public async Task OnBotDestroy(Bot bot) {
@@ -195,6 +206,9 @@ internal sealed class SteamTokenDumperPlugin : OfficialPlugin, IASF, IBot, IBotC
}
if (BotSynchronizations.TryRemove(bot, out (SemaphoreSlim RefreshSemaphore, Timer RefreshTimer) synchronization)) {
// Ensure the semaphore is empty, otherwise we're risking disposed exceptions
await synchronization.RefreshSemaphore.WaitAsync().ConfigureAwait(false);
synchronization.RefreshSemaphore.Dispose();
await synchronization.RefreshTimer.DisposeAsync().ConfigureAwait(false);
@@ -204,7 +218,8 @@ internal sealed class SteamTokenDumperPlugin : OfficialPlugin, IASF, IBot, IBotC
public async Task OnBotInit(Bot bot) {
ArgumentNullException.ThrowIfNull(bot);
if (Config is not { Enabled: true }) {
if (GlobalCache == null) {
// We can't operate like this anyway, skip initialization of synchronization structures
return;
}
@@ -248,40 +263,19 @@ internal sealed class SteamTokenDumperPlugin : OfficialPlugin, IASF, IBot, IBotC
}
public Task OnPICSChanges(uint currentChangeNumber, IReadOnlyDictionary<uint, SteamApps.PICSChangesCallback.PICSChangeData> appChanges, IReadOnlyDictionary<uint, SteamApps.PICSChangesCallback.PICSChangeData> packageChanges) {
if (currentChangeNumber == 0) {
throw new ArgumentOutOfRangeException(nameof(currentChangeNumber));
}
ArgumentOutOfRangeException.ThrowIfZero(currentChangeNumber);
ArgumentNullException.ThrowIfNull(appChanges);
ArgumentNullException.ThrowIfNull(packageChanges);
if (Config is not { Enabled: true }) {
return Task.CompletedTask;
}
if (GlobalCache == null) {
throw new InvalidOperationException(nameof(GlobalCache));
}
GlobalCache.OnPICSChanges(currentChangeNumber, appChanges);
GlobalCache?.OnPICSChanges(currentChangeNumber, appChanges);
return Task.CompletedTask;
}
public Task OnPICSChangesRestart(uint currentChangeNumber) {
if (currentChangeNumber == 0) {
throw new ArgumentOutOfRangeException(nameof(currentChangeNumber));
}
ArgumentOutOfRangeException.ThrowIfZero(currentChangeNumber);
if (Config is not { Enabled: true }) {
return Task.CompletedTask;
}
if (GlobalCache == null) {
throw new InvalidOperationException(nameof(GlobalCache));
}
GlobalCache.OnPICSChangesRestart(currentChangeNumber);
GlobalCache?.OnPICSChangesRestart(currentChangeNumber);
return Task.CompletedTask;
}
@@ -302,30 +296,34 @@ internal sealed class SteamTokenDumperPlugin : OfficialPlugin, IASF, IBot, IBotC
return;
}
if (GlobalCache == null) {
throw new InvalidOperationException(nameof(GlobalCache));
}
Dictionary<uint, ulong> packageTokens = callback.LicenseList.Where(static license => !Config.SecretPackageIDs.Contains(license.PackageID) && ((license.PaymentMethod != EPaymentMethod.AutoGrant) || !Config.SkipAutoGrantPackages)).GroupBy(static license => license.PackageID).ToDictionary(static group => group.Key, static group => group.OrderByDescending(static license => license.TimeCreated).First().AccessToken);
GlobalCache.UpdatePackageTokens(packageTokens);
await Refresh(bot, packageTokens.Keys).ConfigureAwait(false);
}
private static async Task Refresh(Bot bot, IReadOnlyCollection<uint>? packageIDs = null) {
ArgumentNullException.ThrowIfNull(bot);
if (Config is not { Enabled: true }) {
// Schedule a refresh in a while from now
if (!BotSynchronizations.TryGetValue(bot, out (SemaphoreSlim RefreshSemaphore, Timer RefreshTimer) synchronization)) {
return;
}
if (!await synchronization.RefreshSemaphore.WaitAsync(0).ConfigureAwait(false)) {
// Another refresh is in progress, skip the refresh for now
return;
}
try {
synchronization.RefreshTimer.Change(TimeSpan.FromMinutes(1), TimeSpan.FromHours(SharedInfo.MaximumHoursBetweenRefresh));
} finally {
synchronization.RefreshSemaphore.Release();
}
}
private static async void OnSubmissionTimer(object? state = null) => await SubmitData().ConfigureAwait(false);
private static async Task Refresh(Bot bot) {
ArgumentNullException.ThrowIfNull(bot);
if (GlobalCache == null) {
throw new InvalidOperationException(nameof(GlobalCache));
}
if (ASF.GlobalDatabase == null) {
throw new InvalidOperationException(nameof(GlobalCache));
throw new InvalidOperationException(nameof(ASF.GlobalDatabase));
}
if (!BotSynchronizations.TryGetValue(bot, out (SemaphoreSlim RefreshSemaphore, Timer RefreshTimer) synchronization)) {
@@ -343,17 +341,17 @@ internal sealed class SteamTokenDumperPlugin : OfficialPlugin, IASF, IBot, IBotC
return;
}
packageIDs ??= bot.OwnedPackageIDs.Where(static package => !Config.SecretPackageIDs.Contains(package.Key) && ((package.Value.PaymentMethod != EPaymentMethod.AutoGrant) || !Config.SkipAutoGrantPackages)).Select(static package => package.Key).ToHashSet();
HashSet<uint> packageIDs = bot.OwnedPackageIDs.Where(static package => (Config?.SecretPackageIDs.Contains(package.Key) != true) && ((package.Value.PaymentMethod != EPaymentMethod.AutoGrant) || (Config?.SkipAutoGrantPackages == false))).Select(static package => package.Key).ToHashSet();
HashSet<uint> appIDsToRefresh = new();
HashSet<uint> appIDsToRefresh = [];
foreach (uint packageID in packageIDs.Where(static packageID => !Config.SecretPackageIDs.Contains(packageID))) {
foreach (uint packageID in packageIDs.Where(static packageID => Config?.SecretPackageIDs.Contains(packageID) != true)) {
if (!ASF.GlobalDatabase.PackagesDataReadOnly.TryGetValue(packageID, out PackageData? packageData) || (packageData.AppIDs == null)) {
// ASF might not have the package info for us at the moment, we'll retry later
continue;
}
appIDsToRefresh.UnionWith(packageData.AppIDs.Where(static appID => !Config.SecretAppIDs.Contains(appID) && GlobalCache.ShouldRefreshAppInfo(appID)));
appIDsToRefresh.UnionWith(packageData.AppIDs.Where(static appID => (Config?.SecretAppIDs.Contains(appID) != true) && GlobalCache.ShouldRefreshAppInfo(appID)));
}
if (appIDsToRefresh.Count == 0) {
@@ -405,7 +403,7 @@ internal sealed class SteamTokenDumperPlugin : OfficialPlugin, IASF, IBot, IBotC
bot.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.BotFinishedRetrievingTotalAppAccessTokens, appIDsToRefresh.Count));
bot.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.BotRetrievingTotalDepots, appIDsToRefresh.Count));
(_, ImmutableHashSet<uint>? knownDepotIDs) = await GlobalCache.KnownDepotIDs.GetValue(ECacheFallback.SuccessPreviously).ConfigureAwait(false);
(_, FrozenSet<uint>? knownDepotIDs) = await GlobalCache.KnownDepotIDs.GetValue(ECacheFallback.SuccessPreviously).ConfigureAwait(false);
using (HashSet<uint>.Enumerator enumerator = appIDsToRefresh.GetEnumerator()) {
while (true) {
@@ -458,7 +456,7 @@ internal sealed class SteamTokenDumperPlugin : OfficialPlugin, IASF, IBot, IBotC
bool shouldFetchMainKey = false;
foreach (KeyValue depot in app.KeyValues["depots"].Children) {
if (!uint.TryParse(depot.Name, out uint depotID) || (knownDepotIDs?.Contains(depotID) == true) || Config.SecretDepotIDs.Contains(depotID) || !GlobalCache.ShouldRefreshDepotKey(depotID)) {
if (!uint.TryParse(depot.Name, out uint depotID) || (knownDepotIDs?.Contains(depotID) == true) || (Config?.SecretDepotIDs.Contains(depotID) == true) || !GlobalCache.ShouldRefreshDepotKey(depotID)) {
continue;
}
@@ -521,7 +519,9 @@ internal sealed class SteamTokenDumperPlugin : OfficialPlugin, IASF, IBot, IBotC
}
}
bot.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.BotFinishedRetrievingDepotKeys, depotKeysSuccessful, depotKeysTotal));
if (depotKeysTotal > 0) {
bot.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.BotFinishedRetrievingDepotKeys, depotKeysSuccessful, depotKeysTotal));
}
if (depotKeysSuccessful < depotKeysTotal) {
// We're not going to record app change numbers, as we didn't fetch all the depot keys we wanted
@@ -534,9 +534,11 @@ internal sealed class SteamTokenDumperPlugin : OfficialPlugin, IASF, IBot, IBotC
bot.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.BotFinishedRetrievingTotalDepots, appIDsToRefresh.Count));
} finally {
TimeSpan timeSpan = TimeSpan.FromHours(SharedInfo.MaximumHoursBetweenRefresh);
if (Config?.Enabled == true) {
TimeSpan timeSpan = TimeSpan.FromHours(SharedInfo.MaximumHoursBetweenRefresh);
synchronization.RefreshTimer.Change(timeSpan, timeSpan);
synchronization.RefreshTimer.Change(timeSpan, timeSpan);
}
await depotsRateLimitingSemaphore.WaitAsync().ConfigureAwait(false);
@@ -546,13 +548,72 @@ internal sealed class SteamTokenDumperPlugin : OfficialPlugin, IASF, IBot, IBotC
}
}
private static async void SubmitData(object? state = null) {
if (Bot.Bots == null) {
throw new InvalidOperationException(nameof(Bot.Bots));
private static string? ResponseRefreshManually(EAccess access, Bot bot) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
if (Config is not { Enabled: true }) {
return;
ArgumentNullException.ThrowIfNull(bot);
if (access < EAccess.Master) {
return access > EAccess.None ? bot.Commands.FormatBotResponse(ArchiSteamFarm.Localization.Strings.ErrorAccessDenied) : null;
}
if (GlobalCache == null) {
return bot.Commands.FormatBotResponse(string.Format(CultureInfo.CurrentCulture, ArchiSteamFarm.Localization.Strings.WarningFailedWithError, nameof(GlobalCache)));
}
Utilities.InBackground(
async () => {
await Refresh(bot).ConfigureAwait(false);
await SubmitData().ConfigureAwait(false);
}
);
return bot.Commands.FormatBotResponse(ArchiSteamFarm.Localization.Strings.Done);
}
private static string? ResponseRefreshManually(EAccess access, string botNames, ulong steamID = 0) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
ArgumentException.ThrowIfNullOrEmpty(botNames);
if ((steamID != 0) && !new SteamID(steamID).IsIndividualAccount) {
throw new ArgumentOutOfRangeException(nameof(steamID));
}
HashSet<Bot>? bots = Bot.GetBots(botNames);
if ((bots == null) || (bots.Count == 0)) {
return access >= EAccess.Owner ? Commands.FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, ArchiSteamFarm.Localization.Strings.BotNotFound, botNames)) : null;
}
if (bots.RemoveWhere(bot => Commands.GetProxyAccess(bot, access, steamID) < EAccess.Master) > 0) {
if (bots.Count == 0) {
return access >= EAccess.Owner ? Commands.FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, ArchiSteamFarm.Localization.Strings.BotNotFound, botNames)) : null;
}
}
if (GlobalCache == null) {
return Commands.FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, ArchiSteamFarm.Localization.Strings.WarningFailedWithError, nameof(GlobalCache)));
}
Utilities.InBackground(
async () => {
await Utilities.InParallel(bots.Select(static bot => Refresh(bot))).ConfigureAwait(false);
await SubmitData().ConfigureAwait(false);
}
);
return Commands.FormatStaticResponse(ArchiSteamFarm.Localization.Strings.Done);
}
private static async Task SubmitData(CancellationToken cancellationToken = default) {
if (Bot.Bots == null) {
throw new InvalidOperationException(nameof(Bot.Bots));
}
if (GlobalCache == null) {
@@ -567,7 +628,7 @@ internal sealed class SteamTokenDumperPlugin : OfficialPlugin, IASF, IBot, IBotC
return;
}
if (!await SubmissionSemaphore.WaitAsync(0).ConfigureAwait(false)) {
if (!await SubmissionSemaphore.WaitAsync(0, cancellationToken).ConfigureAwait(false)) {
return;
}
@@ -595,7 +656,7 @@ internal sealed class SteamTokenDumperPlugin : OfficialPlugin, IASF, IBot, IBotC
ASF.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.SubmissionInProgress, appTokens.Count, packageTokens.Count, depotKeys.Count));
ObjectResponse<SubmitResponse>? response = await ASF.WebBrowser.UrlPostToJsonObject<SubmitResponse, SubmitRequest>(request, data: data, requestOptions: WebBrowser.ERequestOptions.ReturnClientErrors | WebBrowser.ERequestOptions.AllowInvalidBodyOnErrors).ConfigureAwait(false);
ObjectResponse<SubmitResponse>? response = await ASF.WebBrowser.UrlPostToJsonObject<SubmitResponse, SubmitRequest>(request, data: data, requestOptions: WebBrowser.ERequestOptions.ReturnClientErrors | WebBrowser.ERequestOptions.AllowInvalidBodyOnErrors, cancellationToken: cancellationToken).ConfigureAwait(false);
if (response == null) {
ASF.ArchiLogger.LogGenericWarning(ArchiSteamFarm.Localization.Strings.WarningFailed);
@@ -610,7 +671,7 @@ internal sealed class SteamTokenDumperPlugin : OfficialPlugin, IASF, IBot, IBotC
ASF.ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, ArchiSteamFarm.Localization.Strings.WarningFailedWithError, response.StatusCode));
switch (response.StatusCode) {
case HttpStatusCode.Forbidden:
case HttpStatusCode.Forbidden when Config?.Enabled == true:
// SteamDB told us to stop submitting data for now
// ReSharper disable once SuspiciousLockOverSynchronizationPrimitive - this is not a mistake, we need extra synchronization, and we can re-use the semaphore object for that
lock (SubmissionSemaphore) {
@@ -623,12 +684,7 @@ internal sealed class SteamTokenDumperPlugin : OfficialPlugin, IASF, IBot, IBotC
GlobalCache.Reset(true);
break;
#if NETFRAMEWORK || NETSTANDARD
case (HttpStatusCode) 429:
#else
case HttpStatusCode.TooManyRequests:
#endif
case HttpStatusCode.TooManyRequests when Config?.Enabled == true:
// SteamDB told us to try again later
#pragma warning disable CA5394 // This call isn't used in a security-sensitive manner
TimeSpan startIn = TimeSpan.FromMinutes(Random.Shared.Next(SharedInfo.MinimumMinutesBeforeFirstUpload, SharedInfo.MaximumMinutesBeforeFirstUpload));
@@ -654,7 +710,7 @@ internal sealed class SteamTokenDumperPlugin : OfficialPlugin, IASF, IBot, IBotC
}
if (response.Content.Data == null) {
ASF.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, ArchiSteamFarm.Localization.Strings.ErrorIsInvalid), nameof(response.Content.Data));
ASF.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, ArchiSteamFarm.Localization.Strings.ErrorIsInvalid, nameof(response.Content.Data)));
return;
}

View File

@@ -6,9 +6,7 @@
<ItemGroup>
<PackageReference Include="ConfigureAwaitChecker.Analyzer" PrivateAssets="all" />
<PackageReference Include="JetBrains.Annotations" PrivateAssets="all" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="MSTest.TestAdapter" />
<PackageReference Include="MSTest.TestFramework" />
<PackageReference Include="MSTest" />
</ItemGroup>
<ItemGroup>

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -39,7 +39,7 @@ public sealed class Bot {
{ 43, MinCardsPerBadge + 1 }
};
HashSet<Asset> items = new();
HashSet<Asset> items = [];
foreach ((uint appID, byte cards) in itemsPerSet) {
for (byte i = 1; i <= cards; i++) {
@@ -55,30 +55,27 @@ public sealed class Bot {
}
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void MaxItemsTooSmall() {
const uint appID = 42;
HashSet<Asset> items = new() {
HashSet<Asset> items = [
CreateCard(1, appID),
CreateCard(2, appID)
};
];
GetItemsForFullBadge(items, 2, appID, MinCardsPerBadge - 1);
Assert.Fail();
Assert.ThrowsException<ArgumentOutOfRangeException>(() => GetItemsForFullBadge(items, 2, appID, MinCardsPerBadge - 1));
}
[TestMethod]
public void MoreCardsThanNeeded() {
const uint appID = 42;
HashSet<Asset> items = new() {
HashSet<Asset> items = [
CreateCard(1, appID),
CreateCard(1, appID),
CreateCard(2, appID),
CreateCard(3, appID)
};
];
HashSet<Asset> itemsToSend = GetItemsForFullBadge(items, 3, appID);
@@ -95,12 +92,12 @@ public sealed class Bot {
public void MultipleSets() {
const uint appID = 42;
HashSet<Asset> items = new() {
HashSet<Asset> items = [
CreateCard(1, appID),
CreateCard(1, appID),
CreateCard(2, appID),
CreateCard(2, appID)
};
];
HashSet<Asset> itemsToSend = GetItemsForFullBadge(items, 2, appID);
@@ -116,11 +113,11 @@ public sealed class Bot {
public void MultipleSetsDifferentAmount() {
const uint appID = 42;
HashSet<Asset> items = new() {
HashSet<Asset> items = [
CreateCard(1, appID, 2),
CreateCard(2, appID),
CreateCard(2, appID)
};
];
HashSet<Asset> itemsToSend = GetItemsForFullBadge(items, 2, appID);
@@ -136,7 +133,7 @@ public sealed class Bot {
public void MutliRarityAndType() {
const uint appID = 42;
HashSet<Asset> items = new() {
HashSet<Asset> items = [
CreateCard(1, appID, type: Asset.EType.TradingCard, rarity: Asset.ERarity.Common),
CreateCard(2, appID, type: Asset.EType.TradingCard, rarity: Asset.ERarity.Common),
@@ -158,7 +155,7 @@ public sealed class Bot {
CreateCard(2, appID, type: Asset.EType.Unknown, rarity: Asset.ERarity.Rare),
CreateCard(3, appID, type: Asset.EType.Unknown, rarity: Asset.ERarity.Rare),
CreateCard(4, appID, type: Asset.EType.Unknown, rarity: Asset.ERarity.Rare)
};
];
HashSet<Asset> itemsToSend = GetItemsForFullBadge(items, 3, appID);
@@ -177,10 +174,10 @@ public sealed class Bot {
public void NotAllCardsPresent() {
const uint appID = 42;
HashSet<Asset> items = new() {
HashSet<Asset> items = [
CreateCard(1, appID),
CreateCard(2, appID)
};
];
HashSet<Asset> itemsToSend = GetItemsForFullBadge(items, 3, appID);
@@ -192,10 +189,10 @@ public sealed class Bot {
public void OneSet() {
const uint appID = 42;
HashSet<Asset> items = new() {
HashSet<Asset> items = [
CreateCard(1, appID),
CreateCard(2, appID)
};
];
HashSet<Asset> itemsToSend = GetItemsForFullBadge(items, 2, appID);
@@ -212,10 +209,10 @@ public sealed class Bot {
const uint appID0 = 42;
const uint appID1 = 43;
HashSet<Asset> items = new() {
HashSet<Asset> items = [
CreateCard(1, appID0),
CreateCard(1, appID1)
};
];
HashSet<Asset> itemsToSend = GetItemsForFullBadge(
items, new Dictionary<uint, byte> {
@@ -237,10 +234,10 @@ public sealed class Bot {
const uint appID0 = 42;
const uint appID1 = 43;
HashSet<Asset> items = new() {
HashSet<Asset> items = [
CreateCard(1, appID0),
CreateCard(1, appID1)
};
];
HashSet<Asset> itemsToSend = GetItemsForFullBadge(
items, new Dictionary<uint, byte> {
@@ -260,14 +257,14 @@ public sealed class Bot {
const uint appID1 = 43;
const uint appID2 = 44;
HashSet<Asset> items = new() {
HashSet<Asset> items = [
CreateCard(1, appID0),
CreateCard(2, appID0),
CreateCard(1, appID1),
CreateCard(2, appID1),
CreateCard(3, appID1)
};
];
HashSet<Asset> itemsToSend = GetItemsForFullBadge(
items, new Dictionary<uint, byte> {
@@ -290,10 +287,10 @@ public sealed class Bot {
public void OtherRarityFullSets() {
const uint appID = 42;
HashSet<Asset> items = new() {
HashSet<Asset> items = [
CreateCard(1, appID, rarity: Asset.ERarity.Common),
CreateCard(1, appID, rarity: Asset.ERarity.Rare)
};
];
HashSet<Asset> itemsToSend = GetItemsForFullBadge(items, 1, appID);
@@ -308,10 +305,10 @@ public sealed class Bot {
public void OtherRarityNoSets() {
const uint appID = 42;
HashSet<Asset> items = new() {
HashSet<Asset> items = [
CreateCard(1, appID, rarity: Asset.ERarity.Common),
CreateCard(1, appID, rarity: Asset.ERarity.Rare)
};
];
HashSet<Asset> itemsToSend = GetItemsForFullBadge(items, 2, appID);
@@ -324,13 +321,13 @@ public sealed class Bot {
public void OtherRarityOneSet() {
const uint appID = 42;
HashSet<Asset> items = new() {
HashSet<Asset> items = [
CreateCard(1, appID, rarity: Asset.ERarity.Common),
CreateCard(2, appID, rarity: Asset.ERarity.Common),
CreateCard(1, appID, rarity: Asset.ERarity.Uncommon),
CreateCard(2, appID, rarity: Asset.ERarity.Uncommon),
CreateCard(3, appID, rarity: Asset.ERarity.Uncommon)
};
];
HashSet<Asset> itemsToSend = GetItemsForFullBadge(items, 3, appID);
@@ -347,10 +344,10 @@ public sealed class Bot {
public void OtherTypeFullSets() {
const uint appID = 42;
HashSet<Asset> items = new() {
HashSet<Asset> items = [
CreateCard(1, appID, type: Asset.EType.TradingCard),
CreateCard(1, appID, type: Asset.EType.FoilTradingCard)
};
];
HashSet<Asset> itemsToSend = GetItemsForFullBadge(items, 1, appID);
@@ -365,10 +362,10 @@ public sealed class Bot {
public void OtherTypeNoSets() {
const uint appID = 42;
HashSet<Asset> items = new() {
HashSet<Asset> items = [
CreateCard(1, appID, type: Asset.EType.TradingCard),
CreateCard(1, appID, type: Asset.EType.FoilTradingCard)
};
];
HashSet<Asset> itemsToSend = GetItemsForFullBadge(items, 2, appID);
@@ -381,13 +378,13 @@ public sealed class Bot {
public void OtherTypeOneSet() {
const uint appID = 42;
HashSet<Asset> items = new() {
HashSet<Asset> items = [
CreateCard(1, appID, type: Asset.EType.TradingCard),
CreateCard(2, appID, type: Asset.EType.TradingCard),
CreateCard(1, appID, type: Asset.EType.FoilTradingCard),
CreateCard(2, appID, type: Asset.EType.FoilTradingCard),
CreateCard(3, appID, type: Asset.EType.FoilTradingCard)
};
];
HashSet<Asset> itemsToSend = GetItemsForFullBadge(items, 3, appID);
@@ -404,10 +401,10 @@ public sealed class Bot {
public void TooHighAmount() {
const uint appID0 = 42;
HashSet<Asset> items = new() {
HashSet<Asset> items = [
CreateCard(1, appID0, 2),
CreateCard(2, appID0)
};
];
HashSet<Asset> itemsToSend = GetItemsForFullBadge(items, 2, appID0);
@@ -423,7 +420,7 @@ public sealed class Bot {
public void TooManyCardsForSingleTrade() {
const uint appID = 42;
HashSet<Asset> items = new();
HashSet<Asset> items = [];
for (byte i = 0; i < Steam.Exchange.Trading.MaxItemsPerTrade; i++) {
items.Add(CreateCard(1, appID));
@@ -440,7 +437,7 @@ public sealed class Bot {
const uint appID0 = 42;
const uint appID1 = 43;
HashSet<Asset> items = new();
HashSet<Asset> items = [];
for (byte i = 0; i < 100; i++) {
items.Add(CreateCard(1, appID0));
@@ -460,28 +457,27 @@ public sealed class Bot {
}
[TestMethod]
[ExpectedException(typeof(InvalidOperationException))]
public void TooManyCardsPerSet() {
const uint appID0 = 42;
const uint appID1 = 43;
const uint appID2 = 44;
HashSet<Asset> items = new() {
HashSet<Asset> items = [
CreateCard(1, appID0),
CreateCard(2, appID0),
CreateCard(3, appID0),
CreateCard(4, appID0)
};
];
GetItemsForFullBadge(
items, new Dictionary<uint, byte> {
{ appID0, 3 },
{ appID1, 3 },
{ appID2, 3 }
}
Assert.ThrowsException<InvalidOperationException>(
() => GetItemsForFullBadge(
items, new Dictionary<uint, byte> {
{ appID0, 3 },
{ appID1, 3 },
{ appID2, 3 }
}
)
);
Assert.Fail();
}
private static void AssertResultMatchesExpectation(IReadOnlyDictionary<(uint RealAppID, ulong ContextID, ulong ClassID), uint> expectedResult, IReadOnlyCollection<Asset> itemsToSend) {

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -200,60 +200,62 @@ public sealed class SteamChatMessage {
public async Task RyzhehvostInitialTestForSplitting() {
const string prefix = "/me ";
const string message = @"<XLimited5> Уже имеет: app/1493800 | Aircraft Carrier Survival: Prolouge
<XLimited5> Уже имеет: app/349520 | Armillo
<XLimited5> Уже имеет: app/346330 | BrainBread 2
<XLimited5> Уже имеет: app/1086690 | C-War 2
<XLimited5> Уже имеет: app/730 | Counter-Strike: Global Offensive
<XLimited5> Уже имеет: app/838380 | DEAD OR ALIVE 6
<XLimited5> Уже имеет: app/582890 | Estranged: The Departure
<XLimited5> Уже имеет: app/331470 | Everlasting Summer
<XLimited5> Уже имеет: app/1078000 | Gamecraft
<XLimited5> Уже имеет: app/266310 | GameGuru
<XLimited5> Уже имеет: app/275390 | Guacamelee! Super Turbo Championship Edition
<XLimited5> Уже имеет: app/627690 | Idle Champions of the Forgotten Realms
<XLimited5> Уже имеет: app/1048540 | Kao the Kangaroo: Round 2
<XLimited5> Уже имеет: app/370910 | Kathy Rain
<XLimited5> Уже имеет: app/343710 | KHOLAT
<XLimited5> Уже имеет: app/253900 | Knights and Merchants
<XLimited5> Уже имеет: app/224260 | No More Room in Hell
<XLimited5> Уже имеет: app/343360 | Particula
<XLimited5> Уже имеет: app/237870 | Planet Explorers
<XLimited5> Уже имеет: app/684680 | Polygoneer
<XLimited5> Уже имеет: app/1089130 | Quake II RTX
<XLimited5> Уже имеет: app/755790 | Ring of Elysium
<XLimited5> Уже имеет: app/1258080 | Shop Titans
<XLimited5> Уже имеет: app/759530 | Struckd - 3D Game Creator
<XLimited5> Уже имеет: app/269710 | Tumblestone
<XLimited5> Уже имеет: app/304930 | Unturned
<XLimited5> Уже имеет: app/1019250 | WWII TCG - World War 2: The Card Game
const string message = """
<XLimited5> Уже имеет: app/1493800 | Aircraft Carrier Survival: Prolouge
<XLimited5> Уже имеет: app/349520 | Armillo
<XLimited5> Уже имеет: app/346330 | BrainBread 2
<XLimited5> Уже имеет: app/1086690 | C-War 2
<XLimited5> Уже имеет: app/730 | Counter-Strike: Global Offensive
<XLimited5> Уже имеет: app/838380 | DEAD OR ALIVE 6
<XLimited5> Уже имеет: app/582890 | Estranged: The Departure
<XLimited5> Уже имеет: app/331470 | Everlasting Summer
<XLimited5> Уже имеет: app/1078000 | Gamecraft
<XLimited5> Уже имеет: app/266310 | GameGuru
<XLimited5> Уже имеет: app/275390 | Guacamelee! Super Turbo Championship Edition
<XLimited5> Уже имеет: app/627690 | Idle Champions of the Forgotten Realms
<XLimited5> Уже имеет: app/1048540 | Kao the Kangaroo: Round 2
<XLimited5> Уже имеет: app/370910 | Kathy Rain
<XLimited5> Уже имеет: app/343710 | KHOLAT
<XLimited5> Уже имеет: app/253900 | Knights and Merchants
<XLimited5> Уже имеет: app/224260 | No More Room in Hell
<XLimited5> Уже имеет: app/343360 | Particula
<XLimited5> Уже имеет: app/237870 | Planet Explorers
<XLimited5> Уже имеет: app/684680 | Polygoneer
<XLimited5> Уже имеет: app/1089130 | Quake II RTX
<XLimited5> Уже имеет: app/755790 | Ring of Elysium
<XLimited5> Уже имеет: app/1258080 | Shop Titans
<XLimited5> Уже имеет: app/759530 | Struckd - 3D Game Creator
<XLimited5> Уже имеет: app/269710 | Tumblestone
<XLimited5> Уже имеет: app/304930 | Unturned
<XLimited5> Уже имеет: app/1019250 | WWII TCG - World War 2: The Card Game
<ASF> 1/1 ботов уже имеют игру app/1493800 | Aircraft Carrier Survival: Prolouge.
<ASF> 1/1 ботов уже имеют игру app/349520 | Armillo.
<ASF> 1/1 ботов уже имеют игру app/346330 | BrainBread 2.
<ASF> 1/1 ботов уже имеют игру app/1086690 | C-War 2.
<ASF> 1/1 ботов уже имеют игру app/730 | Counter-Strike: Global Offensive.
<ASF> 1/1 ботов уже имеют игру app/838380 | DEAD OR ALIVE 6.
<ASF> 1/1 ботов уже имеют игру app/582890 | Estranged: The Departure.
<ASF> 1/1 ботов уже имеют игру app/331470 | Everlasting Summer.
<ASF> 1/1 ботов уже имеют игру app/1078000 | Gamecraft.
<ASF> 1/1 ботов уже имеют игру app/266310 | GameGuru.
<ASF> 1/1 ботов уже имеют игру app/275390 | Guacamelee! Super Turbo Championship Edition.
<ASF> 1/1 ботов уже имеют игру app/627690 | Idle Champions of the Forgotten Realms.
<ASF> 1/1 ботов уже имеют игру app/1048540 | Kao the Kangaroo: Round 2.
<ASF> 1/1 ботов уже имеют игру app/370910 | Kathy Rain.
<ASF> 1/1 ботов уже имеют игру app/343710 | KHOLAT.
<ASF> 1/1 ботов уже имеют игру app/253900 | Knights and Merchants.
<ASF> 1/1 ботов уже имеют игру app/224260 | No More Room in Hell.
<ASF> 1/1 ботов уже имеют игру app/343360 | Particula.
<ASF> 1/1 ботов уже имеют игру app/237870 | Planet Explorers.
<ASF> 1/1 ботов уже имеют игру app/684680 | Polygoneer.
<ASF> 1/1 ботов уже имеют игру app/1089130 | Quake II RTX.
<ASF> 1/1 ботов уже имеют игру app/755790 | Ring of Elysium.
<ASF> 1/1 ботов уже имеют игру app/1258080 | Shop Titans.
<ASF> 1/1 ботов уже имеют игру app/759530 | Struckd - 3D Game Creator.
<ASF> 1/1 ботов уже имеют игру app/269710 | Tumblestone.
<ASF> 1/1 ботов уже имеют игру app/304930 | Unturned.";
<ASF> 1/1 ботов уже имеют игру app/1493800 | Aircraft Carrier Survival: Prolouge.
<ASF> 1/1 ботов уже имеют игру app/349520 | Armillo.
<ASF> 1/1 ботов уже имеют игру app/346330 | BrainBread 2.
<ASF> 1/1 ботов уже имеют игру app/1086690 | C-War 2.
<ASF> 1/1 ботов уже имеют игру app/730 | Counter-Strike: Global Offensive.
<ASF> 1/1 ботов уже имеют игру app/838380 | DEAD OR ALIVE 6.
<ASF> 1/1 ботов уже имеют игру app/582890 | Estranged: The Departure.
<ASF> 1/1 ботов уже имеют игру app/331470 | Everlasting Summer.
<ASF> 1/1 ботов уже имеют игру app/1078000 | Gamecraft.
<ASF> 1/1 ботов уже имеют игру app/266310 | GameGuru.
<ASF> 1/1 ботов уже имеют игру app/275390 | Guacamelee! Super Turbo Championship Edition.
<ASF> 1/1 ботов уже имеют игру app/627690 | Idle Champions of the Forgotten Realms.
<ASF> 1/1 ботов уже имеют игру app/1048540 | Kao the Kangaroo: Round 2.
<ASF> 1/1 ботов уже имеют игру app/370910 | Kathy Rain.
<ASF> 1/1 ботов уже имеют игру app/343710 | KHOLAT.
<ASF> 1/1 ботов уже имеют игру app/253900 | Knights and Merchants.
<ASF> 1/1 ботов уже имеют игру app/224260 | No More Room in Hell.
<ASF> 1/1 ботов уже имеют игру app/343360 | Particula.
<ASF> 1/1 ботов уже имеют игру app/237870 | Planet Explorers.
<ASF> 1/1 ботов уже имеют игру app/684680 | Polygoneer.
<ASF> 1/1 ботов уже имеют игру app/1089130 | Quake II RTX.
<ASF> 1/1 ботов уже имеют игру app/755790 | Ring of Elysium.
<ASF> 1/1 ботов уже имеют игру app/1258080 | Shop Titans.
<ASF> 1/1 ботов уже имеют игру app/759530 | Struckd - 3D Game Creator.
<ASF> 1/1 ботов уже имеют игру app/269710 | Tumblestone.
<ASF> 1/1 ботов уже имеют игру app/304930 | Unturned.
""";
List<string> output = await GetMessageParts(message, prefix).ToListAsync().ConfigureAwait(false);
@@ -266,7 +268,7 @@ public sealed class SteamChatMessage {
return;
}
string[] lines = messagePart.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None);
string[] lines = messagePart.Split(SharedInfo.NewLineIndicators, StringSplitOptions.None);
int bytes = lines.Where(static line => line.Length > 0).Sum(Encoding.UTF8.GetByteCount) + ((lines.Length - 1) * NewlineWeight);
@@ -309,27 +311,21 @@ public sealed class SteamChatMessage {
Assert.AreEqual(newlinePart, output[3]);
}
[ExpectedException(typeof(ArgumentOutOfRangeException))]
[TestMethod]
public async Task ThrowsOnTooLongNewlinesPrefix() {
string prefix = new('\n', (MaxMessagePrefixBytes / NewlineWeight) + 1);
const string message = "asdf";
await GetMessageParts(message, prefix).ToListAsync().ConfigureAwait(false);
Assert.Fail();
await Assert.ThrowsExceptionAsync<ArgumentOutOfRangeException>(async () => await GetMessageParts(message, prefix).ToListAsync().ConfigureAwait(false)).ConfigureAwait(false);
}
[ExpectedException(typeof(ArgumentOutOfRangeException))]
[TestMethod]
public async Task ThrowsOnTooLongPrefix() {
string prefix = new('x', MaxMessagePrefixBytes + 1);
const string message = "asdf";
await GetMessageParts(message, prefix).ToListAsync().ConfigureAwait(false);
Assert.Fail();
await Assert.ThrowsExceptionAsync<ArgumentOutOfRangeException>(async () => await GetMessageParts(message, prefix).ToListAsync().ConfigureAwait(false)).ConfigureAwait(false);
}
}

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -30,21 +30,21 @@ namespace ArchiSteamFarm.Tests;
public sealed class Trading {
[TestMethod]
public void ExploitingNewSetsIsFairButNotNeutral() {
HashSet<Asset> inventory = new() {
HashSet<Asset> inventory = [
CreateItem(1, 40),
CreateItem(2, 10),
CreateItem(3, 10)
};
];
HashSet<Asset> itemsToGive = new() {
HashSet<Asset> itemsToGive = [
CreateItem(2, 5),
CreateItem(3, 5)
};
];
HashSet<Asset> itemsToReceive = new() {
HashSet<Asset> itemsToReceive = [
CreateItem(1, 9),
CreateItem(4)
};
];
Assert.IsTrue(IsFairExchange(itemsToGive, itemsToReceive));
Assert.IsFalse(IsTradeNeutralOrBetter(inventory, itemsToGive, itemsToReceive));
@@ -52,45 +52,45 @@ public sealed class Trading {
[TestMethod]
public void MismatchRarityIsNotFair() {
HashSet<Asset> itemsToGive = new() { CreateItem(1, rarity: Asset.ERarity.Rare) };
HashSet<Asset> itemsToReceive = new() { CreateItem(2) };
HashSet<Asset> itemsToGive = [CreateItem(1, rarity: Asset.ERarity.Rare)];
HashSet<Asset> itemsToReceive = [CreateItem(2)];
Assert.IsFalse(IsFairExchange(itemsToGive, itemsToReceive));
}
[TestMethod]
public void MismatchRealAppIDsIsNotFair() {
HashSet<Asset> itemsToGive = new() { CreateItem(1, realAppID: 570) };
HashSet<Asset> itemsToReceive = new() { CreateItem(2) };
HashSet<Asset> itemsToGive = [CreateItem(1, realAppID: 570)];
HashSet<Asset> itemsToReceive = [CreateItem(2)];
Assert.IsFalse(IsFairExchange(itemsToGive, itemsToReceive));
}
[TestMethod]
public void MismatchTypesIsNotFair() {
HashSet<Asset> itemsToGive = new() { CreateItem(1, type: Asset.EType.Emoticon) };
HashSet<Asset> itemsToReceive = new() { CreateItem(2) };
HashSet<Asset> itemsToGive = [CreateItem(1, type: Asset.EType.Emoticon)];
HashSet<Asset> itemsToReceive = [CreateItem(2)];
Assert.IsFalse(IsFairExchange(itemsToGive, itemsToReceive));
}
[TestMethod]
public void MultiGameMultiTypeBadReject() {
HashSet<Asset> inventory = new() {
HashSet<Asset> inventory = [
CreateItem(1, 9),
CreateItem(3, 9, 730, Asset.EType.Emoticon),
CreateItem(4, realAppID: 730, type: Asset.EType.Emoticon)
};
];
HashSet<Asset> itemsToGive = new() {
HashSet<Asset> itemsToGive = [
CreateItem(1),
CreateItem(4, realAppID: 730, type: Asset.EType.Emoticon)
};
];
HashSet<Asset> itemsToReceive = new() {
HashSet<Asset> itemsToReceive = [
CreateItem(2),
CreateItem(3, realAppID: 730, type: Asset.EType.Emoticon)
};
];
Assert.IsTrue(IsFairExchange(itemsToGive, itemsToReceive));
Assert.IsFalse(IsTradeNeutralOrBetter(inventory, itemsToGive, itemsToReceive));
@@ -98,20 +98,20 @@ public sealed class Trading {
[TestMethod]
public void MultiGameMultiTypeNeutralAccept() {
HashSet<Asset> inventory = new() {
HashSet<Asset> inventory = [
CreateItem(1, 9),
CreateItem(3, realAppID: 730, type: Asset.EType.Emoticon)
};
];
HashSet<Asset> itemsToGive = new() {
HashSet<Asset> itemsToGive = [
CreateItem(1),
CreateItem(3, realAppID: 730, type: Asset.EType.Emoticon)
};
];
HashSet<Asset> itemsToReceive = new() {
HashSet<Asset> itemsToReceive = [
CreateItem(2),
CreateItem(4, realAppID: 730, type: Asset.EType.Emoticon)
};
];
Assert.IsTrue(IsFairExchange(itemsToGive, itemsToReceive));
Assert.IsTrue(IsTradeNeutralOrBetter(inventory, itemsToGive, itemsToReceive));
@@ -119,21 +119,21 @@ public sealed class Trading {
[TestMethod]
public void MultiGameSingleTypeBadReject() {
HashSet<Asset> inventory = new() {
HashSet<Asset> inventory = [
CreateItem(1, 9),
CreateItem(3, realAppID: 730),
CreateItem(4, realAppID: 730)
};
];
HashSet<Asset> itemsToGive = new() {
HashSet<Asset> itemsToGive = [
CreateItem(1),
CreateItem(3, realAppID: 730)
};
];
HashSet<Asset> itemsToReceive = new() {
HashSet<Asset> itemsToReceive = [
CreateItem(2),
CreateItem(4, realAppID: 730)
};
];
Assert.IsTrue(IsFairExchange(itemsToGive, itemsToReceive));
Assert.IsFalse(IsTradeNeutralOrBetter(inventory, itemsToGive, itemsToReceive));
@@ -141,20 +141,20 @@ public sealed class Trading {
[TestMethod]
public void MultiGameSingleTypeNeutralAccept() {
HashSet<Asset> inventory = new() {
HashSet<Asset> inventory = [
CreateItem(1, 2),
CreateItem(3, realAppID: 730)
};
];
HashSet<Asset> itemsToGive = new() {
HashSet<Asset> itemsToGive = [
CreateItem(1),
CreateItem(3, realAppID: 730)
};
];
HashSet<Asset> itemsToReceive = new() {
HashSet<Asset> itemsToReceive = [
CreateItem(2),
CreateItem(4, realAppID: 730)
};
];
Assert.IsTrue(IsFairExchange(itemsToGive, itemsToReceive));
Assert.IsTrue(IsTradeNeutralOrBetter(inventory, itemsToGive, itemsToReceive));
@@ -162,21 +162,17 @@ public sealed class Trading {
[TestMethod]
public void SingleGameAbrynosWasWrongNeutralAccept() {
HashSet<Asset> inventory = new() {
HashSet<Asset> inventory = [
CreateItem(1),
CreateItem(2, 2),
CreateItem(3),
CreateItem(4),
CreateItem(5)
};
];
HashSet<Asset> itemsToGive = new() {
CreateItem(2)
};
HashSet<Asset> itemsToGive = [CreateItem(2)];
HashSet<Asset> itemsToReceive = new() {
CreateItem(3)
};
HashSet<Asset> itemsToReceive = [CreateItem(3)];
Assert.IsTrue(IsFairExchange(itemsToGive, itemsToReceive));
Assert.IsTrue(IsTradeNeutralOrBetter(inventory, itemsToGive, itemsToReceive));
@@ -184,18 +180,14 @@ public sealed class Trading {
[TestMethod]
public void SingleGameDonationAccept() {
HashSet<Asset> inventory = new() {
CreateItem(1)
};
HashSet<Asset> inventory = [CreateItem(1)];
HashSet<Asset> itemsToGive = new() {
CreateItem(1)
};
HashSet<Asset> itemsToGive = [CreateItem(1)];
HashSet<Asset> itemsToReceive = new() {
HashSet<Asset> itemsToReceive = [
CreateItem(2),
CreateItem(3, type: Asset.EType.SteamGems)
};
];
Assert.IsTrue(IsFairExchange(itemsToGive, itemsToReceive));
Assert.IsTrue(IsTradeNeutralOrBetter(inventory, itemsToGive, itemsToReceive));
@@ -203,21 +195,21 @@ public sealed class Trading {
[TestMethod]
public void SingleGameMultiTypeBadReject() {
HashSet<Asset> inventory = new() {
HashSet<Asset> inventory = [
CreateItem(1, 9),
CreateItem(3, 9, type: Asset.EType.Emoticon),
CreateItem(4, type: Asset.EType.Emoticon)
};
];
HashSet<Asset> itemsToGive = new() {
HashSet<Asset> itemsToGive = [
CreateItem(1),
CreateItem(4, type: Asset.EType.Emoticon)
};
];
HashSet<Asset> itemsToReceive = new() {
HashSet<Asset> itemsToReceive = [
CreateItem(2),
CreateItem(3, type: Asset.EType.Emoticon)
};
];
Assert.IsTrue(IsFairExchange(itemsToGive, itemsToReceive));
Assert.IsFalse(IsTradeNeutralOrBetter(inventory, itemsToGive, itemsToReceive));
@@ -225,20 +217,20 @@ public sealed class Trading {
[TestMethod]
public void SingleGameMultiTypeNeutralAccept() {
HashSet<Asset> inventory = new() {
HashSet<Asset> inventory = [
CreateItem(1, 9),
CreateItem(3, type: Asset.EType.Emoticon)
};
];
HashSet<Asset> itemsToGive = new() {
HashSet<Asset> itemsToGive = [
CreateItem(1),
CreateItem(3, type: Asset.EType.Emoticon)
};
];
HashSet<Asset> itemsToReceive = new() {
HashSet<Asset> itemsToReceive = [
CreateItem(2),
CreateItem(4, type: Asset.EType.Emoticon)
};
];
Assert.IsTrue(IsFairExchange(itemsToGive, itemsToReceive));
Assert.IsTrue(IsTradeNeutralOrBetter(inventory, itemsToGive, itemsToReceive));
@@ -246,19 +238,19 @@ public sealed class Trading {
[TestMethod]
public void SingleGameQuantityBadReject() {
HashSet<Asset> inventory = new() {
HashSet<Asset> inventory = [
CreateItem(1),
CreateItem(2),
CreateItem(3)
};
];
HashSet<Asset> itemsToGive = new() {
HashSet<Asset> itemsToGive = [
CreateItem(1),
CreateItem(2),
CreateItem(3)
};
];
HashSet<Asset> itemsToReceive = new() { CreateItem(4, 3) };
HashSet<Asset> itemsToReceive = [CreateItem(4, 3)];
Assert.IsTrue(IsFairExchange(itemsToGive, itemsToReceive));
Assert.IsFalse(IsTradeNeutralOrBetter(inventory, itemsToGive, itemsToReceive));
@@ -266,17 +258,17 @@ public sealed class Trading {
[TestMethod]
public void SingleGameQuantityBadReject2() {
HashSet<Asset> inventory = new() {
HashSet<Asset> inventory = [
CreateItem(1),
CreateItem(2, 2)
};
];
HashSet<Asset> itemsToGive = new() {
HashSet<Asset> itemsToGive = [
CreateItem(1),
CreateItem(2, 2)
};
];
HashSet<Asset> itemsToReceive = new() { CreateItem(3, 3) };
HashSet<Asset> itemsToReceive = [CreateItem(3, 3)];
Assert.IsTrue(IsFairExchange(itemsToGive, itemsToReceive));
Assert.IsFalse(IsTradeNeutralOrBetter(inventory, itemsToGive, itemsToReceive));
@@ -284,17 +276,17 @@ public sealed class Trading {
[TestMethod]
public void SingleGameQuantityNeutralAccept() {
HashSet<Asset> inventory = new() {
HashSet<Asset> inventory = [
CreateItem(1, 2),
CreateItem(2)
};
];
HashSet<Asset> itemsToGive = new() {
HashSet<Asset> itemsToGive = [
CreateItem(1),
CreateItem(2)
};
];
HashSet<Asset> itemsToReceive = new() { CreateItem(3, 2) };
HashSet<Asset> itemsToReceive = [CreateItem(3, 2)];
Assert.IsTrue(IsFairExchange(itemsToGive, itemsToReceive));
Assert.IsTrue(IsTradeNeutralOrBetter(inventory, itemsToGive, itemsToReceive));
@@ -302,13 +294,13 @@ public sealed class Trading {
[TestMethod]
public void SingleGameSingleTypeBadReject() {
HashSet<Asset> inventory = new() {
HashSet<Asset> inventory = [
CreateItem(1),
CreateItem(2)
};
];
HashSet<Asset> itemsToGive = new() { CreateItem(1) };
HashSet<Asset> itemsToReceive = new() { CreateItem(2) };
HashSet<Asset> itemsToGive = [CreateItem(1)];
HashSet<Asset> itemsToReceive = [CreateItem(2)];
Assert.IsTrue(IsFairExchange(itemsToGive, itemsToReceive));
Assert.IsFalse(IsTradeNeutralOrBetter(inventory, itemsToGive, itemsToReceive));
@@ -316,18 +308,18 @@ public sealed class Trading {
[TestMethod]
public void SingleGameSingleTypeBadWithOverpayingReject() {
HashSet<Asset> inventory = new() {
HashSet<Asset> inventory = [
CreateItem(1, 2),
CreateItem(2, 2),
CreateItem(3, 2)
};
];
HashSet<Asset> itemsToGive = new() { CreateItem(2) };
HashSet<Asset> itemsToGive = [CreateItem(2)];
HashSet<Asset> itemsToReceive = new() {
HashSet<Asset> itemsToReceive = [
CreateItem(1),
CreateItem(3)
};
];
Assert.IsTrue(IsFairExchange(itemsToGive, itemsToReceive));
Assert.IsFalse(IsTradeNeutralOrBetter(inventory, itemsToGive, itemsToReceive));
@@ -335,14 +327,14 @@ public sealed class Trading {
[TestMethod]
public void SingleGameSingleTypeBigDifferenceAccept() {
HashSet<Asset> inventory = new() {
HashSet<Asset> inventory = [
CreateItem(1),
CreateItem(2, 5),
CreateItem(3)
};
];
HashSet<Asset> itemsToGive = new() { CreateItem(2) };
HashSet<Asset> itemsToReceive = new() { CreateItem(3) };
HashSet<Asset> itemsToGive = [CreateItem(2)];
HashSet<Asset> itemsToReceive = [CreateItem(3)];
Assert.IsTrue(IsFairExchange(itemsToGive, itemsToReceive));
Assert.IsTrue(IsTradeNeutralOrBetter(inventory, itemsToGive, itemsToReceive));
@@ -350,23 +342,23 @@ public sealed class Trading {
[TestMethod]
public void SingleGameSingleTypeBigDifferenceReject() {
HashSet<Asset> inventory = new() {
HashSet<Asset> inventory = [
CreateItem(1),
CreateItem(2, 2),
CreateItem(3, 2),
CreateItem(4, 3),
CreateItem(5, 10)
};
];
HashSet<Asset> itemsToGive = new() {
HashSet<Asset> itemsToGive = [
CreateItem(2),
CreateItem(5)
};
];
HashSet<Asset> itemsToReceive = new() {
HashSet<Asset> itemsToReceive = [
CreateItem(3),
CreateItem(4)
};
];
Assert.IsTrue(IsFairExchange(itemsToGive, itemsToReceive));
Assert.IsFalse(IsTradeNeutralOrBetter(inventory, itemsToGive, itemsToReceive));
@@ -374,9 +366,9 @@ public sealed class Trading {
[TestMethod]
public void SingleGameSingleTypeGoodAccept() {
HashSet<Asset> inventory = new() { CreateItem(1, 2) };
HashSet<Asset> itemsToGive = new() { CreateItem(1) };
HashSet<Asset> itemsToReceive = new() { CreateItem(2) };
HashSet<Asset> inventory = [CreateItem(1, 2)];
HashSet<Asset> itemsToGive = [CreateItem(1)];
HashSet<Asset> itemsToReceive = [CreateItem(2)];
Assert.IsTrue(IsFairExchange(itemsToGive, itemsToReceive));
Assert.IsTrue(IsTradeNeutralOrBetter(inventory, itemsToGive, itemsToReceive));
@@ -384,9 +376,9 @@ public sealed class Trading {
[TestMethod]
public void SingleGameSingleTypeNeutralAccept() {
HashSet<Asset> inventory = new() { CreateItem(1) };
HashSet<Asset> itemsToGive = new() { CreateItem(1) };
HashSet<Asset> itemsToReceive = new() { CreateItem(2) };
HashSet<Asset> inventory = [CreateItem(1)];
HashSet<Asset> itemsToGive = [CreateItem(1)];
HashSet<Asset> itemsToReceive = [CreateItem(2)];
Assert.IsTrue(IsFairExchange(itemsToGive, itemsToReceive));
Assert.IsTrue(IsTradeNeutralOrBetter(inventory, itemsToGive, itemsToReceive));
@@ -394,17 +386,41 @@ public sealed class Trading {
[TestMethod]
public void SingleGameSingleTypeNeutralWithOverpayingAccept() {
HashSet<Asset> inventory = new() {
HashSet<Asset> inventory = [
CreateItem(1, 2),
CreateItem(2, 2)
};
];
HashSet<Asset> itemsToGive = new() { CreateItem(2) };
HashSet<Asset> itemsToGive = [CreateItem(2)];
HashSet<Asset> itemsToReceive = new() {
HashSet<Asset> itemsToReceive = [
CreateItem(1),
CreateItem(3)
};
];
Assert.IsTrue(IsFairExchange(itemsToGive, itemsToReceive));
Assert.IsTrue(IsTradeNeutralOrBetter(inventory, itemsToGive, itemsToReceive));
}
[TestMethod]
public void TakingExcessiveAmountOfSingleCardCanStillBeFairAndNeutral() {
HashSet<Asset> inventory = [
CreateItem(1, 52),
CreateItem(2, 73),
CreateItem(3, 52),
CreateItem(4, 47),
CreateItem(5)
];
HashSet<Asset> itemsToGive = [CreateItem(2, 73)];
HashSet<Asset> itemsToReceive = [
CreateItem(1, 9),
CreateItem(3, 9),
CreateItem(4, 8),
CreateItem(5, 24),
CreateItem(6, 23)
];
Assert.IsTrue(IsFairExchange(itemsToGive, itemsToReceive));
Assert.IsTrue(IsTradeNeutralOrBetter(inventory, itemsToGive, itemsToReceive));

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");

File diff suppressed because one or more lines are too long

View File

@@ -4,6 +4,9 @@
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<OpenApiGenerateDocuments>false</OpenApiGenerateDocuments>
<OutputType>Exe</OutputType>
<!-- TODO: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/2550 -->
<JsonSerializerIsReflectionEnabledByDefault>true</JsonSerializerIsReflectionEnabledByDefault>
</PropertyGroup>
<ItemGroup>
@@ -13,41 +16,16 @@
<PackageReference Include="Humanizer" />
<PackageReference Include="JetBrains.Annotations" PrivateAssets="all" />
<PackageReference Include="Markdig.Signed" />
<PackageReference Include="Newtonsoft.Json" />
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" />
<PackageReference Include="Nito.AsyncEx.Coordination" />
<PackageReference Include="NLog.Web.AspNetCore" />
<PackageReference Include="SteamKit2" />
<PackageReference Include="Swashbuckle.AspNetCore" />
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" />
<PackageReference Include="Swashbuckle.AspNetCore.Newtonsoft" />
<PackageReference Include="System.Composition" />
<PackageReference Include="System.Linq.Async" />
<PackageReference Include="zxcvbn-core" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' != 'net481'">
<PackageReference Include="System.Security.Cryptography.ProtectedData" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net481' OR '$(TargetFramework)' == 'netstandard2.1'">
<PackageReference Include="Microsoft.AspNetCore.Cors" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics" />
<PackageReference Include="Microsoft.AspNetCore.HttpOverrides" />
<PackageReference Include="Microsoft.AspNetCore.ResponseCaching" />
<PackageReference Include="Microsoft.AspNetCore.ResponseCompression" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" />
<PackageReference Include="Microsoft.AspNetCore.WebSockets" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" />
<PackageReference Include="Microsoft.Extensions.Logging.Configuration" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net481'">
<Reference Include="System.Net.Http" HintPath="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8.1\System.Net.Http.dll" />
<Reference Include="System.Security" HintPath="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8.1\System.Security.dll" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.1'">
<PackageReference Include="System.IO.FileSystem.AccessControl" />
<PackageReference Include="zxcvbn-core" />
</ItemGroup>
<ItemGroup>
@@ -86,7 +64,7 @@
</Content>
</ItemGroup>
<ItemGroup Condition="Exists($([System.IO.Path]::Combine('overlay', 'variant-base', $(ASFVariant.Split('-')[0]))))">
<ItemGroup Condition="'$(ASFVariant)' != '' AND Exists($([System.IO.Path]::Combine('overlay', 'variant-base', $(ASFVariant.Split('-')[0]))))">
<Content Include="overlay/variant-base/$(ASFVariant.Split('-')[0])/**/*.*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
@@ -94,7 +72,7 @@
</Content>
</ItemGroup>
<ItemGroup Condition="Exists($([System.IO.Path]::Combine('overlay', 'variant-specific', $(ASFVariant))))">
<ItemGroup Condition="'$(ASFVariant)' != '' AND Exists($([System.IO.Path]::Combine('overlay', 'variant-specific', $(ASFVariant))))">
<Content Include="overlay/variant-specific/$(ASFVariant)/**/*.*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -35,9 +35,10 @@ internal sealed class ConcurrentEnumerator<T> : IEnumerator<T> {
internal ConcurrentEnumerator(IReadOnlyCollection<T> collection, IDisposable lockObject) {
ArgumentNullException.ThrowIfNull(collection);
ArgumentNullException.ThrowIfNull(lockObject);
LockObject = lockObject ?? throw new ArgumentNullException(nameof(lockObject));
Enumerator = collection.GetEnumerator();
LockObject = lockObject;
}
public void Dispose() {

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,11 +24,13 @@ using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Serialization;
using JetBrains.Annotations;
namespace ArchiSteamFarm.Collections;
public sealed class ConcurrentHashSet<T> : IReadOnlyCollection<T>, ISet<T> where T : notnull {
public sealed class ConcurrentHashSet<T> : IReadOnlySet<T>, ISet<T> where T : notnull {
[PublicAPI]
public event EventHandler? OnModified;
public int Count => BackingCollection.Count;
@@ -36,15 +38,31 @@ public sealed class ConcurrentHashSet<T> : IReadOnlyCollection<T>, ISet<T> where
private readonly ConcurrentDictionary<T, bool> BackingCollection;
[JsonConstructor]
public ConcurrentHashSet() => BackingCollection = new ConcurrentDictionary<T, bool>();
public ConcurrentHashSet(IEnumerable<T> collection) {
ArgumentNullException.ThrowIfNull(collection);
BackingCollection = new ConcurrentDictionary<T, bool>(collection.Select(static item => new KeyValuePair<T, bool>(item, true)));
}
public ConcurrentHashSet(IEqualityComparer<T> comparer) {
ArgumentNullException.ThrowIfNull(comparer);
BackingCollection = new ConcurrentDictionary<T, bool>(comparer);
}
public ConcurrentHashSet(IEnumerable<T> collection, IEqualityComparer<T> comparer) {
ArgumentNullException.ThrowIfNull(collection);
ArgumentNullException.ThrowIfNull(comparer);
BackingCollection = new ConcurrentDictionary<T, bool>(collection.Select(static item => new KeyValuePair<T, bool>(item, true)), comparer);
}
public bool Add(T item) {
ArgumentNullException.ThrowIfNull(item);
if (!BackingCollection.TryAdd(item, true)) {
return false;
}
@@ -64,9 +82,18 @@ public sealed class ConcurrentHashSet<T> : IReadOnlyCollection<T>, ISet<T> where
OnModified?.Invoke(this, EventArgs.Empty);
}
public bool Contains(T item) => BackingCollection.ContainsKey(item);
public bool Contains(T item) {
ArgumentNullException.ThrowIfNull(item);
public void CopyTo(T[] array, int arrayIndex) => BackingCollection.Keys.CopyTo(array, arrayIndex);
return BackingCollection.ContainsKey(item);
}
public void CopyTo(T[] array, int arrayIndex) {
ArgumentNullException.ThrowIfNull(array);
ArgumentOutOfRangeException.ThrowIfNegative(arrayIndex);
BackingCollection.Keys.CopyTo(array, arrayIndex);
}
public void ExceptWith(IEnumerable<T> other) {
ArgumentNullException.ThrowIfNull(other);
@@ -79,6 +106,8 @@ public sealed class ConcurrentHashSet<T> : IReadOnlyCollection<T>, ISet<T> where
public IEnumerator<T> GetEnumerator() => BackingCollection.Keys.GetEnumerator();
public void IntersectWith(IEnumerable<T> other) {
ArgumentNullException.ThrowIfNull(other);
ISet<T> otherSet = other as ISet<T> ?? other.ToHashSet();
foreach (T item in this.Where(item => !otherSet.Contains(item))) {
@@ -87,36 +116,48 @@ public sealed class ConcurrentHashSet<T> : IReadOnlyCollection<T>, ISet<T> where
}
public bool IsProperSubsetOf(IEnumerable<T> other) {
ArgumentNullException.ThrowIfNull(other);
ISet<T> otherSet = other as ISet<T> ?? other.ToHashSet();
return (otherSet.Count > Count) && IsSubsetOf(otherSet);
}
public bool IsProperSupersetOf(IEnumerable<T> other) {
ArgumentNullException.ThrowIfNull(other);
ISet<T> otherSet = other as ISet<T> ?? other.ToHashSet();
return (otherSet.Count < Count) && IsSupersetOf(otherSet);
}
public bool IsSubsetOf(IEnumerable<T> other) {
ArgumentNullException.ThrowIfNull(other);
ISet<T> otherSet = other as ISet<T> ?? other.ToHashSet();
return this.All(otherSet.Contains);
}
public bool IsSupersetOf(IEnumerable<T> other) {
ArgumentNullException.ThrowIfNull(other);
ISet<T> otherSet = other as ISet<T> ?? other.ToHashSet();
return otherSet.All(Contains);
}
public bool Overlaps(IEnumerable<T> other) {
ArgumentNullException.ThrowIfNull(other);
ISet<T> otherSet = other as ISet<T> ?? other.ToHashSet();
return otherSet.Any(Contains);
}
public bool Remove(T item) {
ArgumentNullException.ThrowIfNull(item);
if (!BackingCollection.TryRemove(item, out _)) {
return false;
}
@@ -127,14 +168,18 @@ public sealed class ConcurrentHashSet<T> : IReadOnlyCollection<T>, ISet<T> where
}
public bool SetEquals(IEnumerable<T> other) {
ArgumentNullException.ThrowIfNull(other);
ISet<T> otherSet = other as ISet<T> ?? other.ToHashSet();
return (otherSet.Count == Count) && otherSet.All(Contains);
}
public void SymmetricExceptWith(IEnumerable<T> other) {
ArgumentNullException.ThrowIfNull(other);
ISet<T> otherSet = other as ISet<T> ?? other.ToHashSet();
HashSet<T> removed = new();
HashSet<T> removed = [];
foreach (T item in otherSet.Where(Contains)) {
removed.Add(item);
@@ -154,12 +199,18 @@ public sealed class ConcurrentHashSet<T> : IReadOnlyCollection<T>, ISet<T> where
}
}
void ICollection<T>.Add(T item) => Add(item);
void ICollection<T>.Add(T item) {
ArgumentNullException.ThrowIfNull(item);
Add(item);
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
[PublicAPI]
public bool AddRange(IEnumerable<T> items) {
ArgumentNullException.ThrowIfNull(items);
bool result = false;
foreach (T _ in items.Where(Add)) {
@@ -171,6 +222,8 @@ public sealed class ConcurrentHashSet<T> : IReadOnlyCollection<T>, ISet<T> where
[PublicAPI]
public bool RemoveRange(IEnumerable<T> items) {
ArgumentNullException.ThrowIfNull(items);
bool result = false;
foreach (T _ in items.Where(Remove)) {
@@ -180,8 +233,17 @@ public sealed class ConcurrentHashSet<T> : IReadOnlyCollection<T>, ISet<T> where
return result;
}
[PublicAPI]
public int RemoveWhere(Predicate<T> match) {
ArgumentNullException.ThrowIfNull(match);
return BackingCollection.Keys.Where(match.Invoke).Count(key => BackingCollection.TryRemove(key, out _));
}
[PublicAPI]
public bool ReplaceIfNeededWith(IReadOnlyCollection<T> other) {
ArgumentNullException.ThrowIfNull(other);
if (SetEquals(other)) {
return false;
}
@@ -193,6 +255,8 @@ public sealed class ConcurrentHashSet<T> : IReadOnlyCollection<T>, ISet<T> where
[PublicAPI]
public void ReplaceWith(IEnumerable<T> other) {
ArgumentNullException.ThrowIfNull(other);
Clear();
UnionWith(other);
}

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,16 +19,21 @@
// See the License for the specific language governing permissions and
// limitations under the License.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text.Json.Serialization;
using JetBrains.Annotations;
using Nito.AsyncEx;
namespace ArchiSteamFarm.Collections;
internal sealed class ConcurrentList<T> : IList<T>, IReadOnlyList<T> {
public bool IsReadOnly => false;
public sealed class ConcurrentList<T> : IList<T>, IReadOnlyList<T> where T : notnull {
[PublicAPI]
public event EventHandler? OnModified;
internal int Count {
[PublicAPI]
public int Count {
get {
using (Lock.ReaderLock()) {
return BackingCollection.Count;
@@ -36,7 +41,9 @@ internal sealed class ConcurrentList<T> : IList<T>, IReadOnlyList<T> {
}
}
private readonly List<T> BackingCollection = new();
public bool IsReadOnly => false;
private readonly List<T> BackingCollection;
private readonly AsyncReaderWriterLock Lock = new();
int ICollection<T>.Count => Count;
@@ -50,31 +57,55 @@ internal sealed class ConcurrentList<T> : IList<T>, IReadOnlyList<T> {
}
set {
ArgumentNullException.ThrowIfNull(value);
using (Lock.WriterLock()) {
BackingCollection[index] = value;
}
OnModified?.Invoke(this, EventArgs.Empty);
}
}
[JsonConstructor]
public ConcurrentList() => BackingCollection = [];
public ConcurrentList(IEnumerable<T> collection) {
ArgumentNullException.ThrowIfNull(collection);
BackingCollection = [..collection];
}
public void Add(T item) {
ArgumentNullException.ThrowIfNull(item);
using (Lock.WriterLock()) {
BackingCollection.Add(item);
}
OnModified?.Invoke(this, EventArgs.Empty);
}
public void Clear() {
using (Lock.WriterLock()) {
BackingCollection.Clear();
}
OnModified?.Invoke(this, EventArgs.Empty);
}
public bool Contains(T item) {
ArgumentNullException.ThrowIfNull(item);
using (Lock.ReaderLock()) {
return BackingCollection.Contains(item);
}
}
public void CopyTo(T[] array, int arrayIndex) {
ArgumentNullException.ThrowIfNull(array);
ArgumentOutOfRangeException.ThrowIfNegative(arrayIndex);
using (Lock.ReaderLock()) {
BackingCollection.CopyTo(array, arrayIndex);
}
@@ -83,35 +114,59 @@ internal sealed class ConcurrentList<T> : IList<T>, IReadOnlyList<T> {
public IEnumerator<T> GetEnumerator() => new ConcurrentEnumerator<T>(BackingCollection, Lock.ReaderLock());
public int IndexOf(T item) {
ArgumentNullException.ThrowIfNull(item);
using (Lock.ReaderLock()) {
return BackingCollection.IndexOf(item);
}
}
public void Insert(int index, T item) {
ArgumentOutOfRangeException.ThrowIfNegative(index);
ArgumentNullException.ThrowIfNull(item);
using (Lock.WriterLock()) {
BackingCollection.Insert(index, item);
}
OnModified?.Invoke(this, EventArgs.Empty);
}
public bool Remove(T item) {
ArgumentNullException.ThrowIfNull(item);
using (Lock.WriterLock()) {
return BackingCollection.Remove(item);
if (!BackingCollection.Remove(item)) {
return false;
}
}
OnModified?.Invoke(this, EventArgs.Empty);
return true;
}
public void RemoveAt(int index) {
ArgumentOutOfRangeException.ThrowIfNegative(index);
using (Lock.WriterLock()) {
BackingCollection.RemoveAt(index);
}
OnModified?.Invoke(this, EventArgs.Empty);
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
internal void ReplaceWith(IEnumerable<T> collection) {
[PublicAPI]
public void ReplaceWith(IEnumerable<T> collection) {
ArgumentNullException.ThrowIfNull(collection);
using (Lock.WriterLock()) {
BackingCollection.Clear();
BackingCollection.AddRange(collection);
}
OnModified?.Invoke(this, EventArgs.Empty);
}
}

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -23,22 +23,17 @@ using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using ArchiSteamFarm.Core;
namespace ArchiSteamFarm.Collections;
internal sealed class FixedSizeConcurrentQueue<T> : IEnumerable<T> {
internal sealed class FixedSizeConcurrentQueue<T> : IEnumerable<T> where T : notnull {
private readonly ConcurrentQueue<T> BackingQueue = new();
internal byte MaxCount {
get => BackingMaxCount;
set {
if (value == 0) {
ASF.ArchiLogger.LogNullError(value);
return;
}
ArgumentOutOfRangeException.ThrowIfZero(value);
BackingMaxCount = value;
@@ -49,9 +44,7 @@ internal sealed class FixedSizeConcurrentQueue<T> : IEnumerable<T> {
private byte BackingMaxCount;
internal FixedSizeConcurrentQueue(byte maxCount) {
if (maxCount == 0) {
throw new ArgumentNullException(nameof(maxCount));
}
ArgumentOutOfRangeException.ThrowIfZero(maxCount);
MaxCount = maxCount;
}
@@ -60,6 +53,8 @@ internal sealed class FixedSizeConcurrentQueue<T> : IEnumerable<T> {
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
internal void Enqueue(T obj) {
ArgumentNullException.ThrowIfNull(obj);
BackingQueue.Enqueue(obj);
Resize();

View File

@@ -4,7 +4,7 @@
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -23,12 +23,13 @@ using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Text.Json.Serialization;
using JetBrains.Annotations;
using Newtonsoft.Json;
namespace ArchiSteamFarm.Collections;
public sealed class ObservableConcurrentDictionary<TKey, TValue> : IDictionary<TKey, TValue>, IReadOnlyDictionary<TKey, TValue> where TKey : notnull {
[PublicAPI]
public event EventHandler? OnModified;
[PublicAPI]
@@ -39,8 +40,10 @@ public sealed class ObservableConcurrentDictionary<TKey, TValue> : IDictionary<T
public bool IsReadOnly => false;
[JsonProperty(Required = Required.DisallowNull)]
private readonly ConcurrentDictionary<TKey, TValue> BackingDictionary = new();
[PublicAPI]
public ICollection<TKey> Keys => BackingDictionary.Keys;
private readonly ConcurrentDictionary<TKey, TValue> BackingDictionary;
int ICollection<KeyValuePair<TKey, TValue>>.Count => BackingDictionary.Count;
int IReadOnlyCollection<KeyValuePair<TKey, TValue>>.Count => BackingDictionary.Count;
@@ -51,7 +54,10 @@ public sealed class ObservableConcurrentDictionary<TKey, TValue> : IDictionary<T
public TValue this[TKey key] {
get => BackingDictionary[key];
set {
ArgumentNullException.ThrowIfNull(value);
if (BackingDictionary.TryGetValue(key, out TValue? savedValue) && EqualityComparer<TValue>.Default.Equals(savedValue, value)) {
return;
}
@@ -61,13 +67,39 @@ public sealed class ObservableConcurrentDictionary<TKey, TValue> : IDictionary<T
}
}
[JsonConstructor]
public ObservableConcurrentDictionary() => BackingDictionary = new ConcurrentDictionary<TKey, TValue>();
public ObservableConcurrentDictionary(IEnumerable<KeyValuePair<TKey, TValue>> collection) {
ArgumentNullException.ThrowIfNull(collection);
BackingDictionary = new ConcurrentDictionary<TKey, TValue>(collection);
}
public ObservableConcurrentDictionary(IEqualityComparer<TKey> comparer) {
ArgumentNullException.ThrowIfNull(comparer);
BackingDictionary = new ConcurrentDictionary<TKey, TValue>(comparer);
}
public ObservableConcurrentDictionary(IEnumerable<KeyValuePair<TKey, TValue>> collection, IEqualityComparer<TKey> comparer) {
ArgumentNullException.ThrowIfNull(collection);
ArgumentNullException.ThrowIfNull(comparer);
BackingDictionary = new ConcurrentDictionary<TKey, TValue>(collection, comparer);
}
public void Add(KeyValuePair<TKey, TValue> item) {
(TKey key, TValue value) = item;
Add(key, value);
}
public void Add(TKey key, TValue value) => TryAdd(key, value);
public void Add(TKey key, TValue value) {
ArgumentNullException.ThrowIfNull(key);
TryAdd(key, value);
}
public void Clear() {
if (BackingDictionary.IsEmpty) {
@@ -79,7 +111,14 @@ public sealed class ObservableConcurrentDictionary<TKey, TValue> : IDictionary<T
}
public bool Contains(KeyValuePair<TKey, TValue> item) => ((ICollection<KeyValuePair<TKey, TValue>>) BackingDictionary).Contains(item);
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) => ((ICollection<KeyValuePair<TKey, TValue>>) BackingDictionary).CopyTo(array, arrayIndex);
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) {
ArgumentNullException.ThrowIfNull(array);
ArgumentOutOfRangeException.ThrowIfNegative(arrayIndex);
((ICollection<KeyValuePair<TKey, TValue>>) BackingDictionary).CopyTo(array, arrayIndex);
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => BackingDictionary.GetEnumerator();
public bool Remove(KeyValuePair<TKey, TValue> item) {
@@ -95,6 +134,8 @@ public sealed class ObservableConcurrentDictionary<TKey, TValue> : IDictionary<T
}
public bool Remove(TKey key) {
ArgumentNullException.ThrowIfNull(key);
if (!BackingDictionary.TryRemove(key, out _)) {
return false;
}
@@ -104,14 +145,36 @@ public sealed class ObservableConcurrentDictionary<TKey, TValue> : IDictionary<T
return true;
}
bool IDictionary<TKey, TValue>.ContainsKey(TKey key) => BackingDictionary.ContainsKey(key);
bool IReadOnlyDictionary<TKey, TValue>.ContainsKey(TKey key) => BackingDictionary.ContainsKey(key);
bool IDictionary<TKey, TValue>.ContainsKey(TKey key) {
ArgumentNullException.ThrowIfNull(key);
return BackingDictionary.ContainsKey(key);
}
bool IReadOnlyDictionary<TKey, TValue>.ContainsKey(TKey key) {
ArgumentNullException.ThrowIfNull(key);
return BackingDictionary.ContainsKey(key);
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
bool IReadOnlyDictionary<TKey, TValue>.TryGetValue(TKey key, out TValue value) => BackingDictionary.TryGetValue(key, out value!);
bool IDictionary<TKey, TValue>.TryGetValue(TKey key, out TValue value) => BackingDictionary.TryGetValue(key, out value!);
bool IReadOnlyDictionary<TKey, TValue>.TryGetValue(TKey key, out TValue value) {
ArgumentNullException.ThrowIfNull(key);
return BackingDictionary.TryGetValue(key, out value!);
}
bool IDictionary<TKey, TValue>.TryGetValue(TKey key, out TValue value) {
ArgumentNullException.ThrowIfNull(key);
return BackingDictionary.TryGetValue(key, out value!);
}
[PublicAPI]
public bool TryAdd(TKey key, TValue value) {
ArgumentNullException.ThrowIfNull(key);
if (!BackingDictionary.TryAdd(key, value)) {
return false;
}
@@ -122,5 +185,9 @@ public sealed class ObservableConcurrentDictionary<TKey, TValue> : IDictionary<T
}
[PublicAPI]
public bool TryGetValue(TKey key, out TValue? value) => BackingDictionary.TryGetValue(key, out value);
public bool TryGetValue(TKey key, out TValue? value) {
ArgumentNullException.ThrowIfNull(key);
return BackingDictionary.TryGetValue(key, out value);
}
}

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