Compare commits

..

24 Commits

Author SHA1 Message Date
Archi
d16c4822eb Bump 2022-02-10 20:14:35 +01:00
Archi
03e3d74e51 Allow more than one persona flag to be used 2022-02-10 20:10:34 +01:00
Archi
0a3d011e2e More advanced improvements over persona state 2022-02-10 19:55:32 +01:00
Archi
f112a05569 Rider cleanup after merge 2022-02-10 19:44:53 +01:00
Deyvan
1c579d96ee Add VR to UserInterfaceMode (#2511)
* Add VR to UserInterfaceMode

* Add VRMode to BotConfig

* Add logic for VRMode

* Remove VR from EUserInterfaceMode

* Remake VRMode -> PersonaStateFlags

* Rename PersonaStateFlags -> OnlineFlags for more user-friendly

* Parameter checks for SetPersonaStateFlags

* oops

* Update Bot.cs
2022-02-10 19:42:42 +01:00
ArchiBot
19a0be1d26 Automatic translations update 2022-02-10 02:08:21 +00:00
Renovate Bot
a8de495c7c Update ASF-ui commit hash to ff43133 2022-02-09 03:04:43 +00:00
ArchiBot
ab8ceab055 Automatic translations update 2022-02-09 02:12:42 +00:00
Renovate Bot
64b72d1e55 Update ASF-ui commit hash to c7acea4 2022-02-08 22:28:45 +00:00
Łukasz Domeradzki
f807bdb660 Fix permissions when proxifying commands (#2509)
* Fix permissions when proxifying commands

* Version bump
2022-02-08 23:17:03 +01:00
Archi
5b66b70566 Add PlayingWasBlocked logic to GamesPlayedWhileIdle 2022-02-08 17:42:14 +01:00
Renovate Bot
41c06851a5 Update ASF-ui commit hash to 533e608 2022-02-08 03:51:33 +00:00
ArchiBot
4dbb964ba9 Automatic translations update 2022-02-08 02:09:13 +00:00
Renovate Bot
11471c759d Update ASF-ui commit hash to eaddc99 2022-02-07 23:44:16 +00:00
Renovate Bot
2aa4ab7fe8 Update ASF-ui commit hash to 114ff77 2022-02-07 04:53:06 +00:00
ArchiBot
dfc055c066 Automatic translations update 2022-02-07 02:08:26 +00:00
ArchiBot
1a0ac11f46 Automatic translations update 2022-02-06 02:17:05 +00:00
ArchiBot
7266864b3b Automatic translations update 2022-02-05 02:01:05 +00:00
Archi
b52f746138 Remove dead code 2022-02-04 14:47:13 +01:00
Archi
a2585ec8c9 Remove obsolete features 2022-02-04 14:46:09 +01:00
Renovate Bot
37781698e0 Update wiki commit hash to f917797 2022-02-04 10:06:13 +00:00
ArchiBot
2a8fe7611b Automatic translations update 2022-02-04 02:03:32 +00:00
Renovate Bot
8fdf14bb10 Update wiki commit hash to 15d73b5 2022-02-03 20:48:34 +00:00
Archi
31db72b2d6 Bump 2022-02-03 21:05:04 +01:00
14 changed files with 517 additions and 433 deletions

2
ASF-ui

Submodule ASF-ui updated: e25e4c21dd...ff4313357a

View File

@@ -63,42 +63,42 @@
</value>
</resheader>
<data name="PluginDisabledMissingBuildToken" xml:space="preserve">
<value>{0} foi desativado devido à falta de um token de compilação</value>
<value>O {0} foi desativado devido a um token de compilação ausente</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} está desativado de acordo com sua configuração. Se você gostaria de ajudar o SteamDB no envio de dados, por favor, confira nosso wiki.</value>
<value>O {0} está desativado de acordo com a sua configuração. Caso deseje ajudar o SteamDB com o envio de informações, dê uma olhada na nossa wiki.</value>
<comment>{0} will be replaced by the name of the plugin (e.g. "SteamTokenDumperPlugin")</comment>
</data>
<data name="PluginInitializedAndEnabled" xml:space="preserve">
<value>{0} foi inicializado com sucesso, obrigado antecipadamente pela sua ajuda. O primeiro envio ocorrerá em aproximadamente {1} a partir de agora.</value>
<value>O {0} foi inicializado com sucesso, agradecemos a sua ajuda. O primeiro envio ocorrerá em aproximadamente {1}.</value>
<comment>{0} will be replaced by the name of the plugin (e.g. "SteamTokenDumperPlugin"), {1} will be replaced by translated TimeSpan string (such as "53 minutes")</comment>
</data>
<data name="FileCouldNotBeLoadedFreshInit" xml:space="preserve">
<value>{0} não pôde ser carregado, uma instância nova será inicializada...</value>
<value>O {0} não pôde ser carregado, uma instância nova será inicializada...</value>
<comment>{0} will be replaced by the name of the file (e.g. "GlobalCache")</comment>
</data>
<data name="BotNoAppsToRefresh" xml:space="preserve">
<value>Não há aplicativos que necessitem de ser atualizados nesta instância de bot.</value>
<value>Não há aplicativos que exijam atualizações na instância atual.</value>
</data>
<data name="BotRetrievingTotalAppAccessTokens" xml:space="preserve">
<value>Recuperando um total de {0} tokens de acesso a aplicativos...</value>
<value>Recuperando um total de {0} tokens de acesso para aplicativos...</value>
<comment>{0} will be replaced by the number (total count) of app access tokens being retrieved</comment>
</data>
<data name="BotRetrievingAppAccessTokens" xml:space="preserve">
<value>Recuperando {0} tokens de acesso a aplicativos...</value>
<value>Recuperando {0} tokens...</value>
<comment>{0} will be replaced by the number (count this batch) of app access tokens being retrieved</comment>
</data>
<data name="BotFinishedRetrievingAppAccessTokens" xml:space="preserve">
<value>Concluímos a recuperação de {0} tokens de acesso ao aplicativo.</value>
<value>Recuperamos {0} tokens.</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>Obtivemos um total de {0} tokens de acesso aos aplicativos.</value>
<value>Recuperamos um total de {0} tokens de acesso.</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>Recuperando todos os depósitos por um total de {0} apps...</value>
<value>Recuperando depots para todos os {0} aplicativos...</value>
<comment>{0} will be replaced by the number (total count) of apps being retrieved</comment>
</data>
<data name="BotRetrievingAppInfos" xml:space="preserve">
@@ -106,38 +106,38 @@
<comment>{0} will be replaced by the number (count this batch) of app infos being retrieved</comment>
</data>
<data name="BotFinishedRetrievingAppInfos" xml:space="preserve">
<value>Concluímos a recuperação de {0} informações de aplicativo.</value>
<value>Recuperamos um total de {0} informações de aplicativos.</value>
<comment>{0} will be replaced by the number (count this batch) of app infos retrieved</comment>
</data>
<data name="BotRetrievingDepotKeys" xml:space="preserve">
<value>Recuperando {0} chaves de depósito...</value>
<value>Recuperando {0} códigos de depots...</value>
<comment>{0} will be replaced by the number (count this batch) of depot keys being retrieved</comment>
</data>
<data name="BotFinishedRetrievingDepotKeys" xml:space="preserve">
<value>Terminamos de recuperar {0} chaves de depósito.</value>
<value>Recuperamos códigos para {0} depots.</value>
<comment>{0} will be replaced by the number (count this batch) of depot keys retrieved</comment>
</data>
<data name="BotFinishedRetrievingTotalDepots" xml:space="preserve">
<value>Terminamos de recuperar todas as chaves de depósito para um total de {0} apps.</value>
<value>Recuperamos códigos de acesso para {0} aplicativos.</value>
<comment>{0} will be replaced by the number (total count) of apps retrieved</comment>
</data>
<data name="SubmissionNoNewData" xml:space="preserve">
<value>Não há novos dados para enviar, tudo está atualizado.</value>
<value>Não há novos dados a serem enviados. Tudo está atualizado.</value>
</data>
<data name="SubmissionNoContributorSet" xml:space="preserve">
<value>Não foi possível enviar os dados porque não há um conjunto SteamID válido que possamos classificar como colaborador. Considere configurar a propriedade {0}.</value>
<value>Não foi possível enviar os dados porque não conseguimos identificar um ID Steam válido para definir como colaborador. Considere configurar a propriedade "{0}".</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>Enviando um total de apps/pacotes/pacotes registrados: {0}/{1}/{2}...</value>
<value>Enviando um total de {0} aplicativos, {1} pacotes e {2} depots registrados...</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>O envio falhou devido a muitas solicitações enviadas, tentaremos novamente em aproximadamente {0} a partir de agora.</value>
<value>O envio falhou porque muitas solicitações foram enviadas pelo cliente. Tentaremos novamente em aproximadamente {0}.</value>
<comment>{0} will be replaced by translated TimeSpan string (such as "53 minutes")</comment>
</data>
<data name="SubmissionSuccessful" xml:space="preserve">
<value>Os dados foram enviados com sucesso. O servidor registrou um total de novos aplicativos/pacotes/depósitos: {0} ({1} verificado)/{2} ({3} verificado)/{4} ({5} verificado).</value>
<value>Os dados foram enviados com sucesso. O servidor registrou um total de {0} aplicativos ({1} verificados), {2} pacotes ({3} verificados) e {4} depots ({5} verificados).</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">
@@ -157,24 +157,24 @@
<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>Novos depósitos: {0}</value>
<value>Novos depots: {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>Depósitos verificados: {0}</value>
<value>Depots verificados: {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} inicializado, o plugin não resolverá nenhum desses: {1}.</value>
<value>{0} inicializado, o plugin ignorará os seguintes pacotes: {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>Carregando cache STD global...</value>
<value>Carregando cache global do STD...</value>
</data>
<data name="ValidatingGlobalCacheIntegrity" xml:space="preserve">
<value>Validando integridade do cache STD global...</value>
<value>Validando integridade do cache global do STD...</value>
</data>
<data name="GlobalCacheIntegrityValidationFailed" xml:space="preserve">
<value>Falha ao verificar a integridade do cache STD global. Isso sugere uma potencial corrupção de arquivo/memória, uma instância nova será inicializada em vez disso.</value>
<value>Falha ao verificar a integridade do cache global do STD. Isto pode indicar uma possível corrupção de arquivo/memória, uma nova instância será inicializada.</value>
</data>
</root>

View File

@@ -62,34 +62,119 @@
PublicKeyToken=b77a5c561934e089
</value>
</resheader>
<data name="PluginDisabledMissingBuildToken" xml:space="preserve">
<value>由於 {0} 缺少組建權杖而被停用</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} 已根據您的設定被停用。如果您想幫助 SteamDB 提交資料,請查看我們的 Wiki。</value>
<comment>{0} will be replaced by the name of the plugin (e.g. "SteamTokenDumperPlugin")</comment>
</data>
<data name="PluginInitializedAndEnabled" xml:space="preserve">
<value>已成功初始化 {0},預先感謝您的幫助。第一次提交將大約在 {1} 後進行。</value>
<comment>{0} will be replaced by the name of the plugin (e.g. "SteamTokenDumperPlugin"), {1} will be replaced by translated TimeSpan string (such as "53 minutes")</comment>
</data>
<data name="FileCouldNotBeLoadedFreshInit" xml:space="preserve">
<value>無法載入 {0},將初始化一個新實例…</value>
<comment>{0} will be replaced by the name of the file (e.g. "GlobalCache")</comment>
</data>
<data name="BotNoAppsToRefresh" xml:space="preserve">
<value>此 Bot 實例中沒有需要再新的應用程式。</value>
</data>
<data name="BotRetrievingTotalAppAccessTokens" xml:space="preserve">
<value>正在檢索共 {0} 個應用程式存取權杖…</value>
<comment>{0} will be replaced by the number (total count) of app access tokens being retrieved</comment>
</data>
<data name="BotRetrievingAppAccessTokens" xml:space="preserve">
<value>正在檢索 {0} 個應用程式存取權杖…</value>
<comment>{0} will be replaced by the number (count this batch) of app access tokens being retrieved</comment>
</data>
<data name="BotFinishedRetrievingAppAccessTokens" xml:space="preserve">
<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>
<data name="BotRetrievingTotalDepots" xml:space="preserve">
<value>正在檢索共 {0} 個應用程式的 Depot…</value>
<comment>{0} will be replaced by the number (total count) of apps being retrieved</comment>
</data>
<data name="BotRetrievingAppInfos" xml:space="preserve">
<value>正在檢索 {0} 個應用程式資料…</value>
<comment>{0} will be replaced by the number (count this batch) of app infos being retrieved</comment>
</data>
<data name="BotFinishedRetrievingAppInfos" xml:space="preserve">
<value>已完成檢索 {0} 個應用程式資料。</value>
<comment>{0} will be replaced by the number (count this batch) of app infos retrieved</comment>
</data>
<data name="BotRetrievingDepotKeys" xml:space="preserve">
<value>正在檢索 {0} 個應用程式的 Depot 金鑰…</value>
<comment>{0} will be replaced by the number (count this batch) of depot keys being retrieved</comment>
</data>
<data name="BotFinishedRetrievingDepotKeys" xml:space="preserve">
<value>已完成檢索 {0} 個應用程式的 Depot。</value>
<comment>{0} will be replaced by the number (count this batch) of depot keys retrieved</comment>
</data>
<data name="BotFinishedRetrievingTotalDepots" xml:space="preserve">
<value>已完成檢索共 {0} 個應用程式的 Depot。</value>
<comment>{0} will be replaced by the number (total count) of apps retrieved</comment>
</data>
<data name="SubmissionNoNewData" xml:space="preserve">
<value>沒有要提交的新資料,一切都是最新的。</value>
</data>
<data name="SubmissionNoContributorSet" xml:space="preserve">
<value>無法提交資料,因為沒有可以讓我們歸類為貢獻者的有效 SteamID 集。 考慮設定 {0} 屬性。</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>正在提交註冊的應用程式/程式包/Depot 共:{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>由於發送的請求過多導致提交失敗,我們將在約 {0} 後重試。</value>
<comment>{0} will be replaced by translated TimeSpan string (such as "53 minutes")</comment>
</data>
<data name="SubmissionSuccessful" xml:space="preserve">
<value>已成功提交資料。伺服器共已註冊了新的應用程式/程式包/Depot 共:{0} ({1} 個已驗證)/{2} ({3} 個已驗證)/{4} ({5} 個已驗證)。</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>新的應用程式:{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>已驗證的應用程式:{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>新的程式包:{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>已驗證的程式包:{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>新的 Depot{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>已被驗證的 Depot{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} 已被初始化,外掛程式將無法解析其中任何一個:{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>正在載入 STD 全域快取…</value>
</data>
<data name="ValidatingGlobalCacheIntegrity" xml:space="preserve">
<value>正在驗證 STD 全域快取完整性…</value>
</data>
<data name="GlobalCacheIntegrityValidationFailed" xml:space="preserve">
<value>無法驗證 STD 全域快取完整性。這表示可能有檔案/記憶損壞,將初始化一個新實例。</value>
</data>
</root>

View File

@@ -104,7 +104,9 @@
<value>{0} невірний!</value>
<comment>{0} will be replaced by object's name</comment>
</data>
<data name="ErrorNoBotsDefined" xml:space="preserve">
<value>Не налаштовано жодного боту. Можливо ви забули налаштувати ASF? Скористуйтеся розділом 'налаштування' у нашій wiki, якщо не знаєте, що робити.</value>
</data>
<data name="ErrorObjectIsNull" xml:space="preserve">
<value>{0} має значення null!</value>
<comment>{0} will be replaced by object's name</comment>
@@ -192,7 +194,10 @@
<value>Будь ласка, введіть ваш код 2FA з додатку для автентифікації у Steam: </value>
<comment>Please note that this translation should end with space</comment>
</data>
<data name="UserInputSteamGuard" xml:space="preserve">
<value>Будь ласка, введіть код SteamGuard, який був надісланий вам на електронну пошту: </value>
<comment>Please note that this translation should end with space</comment>
</data>
<data name="UserInputSteamLogin" xml:space="preserve">
<value>Будь ласка, введіть свій Steam логін: </value>
<comment>Please note that this translation should end with space</comment>
@@ -222,36 +227,78 @@
<value>Не вдалося знайти бота з ім'ям {0}!</value>
<comment>{0} will be replaced by bot's name query (string)</comment>
</data>
<data name="BotStatusOverview" xml:space="preserve">
<value>Запущено {0}/{1} ботів, усього залишилось {2} ігор ({3} карток) які потрібно фармити.</value>
<comment>{0} will be replaced by number of active bots, {1} will be replaced by total number of bots, {2} will be replaced by total number of games left to farm, {3} will be replaced by total number of cards left to farm</comment>
</data>
<data name="BotStatusIdling" xml:space="preserve">
<value>Бот фармить гру: {0} ({1}, {2} карток залишилося обробити). Всього залишилося {3} ігор ({4} карток), (приблизно {5}) які треба фармити.</value>
<comment>{0} will be replaced by game's ID (number), {1} will be replaced by game's name, {2} will be replaced by number of cards left to farm, {3} will be replaced by total number of games to farm, {4} will be replaced by total number of cards to farm, {5} will be replaced by translated TimeSpan string (such as "1 day, 5 hours and 30 minutes")</comment>
</data>
<data name="BotStatusIdlingList" xml:space="preserve">
<value>Бот фармить ігри: {0} з {1} ігор ({2} картки) які треба фармити (залишилось приблизно {3}).</value>
<comment>{0} will be replaced by list of the games (IDs, numbers), {1} will be replaced by total number of games to farm, {2} will be replaced by total number of cards to farm, {3} will be replaced by translated TimeSpan string (such as "1 day, 5 hours and 30 minutes")</comment>
</data>
<data name="CheckingFirstBadgePage" xml:space="preserve">
<value>Перевірка першої сторінки зі значками...</value>
</data>
<data name="CheckingOtherBadgePages" xml:space="preserve">
<value>Перевірка інших сторінок зі значками...</value>
</data>
<data name="ChosenFarmingAlgorithm" xml:space="preserve">
<value>Обраний алгоритм фарму: {0}</value>
<comment>{0} will be replaced by the name of chosen farming algorithm</comment>
</data>
<data name="Done" xml:space="preserve">
<value>Виконано!</value>
</data>
<data name="GamesToIdle" xml:space="preserve">
<value>Всього залишилось {0} ігор ({1} карток), які потрібно фармити (приблизно {2})...</value>
<comment>{0} will be replaced by number of games, {1} will be replaced by number of cards, {2} will be replaced by translated TimeSpan string (such as "1 day, 5 hours and 30 minutes")</comment>
</data>
<data name="IdlingFinished" xml:space="preserve">
<value>Фарм закінчено!</value>
</data>
<data name="IdlingFinishedForGame" xml:space="preserve">
<value>Закінчено фарм: {0} ({1}) після {2} ігрового часу!</value>
<comment>{0} will be replaced by game's ID (number), {1} will be replaced by game's name, {2} will be replaced by translated TimeSpan string (such as "1 day, 5 hours and 30 minutes")</comment>
</data>
<data name="IdlingFinishedForGames" xml:space="preserve">
<value>Закінчено фарм ігор: {0}</value>
<comment>{0} will be replaced by list of the games (IDs, numbers), separated by a comma</comment>
</data>
<data name="IdlingStatusForGame" xml:space="preserve">
<value>Статус фарма для {0} ({1}): залишилося {2} карт</value>
<comment>{0} will be replaced by game's ID (number), {1} will be replaced by game's name, {2} will be replaced by number of cards left to farm</comment>
</data>
<data name="IdlingStopped" xml:space="preserve">
<value>Фарм зупинено!</value>
</data>
<data name="IgnoredPermanentPauseEnabled" xml:space="preserve">
<value>Ігнорування запиту, тому що ввімкнена постійна пауза!</value>
</data>
<data name="NothingToIdle" xml:space="preserve">
<value>На цьому акаунті нема чого фармити!</value>
</data>
<data name="NowIdling" xml:space="preserve">
<value>Зараз фармиться: {0} ({1})</value>
<comment>{0} will be replaced by game's ID (number), {1} will be replaced by game's name</comment>
</data>
<data name="NowIdlingList" xml:space="preserve">
<value>Зараз фармиться: {0}</value>
<comment>{0} will be replaced by list of the games (IDs, numbers), separated by a comma</comment>
</data>
<data name="PlayingNotAvailable" xml:space="preserve">
<value>Зараз запустити ігру неможливо, спробуємо пізніше!</value>
</data>
<data name="StillIdling" xml:space="preserve">
<value>Все ще фармиться: {0} ({1})</value>
<comment>{0} will be replaced by game's ID (number), {1} will be replaced by game's name</comment>
</data>
<data name="StillIdlingList" xml:space="preserve">
<value>Все ще фармиться: {0}</value>
<comment>{0} will be replaced by list of the games (IDs, numbers), separated by a comma</comment>
</data>
<data name="UnknownCommand" xml:space="preserve">

View File

@@ -104,7 +104,7 @@
<comment>{0} will be replaced by object's name</comment>
</data>
<data name="ErrorNoBotsDefined" xml:space="preserve">
<value>沒有設定任何機器人,您是否忘記設定 ASF 了?如果不明白發生了什麼,請閱讀 Wiki 上的「安裝指南」。</value>
<value>沒有設定任何 Bot。你是否忘記設定 ASF 了?如果不明白發生了什麼,請閱讀 Wiki 上的「設定指南」。</value>
</data>
<data name="ErrorObjectIsNull" xml:space="preserve">
<value>{0} 為空值!</value>
@@ -128,7 +128,7 @@
<value>無法進行更新,因為此版本沒有提供任何資產!</value>
</data>
<data name="ErrorUserInputRunningInHeadlessMode" xml:space="preserve">
<value>收到一個使用者輸入請求,但程目前正以無介面模式執行!</value>
<value>收到一個使用者輸入請求,但程目前正以無介面模式執行!</value>
</data>
<data name="Exiting" xml:space="preserve">
<value>正在退出...</value>
@@ -147,11 +147,11 @@
<comment>{0} will be replaced by trade number</comment>
</data>
<data name="LoggingIn" xml:space="preserve">
<value>正在登到 {0}...</value>
<value>正在登到 {0}</value>
<comment>{0} will be replaced by service's name</comment>
</data>
<data name="NoBotsAreRunning" xml:space="preserve">
<value>沒有執行中的 Bot正在退出…</value>
<value>沒有執行中的 Bot正在退出…</value>
</data>
<data name="RefreshingOurSession" xml:space="preserve">
<value>更新工作階段 </value>
@@ -161,22 +161,22 @@
<comment>{0} will be replaced by trade number</comment>
</data>
<data name="Restarting" xml:space="preserve">
<value>正在重新啟動...</value>
<value>正在重新啟動</value>
</data>
<data name="Starting" xml:space="preserve">
<value>正在啟動...</value>
<value>正在啟動</value>
</data>
<data name="Success" xml:space="preserve">
<value>成功!</value>
</data>
<data name="UnlockingParentalAccount" xml:space="preserve">
<value>正在解鎖家庭監護帳戶...</value>
<value>正在解鎖家庭監護帳號…</value>
</data>
<data name="UpdateCheckingNewVersion" xml:space="preserve">
<value>正在檢查新版本...</value>
<value>正在檢查新版本</value>
</data>
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
<value>正在下載新版本:{0}({1} MB)…等待期間,如果喜歡這個軟體,請考慮捐助 ASF:)</value>
<value>正在下載新版本:{0} ({1} MB)…等待期間,如果喜歡這個軟體,請考慮捐助 ASF:)</value>
<comment>{0} will be replaced by version string, {1} will be replaced by update size (in megabytes)</comment>
</data>
<data name="UpdateFinished" xml:space="preserve">
@@ -190,12 +190,15 @@
<comment>{0} will be replaced by current version, {1} will be replaced by remote version</comment>
</data>
<data name="UserInputSteam2FA" xml:space="preserve">
<value>請輸入你的 Steam Guard 行動驗證器上的兩步驟驗證代碼: </value>
<value>請輸入你的 Steam Guard 行動驗證器上的雙重驗證代碼: </value>
<comment>Please note that this translation should end with space</comment>
</data>
<data name="UserInputSteamGuard" xml:space="preserve">
<value>請輸入寄送至你的電子信箱的 Steam Guard 驗證代碼: </value>
<comment>Please note that this translation should end with space</comment>
</data>
<data name="UserInputSteamLogin" xml:space="preserve">
<value>請輸入你的 Steam 帳戶:</value>
<value>請輸入你的 Steam 帳號: </value>
<comment>Please note that this translation should end with space</comment>
</data>
<data name="UserInputSteamParentalCode" xml:space="preserve">
@@ -214,7 +217,7 @@
<value>IPC 伺服器已就緒!</value>
</data>
<data name="IPCStarting" xml:space="preserve">
<value>IPC 伺服器啟動中……</value>
<value>正在啟動 IPC 伺服器…</value>
</data>
<data name="BotAlreadyStopped" xml:space="preserve">
<value>這個 Bot 已經停止了!</value>
@@ -224,7 +227,7 @@
<comment>{0} will be replaced by bot's name query (string)</comment>
</data>
<data name="BotStatusOverview" xml:space="preserve">
<value>有 {0}/{1} 個 Bot 正在行,總共有 {2} 個遊戲 ({3} 張卡片) 等待掛卡。</value>
<value>有 {0}/{1} 個 Bot 正在行,總共有 {2} 個遊戲 ({3} 張卡片) 等待掛卡。</value>
<comment>{0} will be replaced by number of active bots, {1} will be replaced by total number of bots, {2} will be replaced by total number of games left to farm, {3} will be replaced by total number of cards left to farm</comment>
</data>
<data name="BotStatusIdling" xml:space="preserve">
@@ -236,10 +239,10 @@
<comment>{0} will be replaced by list of the games (IDs, numbers), {1} will be replaced by total number of games to farm, {2} will be replaced by total number of cards to farm, {3} will be replaced by translated TimeSpan string (such as "1 day, 5 hours and 30 minutes")</comment>
</data>
<data name="CheckingFirstBadgePage" xml:space="preserve">
<value>正在檢查徽章頁面第一頁...</value>
<value>正在檢查徽章頁面第一頁</value>
</data>
<data name="CheckingOtherBadgePages" xml:space="preserve">
<value>正在檢查其他徽章頁面...</value>
<value>正在檢查其他徽章頁面</value>
</data>
<data name="ChosenFarmingAlgorithm" xml:space="preserve">
<value>選擇的掛卡演算法為:{0}</value>
@@ -249,7 +252,7 @@
<value>完成!</value>
</data>
<data name="GamesToIdle" xml:space="preserve">
<value>總共有 {0} 個遊戲 (總數 {1} 張卡片) 需要掛卡 (仍需約 {2})...</value>
<value>總共有 {0} 個遊戲 ({1} 張卡片) 需要掛卡 (仍需約 {2})</value>
<comment>{0} will be replaced by number of games, {1} will be replaced by number of cards, {2} will be replaced by translated TimeSpan string (such as "1 day, 5 hours and 30 minutes")</comment>
</data>
<data name="IdlingFinished" xml:space="preserve">
@@ -274,33 +277,33 @@
<value>正在忽略此請求,因為強制暫停已啟用!</value>
</data>
<data name="NothingToIdle" xml:space="preserve">
<value>本帳目前沒有需要掛卡的遊戲!</value>
<value>本帳目前沒有需要掛卡的遊戲!</value>
</data>
<data name="NowIdling" xml:space="preserve">
<value>正在掛卡: {0} ({1})</value>
<value>正在掛卡{0} ({1})</value>
<comment>{0} will be replaced by game's ID (number), {1} will be replaced by game's name</comment>
</data>
<data name="NowIdlingList" xml:space="preserve">
<value>正在掛卡: {0}</value>
<value>正在掛卡{0}</value>
<comment>{0} will be replaced by list of the games (IDs, numbers), separated by a comma</comment>
</data>
<data name="PlayingNotAvailable" xml:space="preserve">
<value>目前無法執行,我們將稍後再試!</value>
</data>
<data name="StillIdling" xml:space="preserve">
<value>仍在掛卡: {0} ({1})</value>
<value>仍在掛卡{0} ({1})</value>
<comment>{0} will be replaced by game's ID (number), {1} will be replaced by game's name</comment>
</data>
<data name="StillIdlingList" xml:space="preserve">
<value>仍在掛卡: {0}</value>
<value>仍在掛卡{0}</value>
<comment>{0} will be replaced by list of the games (IDs, numbers), separated by a comma</comment>
</data>
<data name="StoppedIdling" xml:space="preserve">
<value>停止掛卡: {0} ({1})</value>
<value>停止掛卡{0} ({1})</value>
<comment>{0} will be replaced by game's ID (number), {1} will be replaced by game's name</comment>
</data>
<data name="StoppedIdlingList" xml:space="preserve">
<value>停止掛卡: {0}</value>
<value>停止掛卡{0}</value>
<comment>{0} will be replaced by list of the games (IDs, numbers), separated by a comma</comment>
</data>
<data name="UnknownCommand" xml:space="preserve">
@@ -314,11 +317,11 @@
<comment>{0} will be replaced by game's ID (number), {1} will be replaced by game's name</comment>
</data>
<data name="BotAcceptingGift" xml:space="preserve">
<value>接受禮物:{0}…</value>
<value>接受禮物:{0}…</value>
<comment>{0} will be replaced by giftID (number)</comment>
</data>
<data name="BotAccountLimited" xml:space="preserve">
<value>本帳為受限帳,在限制解除前將無法掛卡!</value>
<value>本帳為受限帳,在限制解除前將無法掛卡!</value>
</data>
<data name="BotAddLicense" xml:space="preserve">
<value>ID{0} | 狀態:{1}</value>
@@ -332,13 +335,13 @@
<value>Bot 已在執行中!</value>
</data>
<data name="BotAuthenticatorConverting" xml:space="preserve">
<value>正在將 .maFile 轉換成 ASF 的檔案格式…</value>
<value>正在將 .maFile 轉換成 ASF 的檔案格式…</value>
</data>
<data name="BotAuthenticatorImportFinished" xml:space="preserve">
<value>已成功匯入行動驗證器!</value>
</data>
<data name="BotAuthenticatorToken" xml:space="preserve">
<value>兩步驟驗證權杖:{0}</value>
<value>雙重驗證權杖:{0}</value>
<comment>{0} will be replaced by generated 2FA token (string)</comment>
</data>
<data name="BotAutomaticIdlingNowPaused" xml:space="preserve">
@@ -360,13 +363,13 @@
<value>已與 Steam 中斷連線!</value>
</data>
<data name="BotDisconnecting" xml:space="preserve">
<value>正在中斷連線...</value>
<value>正在中斷連線</value>
</data>
<data name="BotInstanceNotStartingBecauseDisabled" xml:space="preserve">
<value>這個 Bot 將不會啟動,因為它在設定檔中被停用!</value>
</data>
<data name="BotInvalidAuthenticatorDuringLogin" xml:space="preserve">
<value>已連續收到 {0} 次 TwoFactorCodeMismatch 錯誤訊息。你的兩步驟驗證憑證可能已失效,或者時間不同步,正在中止!</value>
<value>已連續收到 {0} 次 TwoFactorCodeMismatch 錯誤訊息。你的雙重驗證憑證可能已失效,或者時間不同步,正在中止!</value>
<comment>{0} will be replaced by maximum allowed number of failed 2FA attempts</comment>
</data>
<data name="BotLoggedOff" xml:space="preserve">
@@ -378,16 +381,16 @@
<comment>{0} will be replaced by steam ID (number)</comment>
</data>
<data name="BotLoggingIn" xml:space="preserve">
<value>登入中...</value>
<value>正在登入…</value>
</data>
<data name="BotLogonSessionReplaced" xml:space="preserve">
<value>這個帳似乎正被另一個 ASF 使用中,這是未定義的行為,拒絕讓它繼續執行!</value>
<value>這個帳似乎正被另一個 ASF 使用中,這是未定義的行為,拒絕讓它繼續執行!</value>
</data>
<data name="BotLootingFailed" xml:space="preserve">
<value>交易提案已失敗!</value>
</data>
<data name="BotLootingMasterNotDefined" xml:space="preserve">
<value>無法發送交易提案,因為沒有帳設有 master 權限!</value>
<value>無法發送交易提案,因為沒有帳設有 master 權限!</value>
</data>
<data name="BotLootingSuccess" xml:space="preserve">
<value>交易提案發送成功!</value>
@@ -396,7 +399,7 @@
<value>你無法對自己發出交易請求!</value>
</data>
<data name="BotNoASFAuthenticator" xml:space="preserve">
<value>這個 Bot 並未啟用 ASF 兩步驟驗證!您是否忘記將兩步驟驗證導入至 ASF</value>
<value>這個 Bot 並未啟用 ASF 雙重驗證!您是否忘記將雙重驗證導入至 ASF</value>
</data>
<data name="BotNotConnected" xml:space="preserve">
<value>此 Bot 尚未連線!</value>
@@ -414,11 +417,11 @@
<comment>{0} will be replaced by the points balance value (integer)</comment>
</data>
<data name="BotRateLimitExceeded" xml:space="preserve">
<value>超過頻率限制,我們將在 {0} 後重試…</value>
<value>超過頻率限制,我們將在 {0} 後重試…</value>
<comment>{0} will be replaced by translated TimeSpan string (such as "25 minutes")</comment>
</data>
<data name="BotReconnecting" xml:space="preserve">
<value>正在重新連線...</value>
<value>正在重新連線</value>
</data>
<data name="BotRedeem" xml:space="preserve">
<value>序號:{0} | 狀態:{1}</value>
@@ -469,13 +472,13 @@
<value>與 Steam 網路的連線中斷,正在重新連線……</value>
</data>
<data name="BotAccountFree" xml:space="preserve">
<value>帳不再被佔用,已恢復掛卡!</value>
<value>帳不再被佔用,已恢復掛卡!</value>
</data>
<data name="BotAccountOccupied" xml:space="preserve">
<value>帳號目前使用中ASF 將在該帳號空閒時繼續掛卡…</value>
<value>帳號目前使用中ASF 將在該帳號空閒時繼續掛卡…</value>
</data>
<data name="BotConnecting" xml:space="preserve">
<value>正在連線...</value>
<value>正在連線</value>
</data>
<data name="BotHeartBeatFailed" xml:space="preserve">
<value>無法與用戶端中斷連線,正在中止此 Bot</value>
@@ -484,7 +487,7 @@
<value>無法初始化 SteamDirectory與 Steam 網路的連線可能需要更長的時間!</value>
</data>
<data name="BotStopping" xml:space="preserve">
<value>正在停止...</value>
<value>正在停止</value>
</data>
<data name="ErrorBotConfigInvalid" xml:space="preserve">
<value>你的 Bot 設定無效,請確認 {0} 的內容然後再試一次!</value>
@@ -495,7 +498,7 @@
<comment>{0} will be replaced by file's path</comment>
</data>
<data name="Initializing" xml:space="preserve">
<value>正在初始化 {0}...</value>
<value>正在初始化 {0}</value>
<comment>{0} will be replaced by service name that is being initialized</comment>
</data>
<data name="WarningPrivacyPolicy" xml:space="preserve">
@@ -524,7 +527,7 @@
<comment>{0} will be replaced by program's name (e.g. "ASF"), {1} will be replaced by program's version (e.g. "1.0.0.0"). This string typically has nothing to translate and you should leave it as it is, unless you need to change the format, e.g. in RTL languages.</comment>
</data>
<data name="BotAccountLocked" xml:space="preserve">
<value>此帳已被封鎖,永久無法掛卡!</value>
<value>此帳已被封鎖,永久無法掛卡!</value>
</data>
<data name="BotStatusLocked" xml:space="preserve">
<value>Bot 已被封鎖,無法透過掛卡得到卡片。</value>
@@ -548,7 +551,7 @@
<comment>{0} will be replaced by number (in megabytes) of memory being used, {1} will be replaced by translated TimeSpan string (such as "25 minutes"). Please note that this string should include newlines for formatting.</comment>
</data>
<data name="ClearingDiscoveryQueue" xml:space="preserve">
<value>正在瀏覽 Steam 探索佇列 #{0}…</value>
<value>正在瀏覽 Steam 探索佇列 #{0}…</value>
<comment>{0} will be replaced by queue number</comment>
</data>
<data name="DoneClearingDiscoveryQueue" xml:space="preserve">
@@ -556,11 +559,11 @@
<comment>{0} will be replaced by queue number</comment>
</data>
<data name="BotOwnsOverviewPerGame" xml:space="preserve">
<value>{0}/{1} 個 Bot 已擁有遊戲 {2}。</value>
<value>{0}/{1} 個 Bot 已擁有遊戲 {2}。</value>
<comment>{0} will be replaced by number of bots that already own particular game being checked, {1} will be replaced by total number of bots that were checked during the process, {2} will be replaced by game's ID (number)</comment>
</data>
<data name="BotRefreshingPackagesData" xml:space="preserve">
<value>更新套件資料中…</value>
<value>更新套件資料中…</value>
</data>
<data name="WarningDeprecated" xml:space="preserve">
<value>{0} 的用法已棄用,並且將在未來的版本中移除,請改用 {1}。</value>
@@ -589,7 +592,7 @@
<comment>{0} will be replaced by bot's level</comment>
</data>
<data name="ActivelyMatchingItems" xml:space="preserve">
<value>正在比對 Steam 物品,第 #{0} 輪…</value>
<value>正在比對 Steam 物品,第 #{0} 輪…</value>
<comment>{0} will be replaced by round number</comment>
</data>
<data name="DoneActivelyMatchingItems" xml:space="preserve">
@@ -604,7 +607,7 @@
<comment>{0} will be replaced by number of sets traded</comment>
</data>
<data name="WarningExcessiveBotsCount" xml:space="preserve">
<value>你執行的個人 Bot 帳數量超過我們的建議上限 ({0})。請留意,此設定不受支援,且可能會導致各種 Steam 相關問題,包括帳停權。請參閱常見問答了解詳情。</value>
<value>你執行的個人 Bot 帳數量超過我們的建議上限 ({0})。請留意,此設定不受支援,且可能會導致各種 Steam 相關問題,包括帳停權。請參閱常見問答了解詳情。</value>
<comment>{0} will be replaced by our maximum recommended bots count (number)</comment>
</data>
<data name="PluginLoaded" xml:space="preserve">
@@ -612,7 +615,7 @@
<comment>{0} will be replaced by the name of the custom ASF plugin</comment>
</data>
<data name="PluginLoading" xml:space="preserve">
<value>正在載入 {0} V{1}…</value>
<value>正在載入 {0} V{1}…</value>
<comment>{0} will be replaced by the name of the custom ASF plugin, {1} will be replaced by its version</comment>
</data>
<data name="NothingFound" xml:space="preserve">
@@ -622,13 +625,13 @@
<value>你已載入一個以上的自訂外掛程式。由於我們無法支援修改過的配置,如遭遇任何問題,請向相關外掛程式的開發人員尋求協助。</value>
</data>
<data name="PleaseWait" xml:space="preserve">
<value>請稍候⋯⋯</value>
<value>請稍候</value>
</data>
<data name="EnterCommand" xml:space="preserve">
<value>輸入指令:</value>
</data>
<data name="Executing" xml:space="preserve">
<value>執行中……</value>
<value>正在執行…</value>
</data>
<data name="InteractiveConsoleEnabled" xml:space="preserve">
<value>已開啟互動式主控台按「C」進入指令模式</value>
@@ -645,14 +648,14 @@
<comment>{0} will be replaced by number of confirmations</comment>
</data>
<data name="BotExtraIdlingCooldown" xml:space="preserve">
<value>最多等待 {0} 以確保我們可以開始掛卡…</value>
<value>最多等待 {0} 以確保我們可以開始掛卡…</value>
<comment>{0} will be replaced by translated TimeSpan string (such as "1 minute")</comment>
</data>
<data name="UpdateCleanup" xml:space="preserve">
<value>正在清理更新後的過時檔案…</value>
<value>正在清理更新後的過時檔案…</value>
</data>
<data name="BotGeneratingSteamParentalCode" xml:space="preserve">
<value>正在產生 Steam 家庭監護代碼,這會需要一段時間,請考慮將它寫入設定檔中…</value>
<value>正在產生 Steam 家庭監護代碼,這會需要一段時間,請考慮將它寫入設定檔中…</value>
</data>
<data name="IPCConfigChanged" xml:space="preserve">
<value>IPC 設定檔已變更!</value>
@@ -662,7 +665,7 @@
<comment>{0} will be replaced by trade offer ID (number), {1} will be replaced by internal ASF enum name, {2} will be replaced by technical reason why the trade was determined to be in this state</comment>
</data>
<data name="BotInvalidPasswordDuringLogin" xml:space="preserve">
<value>連續收到 InvalidPassword 錯誤代碼 {0} 次。您的帳密碼大概是錯的,中止!</value>
<value>連續收到 InvalidPassword 錯誤代碼 {0} 次。您的帳密碼大概是錯的,中止!</value>
<comment>{0} will be replaced by maximum allowed number of failed login attempts</comment>
</data>
<data name="Result" xml:space="preserve">
@@ -684,40 +687,52 @@
<comment>{0} will be replaced by internal name of the config property (e.g. "GamesPlayedWhileIdle"), {1} will be replaced by comma-separated list of appIDs that user has chosen</comment>
</data>
<data name="AutomaticFileMigration" xml:space="preserve">
<value>{0} 設定檔將會改版成最新的語法…</value>
<value>{0} 設定檔將會改版成最新的語法…</value>
<comment>{0} will be replaced with the relative path to the affected config file</comment>
</data>
<data name="WarningWeakIPCPassword" xml:space="preserve">
<value>你的IPC密碼看起來很弱 你可以考慮換一個安全性更高的密碼 細節:{0}</value>
<value>你的 IPC 密碼看起來很弱。請考慮換一個強度更高的密碼來增加安全性。詳細資訊: {0}</value>
<comment>{0} will be replaced by additional details about the password being considered weak</comment>
</data>
<data name="WarningWeakSteamPassword" xml:space="preserve">
<value>你的steam 密碼 {0} 看起來很弱 你可以考慮換一個 細節: {1}</value>
<value>你的 Steam 密碼{0}看起來很弱。請考慮換一個強度更高的密碼來增加安全性。詳細資訊: {1}</value>
<comment>{0} will be replaced by either the affected bot name or the path to the bots configuration file, {1} will be replaced by additional details about the password being considered weak</comment>
</data>
<data name="WarningWeakCryptKey" xml:space="preserve">
<value>你的加密鑰看起來很弱 可以考慮換一個 細節:{0}</value>
<value>你的加密鑰看起來很弱。請考慮換一個強度更高的金鑰來增加安全性。詳細資訊: {0}</value>
<comment>{0} will be replaced by additional details about the encryption key being considered weak</comment>
</data>
<data name="WarningTooShortCryptKey" xml:space="preserve">
<value>你的加密鑰太短 我們推薦使用{0} 以上長度的密鑰</value>
<value>你的加密鑰太短我們推薦使用 {0} 位元組 (字元) 以上長度的金鑰。</value>
<comment>{0} will be replaced by the number of bytes (characters) recommended</comment>
</data>
<data name="WarningDefaultCryptKeyUsedForHashing" xml:space="preserve">
<value>你正在使用 {0} 設 {1} 屬性 但你沒有提供一個自定義的加密密鑰 你應該提供一個自定義的加密密鑰以提高安全性</value>
<value>你正在使用 {0} 設 {1} 屬性但你沒有提供一個自定義的 --cryptkey。你應該提供自定義 --cryptkey 以提高安全性</value>
<comment>{0} will be replaced by the name of a particular setting (e.g. "SCrypt"), {1} will be replaced by the name of the property (e.g. "IPCPassword")</comment>
</data>
<data name="WarningDefaultCryptKeyUsedForEncryption" xml:space="preserve">
<value>你正在使用 {0} 設 {1} 屬性 但你沒有提供一個自定義的加密密鑰 這很不安全 你應該提供一個自定義的加密密鑰</value>
<value>你正在使用 {0} 設 {1} 屬性但你沒有提供一個自定義的 --cryptkey。這完全破壞了保護因為 ASF 被迫使用自己的 (已知) 金鑰。你應該提供自定義 --cryptkey 用於使用此設定提供的安全優勢。</value>
<comment>{0} will be replaced by the name of a particular setting (e.g. "AES"), {1} will be replaced by the name of the property (e.g. "SteamPassword")</comment>
</data>
<data name="WarningRunningInUnsupportedEnvironment" xml:space="preserve">
<value>你在不支援的環境下提供了 --ignore-unsupported-environment 參數 請注意 我們不為這種情況提供任何支持 風險請完全由你自行承擔 我們已經警告過你了</value>
<data name="WarningRunningAsRoot" xml:space="preserve">
<value>你正在以管理員權限 (Root) 執行 ASF。這會給你的機器帶來重大的安全風險且由於 ASF 的操作不需要 Root 權限,我們建議盡可能以非管理員使用者身份執行它。</value>
</data>
<data name="WarningRunningInUnsupportedEnvironment" xml:space="preserve">
<value>您在不受支援的環境中執行 ASF並提供 --ignore-unsupported-environment 引數。請注意,我們不對這種情況提供任何形式的支援,您完全需要自行承擔風險。你已經被警告過了。</value>
</data>
<data name="FetchingChecksumFromRemoteServer" xml:space="preserve">
<value>正在從遠端伺服器擷取核對和…</value>
</data>
<data name="VerifyingChecksumWithRemoteServer" xml:space="preserve">
<value>正在驗證已下載的二進制檔案與來自遠端伺服器的核對和…</value>
</data>
<data name="ChecksumMissing" xml:space="preserve">
<value>遠端伺服器對我們要更新到的版本一無所知。如果該版本是最近發布的,則可能出現這種情況——立刻拒絕進行更新程序作為額外的安全措施。</value>
</data>
<data name="ChecksumWrong" xml:space="preserve">
<value>遠端伺服器回覆了不同的核對和,這可能意味著下載檔案損毀或遭受中間人攻擊,拒絕繼續更新程序!</value>
</data>
<data name="PatchingFiles" xml:space="preserve">
<value>正在修補 ASF 檔案…</value>
</data>
</root>

View File

@@ -1,42 +0,0 @@
// _ _ _ ____ _ _____
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2022 Ł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.Threading.Tasks;
using ArchiSteamFarm.Steam;
using ArchiSteamFarm.Storage;
using JetBrains.Annotations;
namespace ArchiSteamFarm.Plugins.Interfaces;
[PublicAPI]
[Obsolete($"Use {nameof(IBotCommand2)} instead, this one will be removed soon.", true)]
public interface IBotCommand : IPlugin {
/// <summary>
/// ASF will call this method for unrecognized commands.
/// </summary>
/// <param name="bot">Bot object related to this callback.</param>
/// <param name="steamID">64-bit long unsigned integer of steamID executing the command.</param>
/// <param name="message">Command message in its raw format, stripped of <see cref="GlobalConfig.CommandPrefix" />.</param>
/// <param name="args">Pre-parsed message using standard ASF delimiters.</param>
/// <returns>Response to the command, or null/empty (as the task value) if the command isn't handled by this plugin.</returns>
Task<string?> OnBotCommand(Bot bot, ulong steamID, string message, string[] args);
}

View File

@@ -40,7 +40,6 @@ using ArchiSteamFarm.Steam;
using ArchiSteamFarm.Steam.Data;
using ArchiSteamFarm.Steam.Exchange;
using ArchiSteamFarm.Steam.Integration.Callbacks;
using ArchiSteamFarm.Storage;
using Newtonsoft.Json.Linq;
using SteamKit2;
@@ -297,36 +296,6 @@ internal static class PluginsCore {
return null;
}
ulong oldSteamID = steamID;
if (oldSteamID == 0) {
oldSteamID = ASF.GlobalConfig?.SteamOwnerID ?? GlobalConfig.DefaultSteamOwnerID;
}
if ((oldSteamID != 0) && new SteamID(oldSteamID).IsIndividualAccount) {
IList<string?> oldResponses;
try {
#pragma warning disable CS0618 // We intentionally support deprecated interface for a while longer
oldResponses = await Utilities.InParallel(ActivePlugins.OfType<IBotCommand>().Select(plugin => plugin.OnBotCommand(bot, oldSteamID, message, args))).ConfigureAwait(false);
#pragma warning restore CS0618 // We intentionally support deprecated interface for a while longer
} catch (Exception e) {
ASF.ArchiLogger.LogGenericException(e);
return null;
}
if (oldResponses.Count > 0) {
// Due to fact that responses is string[] array, not a List, we need to reinitialize it
// Normally I'd wrote it differently but this is temporary code to be removed soon, so this will suffice
responses = new List<string?>(responses);
foreach (string? oldResponse in oldResponses) {
responses.Add(oldResponse);
}
}
}
return string.Join(Environment.NewLine, responses.Where(static response => !string.IsNullOrEmpty(response)));
}

View File

@@ -753,27 +753,6 @@ public sealed class Bot : IAsyncDisposable {
return await ArchiWebHandler.GetTradeHoldDurationForTrade(tradeID).ConfigureAwait(false);
}
[PublicAPI]
[Obsolete($"Use {nameof(GetAccess)} instead (if you still need it), this one will be removed soon.", true)]
public bool HasAccess(ulong steamID, BotConfig.EAccess access) {
if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) {
throw new ArgumentOutOfRangeException(nameof(steamID));
}
if ((access == BotConfig.EAccess.None) || !Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(BotConfig.EAccess));
}
if (ASF.IsOwner(steamID)) {
return true;
}
return access switch {
BotConfig.EAccess.FamilySharing when SteamFamilySharingIDs.Contains(steamID) => true,
_ => BotConfig.SteamUserPermissions.TryGetValue(steamID, out BotConfig.EAccess realPermission) && (realPermission >= access)
};
}
[PublicAPI]
public async Task<Dictionary<uint, byte>?> LoadCardsPerSet(IReadOnlyCollection<uint> appIDs) {
if ((appIDs == null) || (appIDs.Count == 0)) {
@@ -1662,6 +1641,18 @@ public sealed class Bot : IAsyncDisposable {
SteamFriends.RequestFriendInfo(SteamID, EClientPersonaStateFlag.PlayerName | EClientPersonaStateFlag.Presence);
}
internal void ResetPersonaState() {
if (BotConfig.OnlineStatus == EPersonaState.Offline) {
return;
}
SteamFriends.SetPersonaState(BotConfig.OnlineStatus);
if (BotConfig.OnlineFlags > 0) {
ArchiHandler.SetPersonaState(BotConfig.OnlineStatus, BotConfig.OnlineFlags);
}
}
internal async Task<bool> SendTypingMessage(ulong steamID) {
if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) {
throw new ArgumentOutOfRangeException(nameof(steamID));
@@ -2876,9 +2867,7 @@ public sealed class Bot : IAsyncDisposable {
Utilities.InBackground(RemoteCommunication.OnLoggedOn);
}
if (BotConfig.OnlineStatus != EPersonaState.Offline) {
SteamFriends.SetPersonaState(BotConfig.OnlineStatus);
}
ResetPersonaState();
if (BotConfig.SteamMasterClanID != 0) {
Utilities.InBackground(
@@ -3294,7 +3283,7 @@ public sealed class Bot : IAsyncDisposable {
}
private async Task ResetGamesPlayed() {
if (CardsFarmer.NowFarming) {
if (!IsConnectedAndLoggedOn || CardsFarmer.NowFarming) {
return;
}
@@ -3306,10 +3295,24 @@ public sealed class Bot : IAsyncDisposable {
// This function might be executed before PlayingSessionStateCallback/SharedLibraryLockStatusCallback, ensure proper delay in this case
await Task.Delay(2000).ConfigureAwait(false);
if (CardsFarmer.NowFarming || !IsPlayingPossible) {
if (!IsConnectedAndLoggedOn || CardsFarmer.NowFarming || !IsPlayingPossible) {
return;
}
if (PlayingWasBlocked) {
byte minFarmingDelayAfterBlock = ASF.GlobalConfig?.MinFarmingDelayAfterBlock ?? GlobalConfig.DefaultMinFarmingDelayAfterBlock;
if (minFarmingDelayAfterBlock > 0) {
for (byte i = 0; (i < minFarmingDelayAfterBlock) && IsConnectedAndLoggedOn && !CardsFarmer.NowFarming && IsPlayingPossible && PlayingWasBlocked; i++) {
await Task.Delay(1000).ConfigureAwait(false);
}
if (!IsConnectedAndLoggedOn || CardsFarmer.NowFarming || !IsPlayingPossible) {
return;
}
}
}
ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.BotIdlingSelectedGames, nameof(BotConfig.GamesPlayedWhileIdle), string.Join(", ", BotConfig.GamesPlayedWhileIdle)));
}

View File

@@ -31,6 +31,7 @@ using ArchiSteamFarm.Steam.Integration.CMsgs;
using JetBrains.Annotations;
using SteamKit2;
using SteamKit2.Internal;
using EPersonaStateFlag = SteamKit2.EPersonaStateFlag;
namespace ArchiSteamFarm.Steam.Integration;
@@ -756,6 +757,33 @@ public sealed class ArchiHandler : ClientMsgHandler {
Client.Send(request);
}
internal void SetPersonaState(EPersonaState state, EPersonaStateFlag flags) {
if (!Enum.IsDefined(state)) {
throw new InvalidEnumArgumentException(nameof(state), (int) state, typeof(EPersonaState));
}
if (flags < 0) {
throw new InvalidEnumArgumentException(nameof(flags), (int) flags, typeof(EPersonaStateFlag));
}
if (Client == null) {
throw new InvalidOperationException(nameof(Client));
}
if (!Client.IsConnected) {
return;
}
ClientMsgProtobuf<CMsgClientChangeStatus> request = new(EMsg.ClientChangeStatus) {
Body = {
persona_state = (uint) state,
persona_state_flags = (uint) flags
}
};
Client.Send(request);
}
[PublicAPI]
public enum EUserInterfaceMode : byte {
Default = 0,

View File

@@ -81,18 +81,6 @@ public sealed class Commands {
return $"<{SharedInfo.ASF}> {response}";
}
[PublicAPI]
[Obsolete($"Use overload which accepts {nameof(EAccess)} instead, this one will be removed soon.", true)]
public async Task<string?> Response(ulong steamID, string message) {
if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) {
throw new ArgumentOutOfRangeException(nameof(steamID));
}
EAccess access = Bot.GetAccess(steamID);
return await Response(access, message, steamID).ConfigureAwait(false);
}
[PublicAPI]
public async Task<string?> Response(EAccess access, string message, ulong steamID = 0) {
if (!Enum.IsDefined(access)) {
@@ -149,7 +137,7 @@ public sealed class Commands {
case "RESTART":
return ResponseRestart(access);
case "SA":
return await ResponseStatus(access, SharedInfo.ASF).ConfigureAwait(false);
return await ResponseStatus(access, SharedInfo.ASF, steamID).ConfigureAwait(false);
case "START":
return ResponseStart(access);
case "STATS":
@@ -174,106 +162,106 @@ public sealed class Commands {
default:
switch (args[0].ToUpperInvariant()) {
case "2FA":
return await Response2FA(access, Utilities.GetArgsAsText(args, 1, ",")).ConfigureAwait(false);
return await Response2FA(access, Utilities.GetArgsAsText(args, 1, ","), steamID).ConfigureAwait(false);
case "2FANO":
return await Response2FAConfirm(access, Utilities.GetArgsAsText(args, 1, ","), false).ConfigureAwait(false);
return await Response2FAConfirm(access, Utilities.GetArgsAsText(args, 1, ","), false, steamID).ConfigureAwait(false);
case "2FAOK":
return await Response2FAConfirm(access, Utilities.GetArgsAsText(args, 1, ","), true).ConfigureAwait(false);
return await Response2FAConfirm(access, Utilities.GetArgsAsText(args, 1, ","), true, steamID).ConfigureAwait(false);
case "ADDLICENSE" when args.Length > 2:
return await ResponseAddLicense(access, args[1], Utilities.GetArgsAsText(args, 2, ",")).ConfigureAwait(false);
return await ResponseAddLicense(access, args[1], Utilities.GetArgsAsText(args, 2, ","), steamID).ConfigureAwait(false);
case "ADDLICENSE":
return await ResponseAddLicense(access, args[1]).ConfigureAwait(false);
case "BALANCE":
return await ResponseWalletBalance(access, Utilities.GetArgsAsText(args, 1, ",")).ConfigureAwait(false);
return await ResponseWalletBalance(access, Utilities.GetArgsAsText(args, 1, ","), steamID).ConfigureAwait(false);
case "BGR":
return await ResponseBackgroundGamesRedeemer(access, Utilities.GetArgsAsText(args, 1, ",")).ConfigureAwait(false);
return await ResponseBackgroundGamesRedeemer(access, Utilities.GetArgsAsText(args, 1, ","), steamID).ConfigureAwait(false);
case "ENCRYPT" when args.Length > 2:
return ResponseEncrypt(access, args[1], Utilities.GetArgsAsText(message, 2));
case "FARM":
return await ResponseFarm(access, Utilities.GetArgsAsText(args, 1, ",")).ConfigureAwait(false);
return await ResponseFarm(access, Utilities.GetArgsAsText(args, 1, ","), steamID).ConfigureAwait(false);
case "FB":
return await ResponseFarmingBlacklist(access, Utilities.GetArgsAsText(args, 1, ",")).ConfigureAwait(false);
return await ResponseFarmingBlacklist(access, Utilities.GetArgsAsText(args, 1, ","), steamID).ConfigureAwait(false);
case "FBADD" when args.Length > 2:
return await ResponseFarmingBlacklistAdd(access, args[1], Utilities.GetArgsAsText(args, 2, ",")).ConfigureAwait(false);
return await ResponseFarmingBlacklistAdd(access, args[1], Utilities.GetArgsAsText(args, 2, ","), steamID).ConfigureAwait(false);
case "FBADD":
return ResponseFarmingBlacklistAdd(access, args[1]);
case "FBRM" when args.Length > 2:
return await ResponseFarmingBlacklistRemove(access, args[1], Utilities.GetArgsAsText(args, 2, ",")).ConfigureAwait(false);
return await ResponseFarmingBlacklistRemove(access, args[1], Utilities.GetArgsAsText(args, 2, ","), steamID).ConfigureAwait(false);
case "FBRM":
return ResponseFarmingBlacklistRemove(access, args[1]);
case "FQ":
return await ResponseFarmingQueue(access, Utilities.GetArgsAsText(args, 1, ",")).ConfigureAwait(false);
return await ResponseFarmingQueue(access, Utilities.GetArgsAsText(args, 1, ","), steamID).ConfigureAwait(false);
case "FQADD" when args.Length > 2:
return await ResponseFarmingQueueAdd(access, args[1], Utilities.GetArgsAsText(args, 2, ",")).ConfigureAwait(false);
return await ResponseFarmingQueueAdd(access, args[1], Utilities.GetArgsAsText(args, 2, ","), steamID).ConfigureAwait(false);
case "FQADD":
return ResponseFarmingQueueAdd(access, args[1]);
case "FQRM" when args.Length > 2:
return await ResponseFarmingQueueRemove(access, args[1], Utilities.GetArgsAsText(args, 2, ",")).ConfigureAwait(false);
return await ResponseFarmingQueueRemove(access, args[1], Utilities.GetArgsAsText(args, 2, ","), steamID).ConfigureAwait(false);
case "FQRM":
return ResponseFarmingQueueRemove(access, args[1]);
case "HASH" when args.Length > 2:
return ResponseHash(access, args[1], Utilities.GetArgsAsText(message, 2));
case "INPUT" when args.Length > 3:
return await ResponseInput(access, args[1], args[2], Utilities.GetArgsAsText(message, 3)).ConfigureAwait(false);
return await ResponseInput(access, args[1], args[2], Utilities.GetArgsAsText(message, 3), steamID).ConfigureAwait(false);
case "INPUT" when args.Length > 2:
return ResponseInput(access, args[1], args[2]);
case "LEVEL":
return await ResponseLevel(access, Utilities.GetArgsAsText(args, 1, ",")).ConfigureAwait(false);
return await ResponseLevel(access, Utilities.GetArgsAsText(args, 1, ","), steamID).ConfigureAwait(false);
case "LOOT":
return await ResponseLoot(access, Utilities.GetArgsAsText(args, 1, ",")).ConfigureAwait(false);
return await ResponseLoot(access, Utilities.GetArgsAsText(args, 1, ","), steamID).ConfigureAwait(false);
case "LOOT^" when args.Length > 3:
return await ResponseAdvancedLoot(access, args[1], args[2], Utilities.GetArgsAsText(message, 3)).ConfigureAwait(false);
return await ResponseAdvancedLoot(access, args[1], args[2], Utilities.GetArgsAsText(message, 3), steamID).ConfigureAwait(false);
case "LOOT^" when args.Length > 2:
return await ResponseAdvancedLoot(access, args[1], args[2]).ConfigureAwait(false);
case "LOOT@" when args.Length > 2:
return await ResponseLootByRealAppIDs(access, args[1], Utilities.GetArgsAsText(args, 2, ",")).ConfigureAwait(false);
return await ResponseLootByRealAppIDs(access, args[1], Utilities.GetArgsAsText(args, 2, ","), false, steamID).ConfigureAwait(false);
case "LOOT@":
return await ResponseLootByRealAppIDs(access, args[1]).ConfigureAwait(false);
return await ResponseLootByRealAppIDs(access, args[1], false).ConfigureAwait(false);
case "LOOT%" when args.Length > 2:
return await ResponseLootByRealAppIDs(access, args[1], Utilities.GetArgsAsText(args, 2, ","), true).ConfigureAwait(false);
return await ResponseLootByRealAppIDs(access, args[1], Utilities.GetArgsAsText(args, 2, ","), true, steamID).ConfigureAwait(false);
case "LOOT%":
return await ResponseLootByRealAppIDs(access, args[1], true).ConfigureAwait(false);
case "MAB":
return await ResponseMatchActivelyBlacklist(access, Utilities.GetArgsAsText(args, 1, ",")).ConfigureAwait(false);
return await ResponseMatchActivelyBlacklist(access, Utilities.GetArgsAsText(args, 1, ","), steamID).ConfigureAwait(false);
case "MABADD" when args.Length > 2:
return await ResponseMatchActivelyBlacklistAdd(access, args[1], Utilities.GetArgsAsText(args, 2, ",")).ConfigureAwait(false);
return await ResponseMatchActivelyBlacklistAdd(access, args[1], Utilities.GetArgsAsText(args, 2, ","), steamID).ConfigureAwait(false);
case "MABADD":
return ResponseMatchActivelyBlacklistAdd(access, args[1]);
case "MABRM" when args.Length > 2:
return await ResponseMatchActivelyBlacklistRemove(access, args[1], Utilities.GetArgsAsText(args, 2, ",")).ConfigureAwait(false);
return await ResponseMatchActivelyBlacklistRemove(access, args[1], Utilities.GetArgsAsText(args, 2, ","), steamID).ConfigureAwait(false);
case "MABRM":
return ResponseMatchActivelyBlacklistRemove(access, args[1]);
case "NICKNAME" when args.Length > 2:
return await ResponseNickname(access, args[1], Utilities.GetArgsAsText(message, 2)).ConfigureAwait(false);
return await ResponseNickname(access, args[1], Utilities.GetArgsAsText(message, 2), steamID).ConfigureAwait(false);
case "NICKNAME":
return ResponseNickname(access, args[1]);
case "OA":
return await ResponseOwns(access, SharedInfo.ASF, Utilities.GetArgsAsText(message, 1)).ConfigureAwait(false);
return await ResponseOwns(access, SharedInfo.ASF, Utilities.GetArgsAsText(message, 1), steamID).ConfigureAwait(false);
case "OWNS" when args.Length > 2:
return await ResponseOwns(access, args[1], Utilities.GetArgsAsText(message, 2)).ConfigureAwait(false);
return await ResponseOwns(access, args[1], Utilities.GetArgsAsText(message, 2), steamID).ConfigureAwait(false);
case "OWNS":
return (await ResponseOwns(access, args[1]).ConfigureAwait(false)).Response;
case "PAUSE":
return await ResponsePause(access, Utilities.GetArgsAsText(args, 1, ","), true).ConfigureAwait(false);
return await ResponsePause(access, Utilities.GetArgsAsText(args, 1, ","), true, steamID: steamID).ConfigureAwait(false);
case "PAUSE~":
return await ResponsePause(access, Utilities.GetArgsAsText(args, 1, ","), false).ConfigureAwait(false);
return await ResponsePause(access, Utilities.GetArgsAsText(args, 1, ","), false, steamID: steamID).ConfigureAwait(false);
case "PAUSE&" when args.Length > 2:
return await ResponsePause(access, args[1], true, Utilities.GetArgsAsText(message, 2)).ConfigureAwait(false);
return await ResponsePause(access, args[1], true, Utilities.GetArgsAsText(message, 2), steamID).ConfigureAwait(false);
case "PAUSE&":
return await ResponsePause(access, true, args[1]).ConfigureAwait(false);
case "PLAY" when args.Length > 2:
return await ResponsePlay(access, args[1], Utilities.GetArgsAsText(message, 2)).ConfigureAwait(false);
return await ResponsePlay(access, args[1], Utilities.GetArgsAsText(message, 2), steamID).ConfigureAwait(false);
case "PLAY":
return await ResponsePlay(access, args[1]).ConfigureAwait(false);
case "POINTS":
return await ResponsePointsBalance(access, Utilities.GetArgsAsText(args, 1, ",")).ConfigureAwait(false);
return await ResponsePointsBalance(access, Utilities.GetArgsAsText(args, 1, ","), steamID).ConfigureAwait(false);
case "PRIVACY" when args.Length > 2:
return await ResponsePrivacy(access, args[1], Utilities.GetArgsAsText(args, 2, ",")).ConfigureAwait(false);
return await ResponsePrivacy(access, args[1], Utilities.GetArgsAsText(args, 2, ","), steamID).ConfigureAwait(false);
case "PRIVACY":
return await ResponsePrivacy(access, args[1]).ConfigureAwait(false);
case "R" when args.Length > 2:
case "REDEEM" when args.Length > 2:
return await ResponseRedeem(access, args[1], Utilities.GetArgsAsText(args, 2, ","), steamID).ConfigureAwait(false);
return await ResponseRedeem(access, args[1], Utilities.GetArgsAsText(args, 2, ","), steamID: steamID).ConfigureAwait(false);
case "R":
case "REDEEM":
return await ResponseRedeem(access, args[1], steamID).ConfigureAwait(false);
@@ -284,43 +272,43 @@ public sealed class Commands {
case "REDEEM^" when args.Length > 2:
return await ResponseAdvancedRedeem(access, args[1], args[2], steamID).ConfigureAwait(false);
case "RESET":
return await ResponseReset(access, Utilities.GetArgsAsText(args, 1, ",")).ConfigureAwait(false);
return await ResponseReset(access, Utilities.GetArgsAsText(args, 1, ","), steamID).ConfigureAwait(false);
case "RESUME":
return await ResponseResume(access, Utilities.GetArgsAsText(args, 1, ",")).ConfigureAwait(false);
return await ResponseResume(access, Utilities.GetArgsAsText(args, 1, ","), steamID).ConfigureAwait(false);
case "START":
return await ResponseStart(access, Utilities.GetArgsAsText(args, 1, ",")).ConfigureAwait(false);
return await ResponseStart(access, Utilities.GetArgsAsText(args, 1, ","), steamID).ConfigureAwait(false);
case "STATUS":
return await ResponseStatus(access, Utilities.GetArgsAsText(args, 1, ",")).ConfigureAwait(false);
return await ResponseStatus(access, Utilities.GetArgsAsText(args, 1, ","), steamID).ConfigureAwait(false);
case "STOP":
return await ResponseStop(access, Utilities.GetArgsAsText(args, 1, ",")).ConfigureAwait(false);
return await ResponseStop(access, Utilities.GetArgsAsText(args, 1, ","), steamID).ConfigureAwait(false);
case "TB":
return await ResponseTradingBlacklist(access, Utilities.GetArgsAsText(args, 1, ",")).ConfigureAwait(false);
return await ResponseTradingBlacklist(access, Utilities.GetArgsAsText(args, 1, ","), steamID).ConfigureAwait(false);
case "TBADD" when args.Length > 2:
return await ResponseTradingBlacklistAdd(access, args[1], Utilities.GetArgsAsText(args, 2, ",")).ConfigureAwait(false);
return await ResponseTradingBlacklistAdd(access, args[1], Utilities.GetArgsAsText(args, 2, ","), steamID).ConfigureAwait(false);
case "TBADD":
return ResponseTradingBlacklistAdd(access, args[1]);
case "TBRM" when args.Length > 2:
return await ResponseTradingBlacklistRemove(access, args[1], Utilities.GetArgsAsText(args, 2, ",")).ConfigureAwait(false);
return await ResponseTradingBlacklistRemove(access, args[1], Utilities.GetArgsAsText(args, 2, ","), steamID).ConfigureAwait(false);
case "TBRM":
return ResponseTradingBlacklistRemove(access, args[1]);
case "TRANSFER" when args.Length > 2:
return await ResponseTransfer(access, args[1], Utilities.GetArgsAsText(message, 2)).ConfigureAwait(false);
return await ResponseTransfer(access, args[1], Utilities.GetArgsAsText(message, 2), steamID).ConfigureAwait(false);
case "TRANSFER":
return await ResponseTransfer(access, args[1]).ConfigureAwait(false);
case "TRANSFER^" when args.Length > 4:
return await ResponseAdvancedTransfer(access, args[1], args[2], args[3], Utilities.GetArgsAsText(message, 4)).ConfigureAwait(false);
return await ResponseAdvancedTransfer(access, args[1], args[2], args[3], Utilities.GetArgsAsText(message, 4), steamID).ConfigureAwait(false);
case "TRANSFER^" when args.Length > 3:
return await ResponseAdvancedTransfer(access, args[1], args[2], args[3]).ConfigureAwait(false);
case "TRANSFER@" when args.Length > 3:
return await ResponseTransferByRealAppIDs(access, args[1], args[2], Utilities.GetArgsAsText(message, 3)).ConfigureAwait(false);
return await ResponseTransferByRealAppIDs(access, args[1], args[2], Utilities.GetArgsAsText(message, 3), false, steamID).ConfigureAwait(false);
case "TRANSFER@" when args.Length > 2:
return await ResponseTransferByRealAppIDs(access, args[1], args[2]).ConfigureAwait(false);
return await ResponseTransferByRealAppIDs(access, args[1], args[2], false).ConfigureAwait(false);
case "TRANSFER%" when args.Length > 3:
return await ResponseTransferByRealAppIDs(access, args[1], args[2], Utilities.GetArgsAsText(message, 3), true).ConfigureAwait(false);
return await ResponseTransferByRealAppIDs(access, args[1], args[2], Utilities.GetArgsAsText(message, 3), true, steamID).ConfigureAwait(false);
case "TRANSFER%" when args.Length > 2:
return await ResponseTransferByRealAppIDs(access, args[1], args[2], true).ConfigureAwait(false);
case "UNPACK":
return await ResponseUnpackBoosters(access, Utilities.GetArgsAsText(args, 1, ",")).ConfigureAwait(false);
return await ResponseUnpackBoosters(access, Utilities.GetArgsAsText(args, 1, ","), steamID).ConfigureAwait(false);
default:
string? pluginsResponse = await PluginsCore.OnBotCommand(Bot, access, message, args, steamID).ConfigureAwait(false);
@@ -516,6 +504,29 @@ public sealed class Commands {
return gamesOwned;
}
private static EAccess ProxyAccess(Bot bot, EAccess access, ulong steamID = 0) {
// The objective here should be simple, calculating effective access of the user
// Problem is, history already proved nothing in this damn file is as simple as it seems
// We use this function for proxying commands such as !status 2 sent to bot 1, which should use 2's user access instead
ArgumentNullException.ThrowIfNull(bot);
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
if ((steamID != 0) && !new SteamID(steamID).IsIndividualAccount) {
throw new ArgumentOutOfRangeException(nameof(steamID));
}
// If we got executed with owner access or lack steamID entirely, then this is effective access
if ((access >= EAccess.Owner) || (steamID == 0)) {
return access;
}
// Otherwise, effective access is the access of the user on target bot, whatever that would be, not this one
return bot.GetAccess(steamID);
}
private async Task<string?> Response2FA(EAccess access) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
@@ -530,7 +541,7 @@ public sealed class Commands {
return FormatBotResponse(success && !string.IsNullOrEmpty(token) ? string.Format(CultureInfo.CurrentCulture, Strings.BotAuthenticatorToken, token) : string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, message));
}
private static async Task<string?> Response2FA(EAccess access, string botNames) {
private static async Task<string?> Response2FA(EAccess access, string botNames, ulong steamID = 0) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
@@ -545,7 +556,7 @@ public sealed class Commands {
return access >= EAccess.Owner ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null;
}
IList<string?> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.Response2FA(access))).ConfigureAwait(false);
IList<string?> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.Response2FA(ProxyAccess(bot, access, steamID)))).ConfigureAwait(false);
List<string> responses = new(results.Where(static result => !string.IsNullOrEmpty(result))!);
@@ -574,7 +585,7 @@ public sealed class Commands {
return FormatBotResponse(success ? message : string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, message));
}
private static async Task<string?> Response2FAConfirm(EAccess access, string botNames, bool confirm) {
private static async Task<string?> Response2FAConfirm(EAccess access, string botNames, bool confirm, ulong steamID = 0) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
@@ -589,7 +600,7 @@ public sealed class Commands {
return access >= EAccess.Owner ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null;
}
IList<string?> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.Response2FAConfirm(access, confirm))).ConfigureAwait(false);
IList<string?> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.Response2FAConfirm(ProxyAccess(bot, access, steamID), confirm))).ConfigureAwait(false);
List<string> responses = new(results.Where(static result => !string.IsNullOrEmpty(result))!);
@@ -668,7 +679,7 @@ public sealed class Commands {
return response.Length > 0 ? response.ToString() : null;
}
private static async Task<string?> ResponseAddLicense(EAccess access, string botNames, string query) {
private static async Task<string?> ResponseAddLicense(EAccess access, string botNames, string query, ulong steamID = 0) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
@@ -687,7 +698,7 @@ public sealed class Commands {
return access >= EAccess.Owner ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null;
}
IList<string?> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseAddLicense(access, query))).ConfigureAwait(false);
IList<string?> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseAddLicense(ProxyAccess(bot, access, steamID), query))).ConfigureAwait(false);
List<string> responses = new(results.Where(static result => !string.IsNullOrEmpty(result))!);
@@ -728,7 +739,7 @@ public sealed class Commands {
return FormatBotResponse(success ? message : string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, message));
}
private static async Task<string?> ResponseAdvancedLoot(EAccess access, string botNames, string appID, string contextID) {
private static async Task<string?> ResponseAdvancedLoot(EAccess access, string botNames, string appID, string contextID, ulong steamID = 0) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
@@ -751,7 +762,7 @@ public sealed class Commands {
return access >= EAccess.Owner ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null;
}
IList<string?> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseAdvancedLoot(access, appID, contextID))).ConfigureAwait(false);
IList<string?> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseAdvancedLoot(ProxyAccess(bot, access, steamID), appID, contextID))).ConfigureAwait(false);
List<string> responses = new(results.Where(static result => !string.IsNullOrEmpty(result))!);
@@ -866,7 +877,7 @@ public sealed class Commands {
return access >= EAccess.Owner ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null;
}
IList<string?> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseAdvancedRedeem(access, options, keys, steamID))).ConfigureAwait(false);
IList<string?> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseAdvancedRedeem(ProxyAccess(bot, access, steamID), options, keys, steamID))).ConfigureAwait(false);
List<string> responses = new(results.Where(static result => !string.IsNullOrEmpty(result))!);
@@ -939,7 +950,7 @@ public sealed class Commands {
return await ResponseAdvancedTransfer(access, appID, contextID, targetBot).ConfigureAwait(false);
}
private static async Task<string?> ResponseAdvancedTransfer(EAccess access, string botNames, string targetAppID, string targetContextID, string botNameTo) {
private static async Task<string?> ResponseAdvancedTransfer(EAccess access, string botNames, string targetAppID, string targetContextID, string botNameTo, ulong steamID = 0) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
@@ -980,7 +991,7 @@ public sealed class Commands {
return access >= EAccess.Owner ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNameTo)) : null;
}
IList<string?> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseAdvancedTransfer(access, appID, contextID, targetBot))).ConfigureAwait(false);
IList<string?> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseAdvancedTransfer(ProxyAccess(bot, access, steamID), appID, contextID, targetBot))).ConfigureAwait(false);
List<string> responses = new(results.Where(static result => !string.IsNullOrEmpty(result))!);
@@ -1001,7 +1012,7 @@ public sealed class Commands {
return FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotGamesToRedeemInBackgroundCount, count));
}
private static async Task<string?> ResponseBackgroundGamesRedeemer(EAccess access, string botNames) {
private static async Task<string?> ResponseBackgroundGamesRedeemer(EAccess access, string botNames, ulong steamID = 0) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
@@ -1016,7 +1027,7 @@ public sealed class Commands {
return access >= EAccess.Owner ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null;
}
IList<string?> results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseBackgroundGamesRedeemer(access)))).ConfigureAwait(false);
IList<string?> results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseBackgroundGamesRedeemer(ProxyAccess(bot, access, steamID))))).ConfigureAwait(false);
List<string> responses = new(results.Where(static result => !string.IsNullOrEmpty(result))!);
@@ -1085,7 +1096,7 @@ public sealed class Commands {
return FormatBotResponse(Strings.Done);
}
private static async Task<string?> ResponseFarm(EAccess access, string botNames) {
private static async Task<string?> ResponseFarm(EAccess access, string botNames, ulong steamID = 0) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
@@ -1100,7 +1111,7 @@ public sealed class Commands {
return access >= EAccess.Owner ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null;
}
IList<string?> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseFarm(access))).ConfigureAwait(false);
IList<string?> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseFarm(ProxyAccess(bot, access, steamID)))).ConfigureAwait(false);
List<string> responses = new(results.Where(static result => !string.IsNullOrEmpty(result))!);
@@ -1115,7 +1126,7 @@ public sealed class Commands {
return access < EAccess.Master ? null : FormatBotResponse(Bot.BotDatabase.FarmingBlacklistAppIDs.Count == 0 ? string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(Bot.BotDatabase.FarmingBlacklistAppIDs)) : string.Join(", ", Bot.BotDatabase.FarmingBlacklistAppIDs));
}
private static async Task<string?> ResponseFarmingBlacklist(EAccess access, string botNames) {
private static async Task<string?> ResponseFarmingBlacklist(EAccess access, string botNames, ulong steamID = 0) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
@@ -1130,7 +1141,7 @@ public sealed class Commands {
return access >= EAccess.Owner ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null;
}
IList<string?> results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseFarmingBlacklist(access)))).ConfigureAwait(false);
IList<string?> results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseFarmingBlacklist(ProxyAccess(bot, access, steamID))))).ConfigureAwait(false);
List<string> responses = new(results.Where(static result => !string.IsNullOrEmpty(result))!);
@@ -1182,7 +1193,7 @@ public sealed class Commands {
return FormatBotResponse(Strings.Done);
}
private static async Task<string?> ResponseFarmingBlacklistAdd(EAccess access, string botNames, string targetAppIDs) {
private static async Task<string?> ResponseFarmingBlacklistAdd(EAccess access, string botNames, string targetAppIDs, ulong steamID = 0) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
@@ -1201,7 +1212,7 @@ public sealed class Commands {
return access >= EAccess.Owner ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null;
}
IList<string?> results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseFarmingBlacklistAdd(access, targetAppIDs)))).ConfigureAwait(false);
IList<string?> results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseFarmingBlacklistAdd(ProxyAccess(bot, access, steamID), targetAppIDs)))).ConfigureAwait(false);
List<string> responses = new(results.Where(static result => !string.IsNullOrEmpty(result))!);
@@ -1248,7 +1259,7 @@ public sealed class Commands {
return FormatBotResponse(Strings.Done);
}
private static async Task<string?> ResponseFarmingBlacklistRemove(EAccess access, string botNames, string targetAppIDs) {
private static async Task<string?> ResponseFarmingBlacklistRemove(EAccess access, string botNames, string targetAppIDs, ulong steamID = 0) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
@@ -1267,7 +1278,7 @@ public sealed class Commands {
return access >= EAccess.Owner ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null;
}
IList<string?> results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseFarmingBlacklistRemove(access, targetAppIDs)))).ConfigureAwait(false);
IList<string?> results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseFarmingBlacklistRemove(ProxyAccess(bot, access, steamID), targetAppIDs)))).ConfigureAwait(false);
List<string> responses = new(results.Where(static result => !string.IsNullOrEmpty(result))!);
@@ -1282,7 +1293,7 @@ public sealed class Commands {
return access < EAccess.Master ? null : FormatBotResponse(Bot.BotDatabase.FarmingPriorityQueueAppIDs.Count == 0 ? string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(Bot.BotDatabase.FarmingPriorityQueueAppIDs)) : string.Join(", ", Bot.BotDatabase.FarmingPriorityQueueAppIDs));
}
private static async Task<string?> ResponseFarmingQueue(EAccess access, string botNames) {
private static async Task<string?> ResponseFarmingQueue(EAccess access, string botNames, ulong steamID = 0) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
@@ -1297,7 +1308,7 @@ public sealed class Commands {
return access >= EAccess.Owner ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null;
}
IList<string?> results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseFarmingQueue(access)))).ConfigureAwait(false);
IList<string?> results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseFarmingQueue(ProxyAccess(bot, access, steamID))))).ConfigureAwait(false);
List<string> responses = new(results.Where(static result => !string.IsNullOrEmpty(result))!);
@@ -1356,7 +1367,7 @@ public sealed class Commands {
return FormatBotResponse(Strings.Done);
}
private static async Task<string?> ResponseFarmingQueueAdd(EAccess access, string botNames, string targetAppIDs) {
private static async Task<string?> ResponseFarmingQueueAdd(EAccess access, string botNames, string targetAppIDs, ulong steamID = 0) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
@@ -1375,7 +1386,7 @@ public sealed class Commands {
return access >= EAccess.Owner ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null;
}
IList<string?> results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseFarmingQueueAdd(access, targetAppIDs)))).ConfigureAwait(false);
IList<string?> results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseFarmingQueueAdd(ProxyAccess(bot, access, steamID), targetAppIDs)))).ConfigureAwait(false);
List<string> responses = new(results.Where(static result => !string.IsNullOrEmpty(result))!);
@@ -1427,7 +1438,7 @@ public sealed class Commands {
return FormatBotResponse(Strings.Done);
}
private static async Task<string?> ResponseFarmingQueueRemove(EAccess access, string botNames, string targetAppIDs) {
private static async Task<string?> ResponseFarmingQueueRemove(EAccess access, string botNames, string targetAppIDs, ulong steamID = 0) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
@@ -1446,7 +1457,7 @@ public sealed class Commands {
return access >= EAccess.Owner ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null;
}
IList<string?> results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseFarmingQueueRemove(access, targetAppIDs)))).ConfigureAwait(false);
IList<string?> results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseFarmingQueueRemove(ProxyAccess(bot, access, steamID), targetAppIDs)))).ConfigureAwait(false);
List<string> responses = new(results.Where(static result => !string.IsNullOrEmpty(result))!);
@@ -1519,7 +1530,7 @@ public sealed class Commands {
return FormatBotResponse(result ? Strings.Done : Strings.WarningFailed);
}
private static async Task<string?> ResponseInput(EAccess access, string botNames, string propertyName, string inputValue) {
private static async Task<string?> ResponseInput(EAccess access, string botNames, string propertyName, string inputValue, ulong steamID = 0) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
@@ -1542,7 +1553,7 @@ public sealed class Commands {
return access >= EAccess.Owner ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null;
}
IList<string?> results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseInput(access, propertyName, inputValue)))).ConfigureAwait(false);
IList<string?> results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseInput(ProxyAccess(bot, access, steamID), propertyName, inputValue)))).ConfigureAwait(false);
List<string> responses = new(results.Where(static result => !string.IsNullOrEmpty(result))!);
@@ -1567,7 +1578,7 @@ public sealed class Commands {
return FormatBotResponse(level.HasValue ? string.Format(CultureInfo.CurrentCulture, Strings.BotLevel, level.Value) : Strings.WarningFailed);
}
private static async Task<string?> ResponseLevel(EAccess access, string botNames) {
private static async Task<string?> ResponseLevel(EAccess access, string botNames, ulong steamID = 0) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
@@ -1582,7 +1593,7 @@ public sealed class Commands {
return access >= EAccess.Owner ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null;
}
IList<string?> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseLevel(access))).ConfigureAwait(false);
IList<string?> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseLevel(ProxyAccess(bot, access, steamID)))).ConfigureAwait(false);
List<string> responses = new(results.Where(static result => !string.IsNullOrEmpty(result))!);
@@ -1611,7 +1622,7 @@ public sealed class Commands {
return FormatBotResponse(success ? message : string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, message));
}
private static async Task<string?> ResponseLoot(EAccess access, string botNames) {
private static async Task<string?> ResponseLoot(EAccess access, string botNames, ulong steamID = 0) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
@@ -1626,14 +1637,14 @@ public sealed class Commands {
return access >= EAccess.Owner ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null;
}
IList<string?> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseLoot(access))).ConfigureAwait(false);
IList<string?> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseLoot(ProxyAccess(bot, access, steamID)))).ConfigureAwait(false);
List<string> responses = new(results.Where(static result => !string.IsNullOrEmpty(result))!);
return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null;
}
private async Task<string?> ResponseLootByRealAppIDs(EAccess access, string realAppIDsText, bool exclude = false) {
private async Task<string?> ResponseLootByRealAppIDs(EAccess access, string realAppIDsText, bool exclude) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
@@ -1675,7 +1686,7 @@ public sealed class Commands {
return FormatBotResponse(success ? message : string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, message));
}
private static async Task<string?> ResponseLootByRealAppIDs(EAccess access, string botNames, string realAppIDsText, bool exclude = false) {
private static async Task<string?> ResponseLootByRealAppIDs(EAccess access, string botNames, string realAppIDsText, bool exclude, ulong steamID = 0) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
@@ -1694,7 +1705,7 @@ public sealed class Commands {
return access >= EAccess.Owner ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null;
}
IList<string?> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseLootByRealAppIDs(access, realAppIDsText, exclude))).ConfigureAwait(false);
IList<string?> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseLootByRealAppIDs(ProxyAccess(bot, access, steamID), realAppIDsText, exclude))).ConfigureAwait(false);
List<string> responses = new(results.Where(static result => !string.IsNullOrEmpty(result))!);
@@ -1709,7 +1720,7 @@ public sealed class Commands {
return access < EAccess.Master ? null : FormatBotResponse(Bot.BotDatabase.MatchActivelyBlacklistAppIDs.Count == 0 ? string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(Bot.BotDatabase.MatchActivelyBlacklistAppIDs)) : string.Join(", ", Bot.BotDatabase.MatchActivelyBlacklistAppIDs));
}
private static async Task<string?> ResponseMatchActivelyBlacklist(EAccess access, string botNames) {
private static async Task<string?> ResponseMatchActivelyBlacklist(EAccess access, string botNames, ulong steamID = 0) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
@@ -1724,7 +1735,7 @@ public sealed class Commands {
return access >= EAccess.Owner ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null;
}
IList<string?> results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseMatchActivelyBlacklist(access)))).ConfigureAwait(false);
IList<string?> results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseMatchActivelyBlacklist(ProxyAccess(bot, access, steamID))))).ConfigureAwait(false);
List<string> responses = new(results.Where(static result => !string.IsNullOrEmpty(result))!);
@@ -1763,7 +1774,7 @@ public sealed class Commands {
return FormatBotResponse(Bot.BotDatabase.MatchActivelyBlacklistAppIDs.AddRange(appIDs) ? Strings.Done : Strings.NothingFound);
}
private static async Task<string?> ResponseMatchActivelyBlacklistAdd(EAccess access, string botNames, string targetAppIDs) {
private static async Task<string?> ResponseMatchActivelyBlacklistAdd(EAccess access, string botNames, string targetAppIDs, ulong steamID = 0) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
@@ -1782,7 +1793,7 @@ public sealed class Commands {
return access >= EAccess.Owner ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null;
}
IList<string?> results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseMatchActivelyBlacklistAdd(access, targetAppIDs)))).ConfigureAwait(false);
IList<string?> results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseMatchActivelyBlacklistAdd(ProxyAccess(bot, access, steamID), targetAppIDs)))).ConfigureAwait(false);
List<string> responses = new(results.Where(static result => !string.IsNullOrEmpty(result))!);
@@ -1821,7 +1832,7 @@ public sealed class Commands {
return FormatBotResponse(Bot.BotDatabase.MatchActivelyBlacklistAppIDs.RemoveRange(appIDs) ? Strings.Done : Strings.NothingFound);
}
private static async Task<string?> ResponseMatchActivelyBlacklistRemove(EAccess access, string botNames, string targetAppIDs) {
private static async Task<string?> ResponseMatchActivelyBlacklistRemove(EAccess access, string botNames, string targetAppIDs, ulong steamID = 0) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
@@ -1840,7 +1851,7 @@ public sealed class Commands {
return access >= EAccess.Owner ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null;
}
IList<string?> results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseMatchActivelyBlacklistRemove(access, targetAppIDs)))).ConfigureAwait(false);
IList<string?> results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseMatchActivelyBlacklistRemove(ProxyAccess(bot, access, steamID), targetAppIDs)))).ConfigureAwait(false);
List<string> responses = new(results.Where(static result => !string.IsNullOrEmpty(result))!);
@@ -1866,10 +1877,13 @@ public sealed class Commands {
Bot.SteamFriends.SetPersonaName(nickname);
// Nickname change affects the current persona state, reset it back to wanted one
Bot.ResetPersonaState();
return FormatBotResponse(Strings.Done);
}
private static async Task<string?> ResponseNickname(EAccess access, string botNames, string nickname) {
private static async Task<string?> ResponseNickname(EAccess access, string botNames, string nickname, ulong steamID = 0) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
@@ -1888,7 +1902,7 @@ public sealed class Commands {
return access >= EAccess.Owner ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null;
}
IList<string?> results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseNickname(access, nickname)))).ConfigureAwait(false);
IList<string?> results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseNickname(ProxyAccess(bot, access, steamID), nickname)))).ConfigureAwait(false);
List<string> responses = new(results.Where(static result => !string.IsNullOrEmpty(result))!);
@@ -2047,7 +2061,7 @@ public sealed class Commands {
return (response.Length > 0 ? response.ToString() : FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotOwnedYet, query)), result);
}
private static async Task<string?> ResponseOwns(EAccess access, string botNames, string query) {
private static async Task<string?> ResponseOwns(EAccess access, string botNames, string query, ulong steamID = 0) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
@@ -2066,7 +2080,7 @@ public sealed class Commands {
return access >= EAccess.Owner ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null;
}
IList<(string? Response, Dictionary<string, string>? OwnedGames)> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseOwns(access, query))).ConfigureAwait(false);
IList<(string? Response, Dictionary<string, string>? OwnedGames)> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseOwns(ProxyAccess(bot, access, steamID), query))).ConfigureAwait(false);
List<(string Response, Dictionary<string, string> OwnedGames)> validResults = new(results.Where(static result => !string.IsNullOrEmpty(result.Response) && (result.OwnedGames != null))!);
@@ -2119,7 +2133,7 @@ public sealed class Commands {
return FormatBotResponse(success ? message : string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, message));
}
private static async Task<string?> ResponsePause(EAccess access, string botNames, bool permanent, string? resumeInSecondsText = null) {
private static async Task<string?> ResponsePause(EAccess access, string botNames, bool permanent, string? resumeInSecondsText = null, ulong steamID = 0) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
@@ -2134,7 +2148,7 @@ public sealed class Commands {
return access >= EAccess.Owner ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null;
}
IList<string?> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponsePause(access, permanent, resumeInSecondsText))).ConfigureAwait(false);
IList<string?> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponsePause(ProxyAccess(bot, access, steamID), permanent, resumeInSecondsText))).ConfigureAwait(false);
List<string> responses = new(results.Where(static result => !string.IsNullOrEmpty(result))!);
@@ -2212,7 +2226,7 @@ public sealed class Commands {
return await ResponsePlay(access, gamesToPlay, gameName.Length > 0 ? gameName.ToString() : null).ConfigureAwait(false);
}
private static async Task<string?> ResponsePlay(EAccess access, string botNames, string targetGameIDs) {
private static async Task<string?> ResponsePlay(EAccess access, string botNames, string targetGameIDs, ulong steamID = 0) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
@@ -2231,7 +2245,7 @@ public sealed class Commands {
return access >= EAccess.Owner ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null;
}
IList<string?> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponsePlay(access, targetGameIDs))).ConfigureAwait(false);
IList<string?> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponsePlay(ProxyAccess(bot, access, steamID), targetGameIDs))).ConfigureAwait(false);
List<string> responses = new(results.Where(static result => !string.IsNullOrEmpty(result))!);
@@ -2256,7 +2270,7 @@ public sealed class Commands {
return FormatBotResponse(points.HasValue ? string.Format(CultureInfo.CurrentCulture, Strings.BotPointsBalance, points) : Strings.WarningFailed);
}
private static async Task<string?> ResponsePointsBalance(EAccess access, string botNames) {
private static async Task<string?> ResponsePointsBalance(EAccess access, string botNames, ulong steamID = 0) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
@@ -2271,7 +2285,7 @@ public sealed class Commands {
return access >= EAccess.Owner ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null;
}
IList<string?> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponsePointsBalance(access))).ConfigureAwait(false);
IList<string?> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponsePointsBalance(ProxyAccess(bot, access, steamID)))).ConfigureAwait(false);
List<string> responses = new(results.Where(static result => !string.IsNullOrEmpty(result))!);
@@ -2412,7 +2426,7 @@ public sealed class Commands {
return FormatBotResponse(await Bot.ArchiWebHandler.ChangePrivacySettings(userPrivacy).ConfigureAwait(false) ? Strings.Success : Strings.WarningFailed);
}
private static async Task<string?> ResponsePrivacy(EAccess access, string botNames, string privacySettingsText) {
private static async Task<string?> ResponsePrivacy(EAccess access, string botNames, string privacySettingsText, ulong steamID = 0) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
@@ -2431,7 +2445,7 @@ public sealed class Commands {
return access >= EAccess.Owner ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null;
}
IList<string?> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponsePrivacy(access, privacySettingsText))).ConfigureAwait(false);
IList<string?> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponsePrivacy(ProxyAccess(bot, access, steamID), privacySettingsText))).ConfigureAwait(false);
List<string> responses = new(results.Where(static result => !string.IsNullOrEmpty(result))!);
@@ -2697,7 +2711,7 @@ public sealed class Commands {
return response.Length > 0 ? response.ToString() : null;
}
private static async Task<string?> ResponseRedeem(EAccess access, string botNames, string keysText, ulong steamID = 0, ERedeemFlags redeemFlags = ERedeemFlags.None) {
private static async Task<string?> ResponseRedeem(EAccess access, string botNames, string keysText, ERedeemFlags redeemFlags = ERedeemFlags.None, ulong steamID = 0) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
@@ -2716,7 +2730,7 @@ public sealed class Commands {
return access >= EAccess.Owner ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null;
}
IList<string?> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseRedeem(access, keysText, steamID, redeemFlags))).ConfigureAwait(false);
IList<string?> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseRedeem(ProxyAccess(bot, access, steamID), keysText, steamID, redeemFlags))).ConfigureAwait(false);
List<string> responses = new(results.Where(static result => !string.IsNullOrEmpty(result))!);
@@ -2741,7 +2755,7 @@ public sealed class Commands {
return FormatBotResponse(Strings.Done);
}
private static async Task<string?> ResponseReset(EAccess access, string botNames) {
private static async Task<string?> ResponseReset(EAccess access, string botNames, ulong steamID = 0) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
@@ -2756,7 +2770,7 @@ public sealed class Commands {
return access >= EAccess.Owner ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null;
}
IList<string?> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseReset(access))).ConfigureAwait(false);
IList<string?> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseReset(ProxyAccess(bot, access, steamID)))).ConfigureAwait(false);
List<string> responses = new(results.Where(static result => !string.IsNullOrEmpty(result))!);
@@ -2791,7 +2805,7 @@ public sealed class Commands {
return FormatBotResponse(success ? message : string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, message));
}
private static async Task<string?> ResponseResume(EAccess access, string botNames) {
private static async Task<string?> ResponseResume(EAccess access, string botNames, ulong steamID = 0) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
@@ -2806,7 +2820,7 @@ public sealed class Commands {
return access >= EAccess.Owner ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null;
}
IList<string?> results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseResume(access)))).ConfigureAwait(false);
IList<string?> results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseResume(ProxyAccess(bot, access, steamID))))).ConfigureAwait(false);
List<string> responses = new(results.Where(static result => !string.IsNullOrEmpty(result))!);
@@ -2827,7 +2841,7 @@ public sealed class Commands {
return FormatBotResponse(success ? message : string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, message));
}
private static async Task<string?> ResponseStart(EAccess access, string botNames) {
private static async Task<string?> ResponseStart(EAccess access, string botNames, ulong steamID = 0) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
@@ -2842,7 +2856,7 @@ public sealed class Commands {
return access >= EAccess.Owner ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null;
}
IList<string?> results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseStart(access)))).ConfigureAwait(false);
IList<string?> results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseStart(ProxyAccess(bot, access, steamID))))).ConfigureAwait(false);
List<string> responses = new(results.Where(static result => !string.IsNullOrEmpty(result))!);
@@ -2906,7 +2920,7 @@ public sealed class Commands {
return (FormatBotResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotStatusIdling, soloGame.AppID, soloGame.GameName, soloGame.CardsRemaining, Bot.CardsFarmer.GamesToFarmReadOnly.Count, Bot.CardsFarmer.GamesToFarmReadOnly.Sum(static game => game.CardsRemaining), Bot.CardsFarmer.TimeRemaining.ToHumanReadable())), Bot);
}
private static async Task<string?> ResponseStatus(EAccess access, string botNames) {
private static async Task<string?> ResponseStatus(EAccess access, string botNames, ulong steamID = 0) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
@@ -2921,7 +2935,7 @@ public sealed class Commands {
return access >= EAccess.Owner ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null;
}
IList<(string? Response, Bot Bot)> results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseStatus(access)))).ConfigureAwait(false);
IList<(string? Response, Bot Bot)> results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseStatus(ProxyAccess(bot, access, steamID))))).ConfigureAwait(false);
List<(string Response, Bot Bot)> validResults = new(results.Where(static result => !string.IsNullOrEmpty(result.Response))!);
@@ -2950,7 +2964,7 @@ public sealed class Commands {
return FormatBotResponse(success ? message : string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, message));
}
private static async Task<string?> ResponseStop(EAccess access, string botNames) {
private static async Task<string?> ResponseStop(EAccess access, string botNames, ulong steamID = 0) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
@@ -2965,7 +2979,7 @@ public sealed class Commands {
return access >= EAccess.Owner ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null;
}
IList<string?> results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseStop(access)))).ConfigureAwait(false);
IList<string?> results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseStop(ProxyAccess(bot, access, steamID))))).ConfigureAwait(false);
List<string> responses = new(results.Where(static result => !string.IsNullOrEmpty(result))!);
@@ -2980,7 +2994,7 @@ public sealed class Commands {
return access < EAccess.Master ? null : FormatBotResponse(Bot.BotDatabase.TradingBlacklistSteamIDs.Count == 0 ? string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsEmpty, nameof(Bot.BotDatabase.TradingBlacklistSteamIDs)) : string.Join(", ", Bot.BotDatabase.TradingBlacklistSteamIDs));
}
private static async Task<string?> ResponseTradingBlacklist(EAccess access, string botNames) {
private static async Task<string?> ResponseTradingBlacklist(EAccess access, string botNames, ulong steamID = 0) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
@@ -2995,7 +3009,7 @@ public sealed class Commands {
return access >= EAccess.Owner ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null;
}
IList<string?> results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseTradingBlacklist(access)))).ConfigureAwait(false);
IList<string?> results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseTradingBlacklist(ProxyAccess(bot, access, steamID))))).ConfigureAwait(false);
List<string> responses = new(results.Where(static result => !string.IsNullOrEmpty(result))!);
@@ -3034,7 +3048,7 @@ public sealed class Commands {
return FormatBotResponse(Bot.BotDatabase.TradingBlacklistSteamIDs.AddRange(targetIDs) ? Strings.Done : Strings.NothingFound);
}
private static async Task<string?> ResponseTradingBlacklistAdd(EAccess access, string botNames, string targetSteamIDs) {
private static async Task<string?> ResponseTradingBlacklistAdd(EAccess access, string botNames, string targetSteamIDs, ulong steamID = 0) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
@@ -3053,7 +3067,7 @@ public sealed class Commands {
return access >= EAccess.Owner ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null;
}
IList<string?> results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseTradingBlacklistAdd(access, targetSteamIDs)))).ConfigureAwait(false);
IList<string?> results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseTradingBlacklistAdd(ProxyAccess(bot, access, steamID), targetSteamIDs)))).ConfigureAwait(false);
List<string> responses = new(results.Where(static result => !string.IsNullOrEmpty(result))!);
@@ -3092,7 +3106,7 @@ public sealed class Commands {
return FormatBotResponse(Bot.BotDatabase.TradingBlacklistSteamIDs.RemoveRange(targetIDs) ? Strings.Done : Strings.NothingFound);
}
private static async Task<string?> ResponseTradingBlacklistRemove(EAccess access, string botNames, string targetSteamIDs) {
private static async Task<string?> ResponseTradingBlacklistRemove(EAccess access, string botNames, string targetSteamIDs, ulong steamID = 0) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
@@ -3111,7 +3125,7 @@ public sealed class Commands {
return access >= EAccess.Owner ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null;
}
IList<string?> results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseTradingBlacklistRemove(access, targetSteamIDs)))).ConfigureAwait(false);
IList<string?> results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseTradingBlacklistRemove(ProxyAccess(bot, access, steamID), targetSteamIDs)))).ConfigureAwait(false);
List<string> responses = new(results.Where(static result => !string.IsNullOrEmpty(result))!);
@@ -3158,7 +3172,7 @@ public sealed class Commands {
return FormatBotResponse(success ? message : string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, message));
}
private static async Task<string?> ResponseTransfer(EAccess access, string botNames, string botNameTo) {
private static async Task<string?> ResponseTransfer(EAccess access, string botNames, string botNameTo, ulong steamID = 0) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
@@ -3177,14 +3191,14 @@ public sealed class Commands {
return access >= EAccess.Owner ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null;
}
IList<string?> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseTransfer(access, botNameTo))).ConfigureAwait(false);
IList<string?> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseTransfer(ProxyAccess(bot, access, steamID), botNameTo))).ConfigureAwait(false);
List<string> responses = new(results.Where(static result => !string.IsNullOrEmpty(result))!);
return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null;
}
private async Task<string?> ResponseTransferByRealAppIDs(EAccess access, IReadOnlyCollection<uint> realAppIDs, Bot targetBot, bool exclude = false) {
private async Task<string?> ResponseTransferByRealAppIDs(EAccess access, IReadOnlyCollection<uint> realAppIDs, Bot targetBot, bool exclude) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
@@ -3220,7 +3234,7 @@ public sealed class Commands {
return FormatBotResponse(success ? message : string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, message));
}
private async Task<string?> ResponseTransferByRealAppIDs(EAccess access, string realAppIDsText, string botNameTo, bool exclude = false) {
private async Task<string?> ResponseTransferByRealAppIDs(EAccess access, string realAppIDsText, string botNameTo, bool exclude) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
@@ -3262,7 +3276,7 @@ public sealed class Commands {
return await ResponseTransferByRealAppIDs(access, realAppIDs, targetBot, exclude).ConfigureAwait(false);
}
private static async Task<string?> ResponseTransferByRealAppIDs(EAccess access, string botNames, string realAppIDsText, string botNameTo, bool exclude = false) {
private static async Task<string?> ResponseTransferByRealAppIDs(EAccess access, string botNames, string realAppIDsText, string botNameTo, bool exclude, ulong steamID = 0) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
@@ -3307,7 +3321,7 @@ public sealed class Commands {
return access >= EAccess.Owner ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNameTo)) : null;
}
IList<string?> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseTransferByRealAppIDs(access, realAppIDs, targetBot, exclude))).ConfigureAwait(false);
IList<string?> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseTransferByRealAppIDs(ProxyAccess(bot, access, steamID), realAppIDs, targetBot, exclude))).ConfigureAwait(false);
List<string> responses = new(results.Where(static result => !string.IsNullOrEmpty(result))!);
@@ -3358,7 +3372,7 @@ public sealed class Commands {
return FormatBotResponse(completeSuccess ? Strings.Success : Strings.Done);
}
private static async Task<string?> ResponseUnpackBoosters(EAccess access, string botNames) {
private static async Task<string?> ResponseUnpackBoosters(EAccess access, string botNames, ulong steamID = 0) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
@@ -3373,7 +3387,7 @@ public sealed class Commands {
return access >= EAccess.Owner ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null;
}
IList<string?> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseUnpackBoosters(access))).ConfigureAwait(false);
IList<string?> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseUnpackBoosters(ProxyAccess(bot, access, steamID)))).ConfigureAwait(false);
List<string> responses = new(results.Where(static result => !string.IsNullOrEmpty(result))!);
@@ -3414,7 +3428,7 @@ public sealed class Commands {
return !Bot.IsConnectedAndLoggedOn ? FormatBotResponse(Strings.BotNotConnected) : FormatBotResponse(Bot.WalletCurrency != ECurrencyCode.Invalid ? string.Format(CultureInfo.CurrentCulture, Strings.BotWalletBalance, Bot.WalletBalance / 100.0, Bot.WalletCurrency.ToString()) : Strings.BotHasNoWallet);
}
private static async Task<string?> ResponseWalletBalance(EAccess access, string botNames) {
private static async Task<string?> ResponseWalletBalance(EAccess access, string botNames, ulong steamID = 0) {
if (!Enum.IsDefined(access)) {
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
}
@@ -3429,7 +3443,7 @@ public sealed class Commands {
return access >= EAccess.Owner ? FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)) : null;
}
IList<string?> results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseWalletBalance(access)))).ConfigureAwait(false);
IList<string?> results = await Utilities.InParallel(bots.Select(bot => Task.Run(() => bot.Commands.ResponseWalletBalance(ProxyAccess(bot, access, steamID))))).ConfigureAwait(false);
List<string> responses = new(results.Where(static result => !string.IsNullOrEmpty(result))!);

View File

@@ -69,6 +69,9 @@ public sealed class BotConfig {
[PublicAPI]
public const byte DefaultHoursUntilCardDrops = 3;
[PublicAPI]
public const EPersonaStateFlag DefaultOnlineFlags = 0;
[PublicAPI]
public const EPersonaState DefaultOnlineStatus = EPersonaState.Online;
@@ -187,6 +190,9 @@ public sealed class BotConfig {
[JsonProperty(Required = Required.DisallowNull)]
public ImmutableHashSet<Asset.EType> MatchableTypes { get; private set; } = DefaultMatchableTypes;
[JsonProperty(Required = Required.DisallowNull)]
public EPersonaStateFlag OnlineFlags { get; private set; } = DefaultOnlineFlags;
[JsonProperty(Required = Required.DisallowNull)]
public EPersonaState OnlineStatus { get; private set; } = DefaultOnlineStatus;
@@ -346,6 +352,9 @@ public sealed class BotConfig {
[UsedImplicitly]
public bool ShouldSerializeMatchableTypes() => !Saving || ((MatchableTypes != DefaultMatchableTypes) && !MatchableTypes.SetEquals(DefaultMatchableTypes));
[UsedImplicitly]
public bool ShouldSerializeOnlineFlags() => !Saving || (OnlineFlags != DefaultOnlineFlags);
[UsedImplicitly]
public bool ShouldSerializeOnlineStatus() => !Saving || (OnlineStatus != DefaultOnlineStatus);
@@ -466,6 +475,10 @@ public sealed class BotConfig {
return (false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorConfigPropertyInvalid, nameof(MatchableTypes), matchableType));
}
if (OnlineFlags < 0) {
return (false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorConfigPropertyInvalid, nameof(OnlineFlags), OnlineFlags));
}
if (!Enum.IsDefined(OnlineStatus)) {
return (false, string.Format(CultureInfo.CurrentCulture, Strings.ErrorConfigPropertyInvalid, nameof(OnlineStatus), OnlineStatus));
}

View File

@@ -94,48 +94,6 @@ internal sealed class BotDatabase : SerializableFile {
[JsonProperty(PropertyName = $"_{nameof(MobileAuthenticator)}")]
private MobileAuthenticator? BackingMobileAuthenticator;
private bool SaveNeededDueToMigration;
[JsonProperty(Required = Required.DisallowNull)]
[Obsolete("Available for limited time and only to migrate existing databases")]
private ConcurrentHashSet<ulong> BlacklistedFromTradesSteamIDs {
set {
if (TradingBlacklistSteamIDs.AddRange(value) && string.IsNullOrEmpty(FilePath)) {
SaveNeededDueToMigration = true;
}
}
}
[JsonProperty(Required = Required.DisallowNull)]
[Obsolete("Available for limited time and only to migrate existing databases")]
private ConcurrentHashSet<uint> IdlingBlacklistedAppIDs {
set {
if (FarmingBlacklistAppIDs.AddRange(value) && string.IsNullOrEmpty(FilePath)) {
SaveNeededDueToMigration = true;
}
}
}
[JsonProperty(Required = Required.DisallowNull)]
[Obsolete("Available for limited time and only to migrate existing databases")]
private ConcurrentHashSet<uint> IdlingPriorityAppIDs {
set {
if (FarmingPriorityQueueAppIDs.AddRange(value) && string.IsNullOrEmpty(FilePath)) {
SaveNeededDueToMigration = true;
}
}
}
[JsonProperty(Required = Required.DisallowNull)]
[Obsolete("Available for limited time and only to migrate existing databases")]
private ConcurrentHashSet<uint> MatchActivelyBlacklistedAppIDs {
set {
if (MatchActivelyBlacklistAppIDs.AddRange(value) && string.IsNullOrEmpty(FilePath)) {
SaveNeededDueToMigration = true;
}
}
}
private BotDatabase(string filePath) {
if (string.IsNullOrEmpty(filePath)) {
throw new ArgumentNullException(nameof(filePath));
@@ -243,12 +201,6 @@ internal sealed class BotDatabase : SerializableFile {
botDatabase.FilePath = filePath;
if (botDatabase.SaveNeededDueToMigration) {
botDatabase.SaveNeededDueToMigration = false;
Utilities.InBackground(botDatabase.Save);
}
return botDatabase;
}

View File

@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>5.2.3.0</Version>
<Version>5.2.3.3</Version>
</PropertyGroup>
<PropertyGroup>

2
wiki

Submodule wiki updated: 106c7d8811...9c878c9bfa