-
-
Notifications
You must be signed in to change notification settings - Fork 1k
Plugins development pt BR
O ASF inclui suporte para plugins personalizados que podem ser carregados durante a execução. Os plugins permitem que você personalize o comportamento do ASF, adicionando comandos e lógicas de troca personalizados ou mesmo uma integração completa com serviços de terceiros e APIs.
Esta página descreve os plugins do ASF sob a perspectiva dos desenvolvedores — criação, manutenção, publicação e afins. Caso você quiser ver pela perspectiva do usuário, vá aqui em vez disso.
Plugins são bibliotecas padrão .NET que definem uma classe que herda da interface comum IPlugin
, declarada no ASF. Você pode desenvolver plugins de forma completamente independente da linha principal do ASF e reutilizá-los nas versões atuais e futuras do ASF, desde que a API interna do ASF permaneça compatível. O sistema de plugins utilizado no ASF é baseado no System.Composition
, anteriormente conhecido como Managed Extensibility Framework, que permite ao ASF descobrir e carregar suas bibliotecas durante a execução.
Nós preparamos o ASF-PluginTemplate para você, que pode (e deve) usar como base para o seu projeto de plugin. Usar o modelo não é obrigatório (pois você pode fazer tudo do zero), mas nós altamente recomendamos que você o utilize, pois pode agilizar drasticamente seu desenvolvimento e reduzir o tempo necessário para acertar todos os detalhes. Simplesmente confira o README do modelo e ele irá te guiar adiante. De qualquer forma, cobriremos os conceitos básicos abaixo caso você queira começar do zero ou entender melhor os conceitos utilizados no modelo de plugin - geralmente, você não precisa fazer nada disso se decidir usar nosso modelo de plugin.
Seu projeto deve ser uma biblioteca padrão .NET voltada para o framework apropriado da versão alvo do ASF, conforme especificado na seção de compilação.
O projeto deve referenciar o assembly principal ArchiSteamFarm
, seja pela biblioteca pré-compilada ArchiSteamFarm.dll
que você baixou como parte do lançamento, ou pelo projeto de código-fonte (por exemplo, se você decidiu adicionar a árvore do ASF como um submódulo). Isso permitirá que você acesse e descubra as estruturas, métodos e propriedades do ASF, especialmente a interface principal IPlugin
, da qual você precisará herdar no próximo passo. O projeto também deve referenciar, no mínimo, o System.Composition.AttributedModel
, que permite a você usar o atributo [Export]
para que seu IPlugin
seja utilizado pelo ASF. Além disso, você pode querer/precisar fazer referência a outras bibliotecas comuns a fim de interpretar as estruturas de dados que são dadas a você em algumas interfaces, mas a menos que você realmente precise delas, isso é suficiente por enquanto.
Se você fez tudo certo, seu csproj
será semelhante ao exemplo abaixo:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<!-- Since you're loading plugin into the ASF process, which already includes this dependency, IncludeAssets="compile" allows you to omit it from the final output -->
<PackageReference Include="System.Composition.AttributedModel" IncludeAssets="compile" Version="9.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="C:\\Path\To\ArchiSteamFarm\ArchiSteamFarm.csproj" ExcludeAssets="all" Private="false" />
<!-- If building with downloaded DLL binary, use this instead of <ProjectReference> above -->
<!-- <Reference Include="ArchiSteamFarm" HintPath="C:\\Path\To\Downloaded\ArchiSteamFarm.dll" /> -->
</ItemGroup>
</Project>
Do lado do código, a classe do seu plugin deve herdar da interface IPlugin
(seja explicitamente ou implicitamente, herdando de uma interface mais especializada, como IASF
) e incluir o atributo [Export(typeof(IPlugin))]
para ser reconhecida pelo ASF durante a execução. O exemplo mais simples que alcança isso seria o seguinte:
using System;
using System.Composition;
using System.Threading.Tasks;
using ArchiSteamFarm;
using ArchiSteamFarm.Plugins;
namespace YourNamespace.YourPluginName;
[Export(typeof(IPlugin))]
public sealed class YourPluginName : IPlugin {
public string Name => nameof(YourPluginName);
public Version Version => typeof(YourPluginName).Assembly.GetName().Version;
public Task OnLoaded() {
ASF.ArchiLogger.LogGenericInfo("Hello World!");
return Task.CompletedTask;
}
}
Para utilizar o seu plugin, você deve primeiro compilá-lo. Você pode fazer isso tanto a partir do seu IDE quanto dentro do diretório raiz do seu projeto através do seguinte comando:
# Se o seu projeto for independente (não é preciso definir um nome já que ele será único)
dotnet publish -c "Release" -o "out"
# Se o seu projeto fizer parte da árvore de código do ASF (para evitar a compilação de partes desnecessárias)
dotnet publish YourPluginName -c "Release" -o "out"
Após isso seu plugin estará pronto. It's up to you how exactly you want to distribute and publish your plugin, but we recommend creating a zip archive where you'll put your compiled plugin together with its dependencies. This way user will simply need to unpack your zip archive into a standalone subdirectory inside their plugins
directory and do nothing else.
Esse é o cenário mais básico, apenas para começar. We have ExamplePlugin
project that shows you example interfaces and actions that you can do within your own plugin, including helpful comments. Feel free to take a look if you'd like to learn from a working code, or discover ArchiSteamFarm.Plugins
namespace yourself and refer to the included documentation for all available options. We'll also further elaborate on some core concepts below to explain them better.
If instead of example plugin you'd want to learn from real projects, there are several official plugins developed by us, e.g. ItemsMatcher
, MobileAuthenticator
or SteamTokenDumper
. In addition to that, there are also plugins developed by other developers, in our third-party section.
O ASF, além do que você tem acesso pela interface, expõe muito de suas APIs internas, as quais você pode usar para estender sua funcionalidade. Por exemplo, se você quiser enviar algum tipo de novo pedido para o Steam web você não precisa implementar tudo a partir do zero, tratando todos os problemas com os quais tivemos de lidar antes de você. Simply use our Bot.ArchiWebHandler
which already exposes a lot of UrlWithSession()
methods for you to use, handling all the lower-level stuff such as authentication, session refresh or web limiting for you. Likewise, for sending web requests outside of Steam platform, you could use standard .NET HttpClient
class, but it's much better idea to use Bot.ArchiWebHandler.WebBrowser
that is available for you, which once again offers you a helpful hand, for example in regards to retrying failed requests.
We have a very open policy in terms of our API availability, so if you'd like to make use of something that ASF code already includes, simply open an issue and explain in it your planned use case of our ASF's internal API. É muito provável que não tenhamos nada contra, desde que o seu uso preterido faça sentido. This also includes all suggestions in regards to new IPlugin
interfaces that could make sense to be added in order to extend existing functionality.
Regardless of ASF API availability however, nothing is stopping you from e.g. including Discord.Net
library in your application and creating a bridge between your Discord bot and ASF commands, since your plugin can also have dependencies on its own. The possibilities are endless, and we made our best to give you as much freedom and flexibility as possible within your plugin, so there are no artificial limits on anything - your plugin is loaded into the main ASF process and can do anything that is realistically possible to do from within C# code.
É importante salientar que o ASF é um aplicativo de consumo e não uma biblioteca típica com superfície de API fixa da qual você pode depender incondicionalmente. This means that you can't assume that your plugin once compiled will keep working with all future ASF releases regardless, it's simply impossible if we want to keep developing the program further, and being unable to adapt to ever-ongoing Steam changes for the sake of backwards compatibility is just not appropriate for our case. Isto deve ser lógico para você, mas é importante salientar esse fato.
We'll do our best to keep public parts of ASF working and stable, but we'll not be afraid to break the compatibility if good enough reasons arise, following our deprecation policy in the process. This is especially important in regards to internal ASF structures that are exposed to you as part of ASF infrastructure (e.g. ArchiWebHandler
) which could be improved (and therefore rewritten) as part of ASF enhancements in one of the future versions. Faremos o possível para informá-lo adequadamente nos logs de alterações e incluir avisos apropriados durante o tempo de execução sobre recursos obsoletos. Não temos intenção de reescrever tudo sem que haja uma necessidade realmente grande, então você pode ter certeza de que a próxima versão menor do ASF não vai simplesmente destruir seu plugin completamente apenas porque ela tem um número de versão maior, mas ficar de olho nos logs de atualização e verificar ocasionalmente se tudo está funcionando bem é uma boa ideia.
Your plugin will include at least two dependencies by default, ArchiSteamFarm
reference for internal API (IPlugin
at the minimum), and PackageReference
of System.Composition.AttributedModel
that is required for being recognized as ASF plugin to begin with ([Export]
clause). In addition to that, it may include more dependencies in regards to what you've decided to do in your plugin (e.g. Discord.Net
library if you've decided to integrate with Discord).
The output of your build will include your core YourPluginName.dll
library, as well as all the dependencies that you've referenced. Since you're developing a plugin to already-working program, you don't have to, and even shouldn't include dependencies that ASF already includes, for example ArchiSteamFarm
, SteamKit2
or AngleSharp
. Retirar as dependências compiladas que são compartilhadas com o ASF não é um requisito absoluto para que seu plugin funcione, mas fazer isso irá reduzir drasticamente a quantidade de memória utilizada e o tamanho do seu plugin, além de melhorar o desempenho, devido ao fato de que o ASF compartilhará suas dependências e só carregará as bibliotecas que ele não conhece.
No geral, é recomendado incluir apenas as bibliotecas que o ASF não inclui ou inclui em uma versão errada/incompatível. Examples of those would be obviously YourPluginName.dll
, but for example also Discord.Net.dll
if you decided to depend on it, as ASF doesn't include it itself. Bundling libraries that are shared with ASF can still make sense if you want to ensure API compatibility (e.g. being sure that AngleSharp
which you depend on in your plugin will always be in version X
and not the one that ASF ships with), but obviously doing that comes for a price of increased memory/size and worse performance, and therefore should be carefully evaluated.
If you know that the dependency which you need is included in ASF, you can mark it with IncludeAssets="compile"
as we showed you in the example csproj
above. Isto dirá ao compilador para evitar a publicação da biblioteca referenciada em si, pois o ASF já a inclui. Likewise, notice that we reference the ASF project with ExcludeAssets="all" Private="false"
which works in a very similar way - telling the compiler to not produce any ASF files (as the user already has them). This applies only when referencing ASF project, since if you reference a dll
library, then you're not producing ASF files as part of your plugin.
If you're confused about above statement and you don't know better, check which dll
libraries are included in ASF-generic.zip
package and ensure that your plugin includes only those that are not part of it yet. This will be only YourPluginName.dll
for the most simple plugins. Se você tiver algum problema durante o tempo de execução em relação a algumas bibliotecas, inclua também as bibliotecas afetadas. Se todo o resto falhar, você pode sempre decidir agrupar tudo.
As dependências nativas são geradas como parte de compilações específicas do Sistema Operacional, pois não há tempo de execução .NET disponível no host e o ASF está sendo executado por meio de seu próprio tempo de execução .NET que é agrupado como parte da compilação específica do Sistema Operacional. Para minimizar o tamanho da compilação, o ASF corta suas dependências nativas para incluir apenas o código que pode ser alcançado dentro do programa, o que efetivamente reduz as partes não utilizadas do tempo de execução. This can create a potential problem for you in regards to your plugin, if suddenly you find out yourself in a situation where your plugin depends on some .NET feature that isn't being used in ASF, and therefore OS-specific builds can't execute it properly, usually throwing System.MissingMethodException
or System.Reflection.ReflectionTypeLoadException
in the process. As your plugin grows in size and becomes more and more complex, sooner or later you'll hit the surface that is not covered by our OS-specific build.
Isto nunca será um problema com as compilações genéricas, pois elas nunca lidam com dependências nativas (pois elas têm um tempo de execução completo no host, executando o ASF). This is why it's a recommended practice to use your plugin in generic builds exclusively, but obviously that has its own downside of cutting your plugin from users that are running OS-specific builds of ASF. Se você estiver se perguntando se seu problema está relacionado a dependências nativas, você pode usar este método para verificação, carregue seu plugin em uma compilação genérica do ASF e veja se ele funciona. Caso positivo, tudo estará certo com as dependencias do pugin e os problemas estão sendo causados pelas dependencias nativas.
Infelizmente, tivemos que fazer uma escolha difícil entre publicar todo o tempo de execução como parte de nossas compilações específicas para SO, ou cortar os recursos não utilizados deixanto a compilação final com cerca de 80 MB a menos em comparação a versão completa. Nós escolhemos a segunda opção, e infelizmente é impossível para você incluir os recursos que faltam no tempo de execução juntamente com seu plugin. If your project requires access to runtime features that are left out, you have to include full .NET runtime that you depend on, and that means running your plugin in generic
ASF flavour. Você não pode executar seu plugin em compilações específicas para SO, já que essas compilações simplesmente não possuem um recurso do tempo de execução que você precisa, e o tempo de execução do .NET runtime é incapaz de "mesclar" a dependência nativa que você poderia ter fornecido à nossa. Talvez isso melhore um dia no futuro, mas no momento simplesmente não é possível.
As compilações específicas de SO do ASF incluem o mínimo de funcionalidades adicionais que são necessárias para executar nossos plugins oficiais. Ele também expande ligeiramente a quantidade de dependências adicionais que podem ser necessárias para os plug-ins mais básicos. Portanto, nem todos os plugins vão precisar se preocupar com dependências nativas - apenas aqueles que vão além do que o ASF e nossos plugins oficiais precisam diretamente. Isso é feito como em extra, porque se nós precisarmos incluir dependências adicionais para nossos propósitos, podemos simplesmente fornecê-las diretamente ao ASF, tornando-as disponíveis para você também. Infelizmente, isso nem sempre é suficiente, e conforme o seu plugin fica maior e mais complexo, a probabilidade de se deparar com funcionalidades problemáticas aumenta. Therefore, we usually recommend you to run your custom plugins in generic
ASF flavour exclusively. Você ainda pode verificar manualmente que a compilação específica do Sistema Operacional do ASF tem tudo o que o seu plugin necessita para funcionar - mas como isso muda tanto para suas atualizações como para as nossas, poderá ser um desafio mantê-lo.
Sometimes it may be possible to "workaround" missing features by either using alternative options or reimplementing them in your plugin. This is however not always possible or viable, especially if the missing feature comes from third-party dependencies that you include as part of your plugin. You can always try to run your plugin in OS-specific build and attempt to make it work, but it might become too much hassle in the long-run, especially if you want from your code to just work, rather than fight with ASF surface.
ASF offers you two interfaces for implementing automatic updates in your plugin:
-
IGitHubPluginUpdates
providing you easy way to implement GitHub-based updates similar to general ASF update mechanism -
IPluginUpdates
providing you lower-level functionality that allows for custom update mechanism, if you require something more complex
The minimum checklist of things that are required for updates to work:
- You need to declare
RepositoryName
, which defines where the updates are pulled from. - You need to make use of tags and releases provided by GitHub. Tag must be in format parsable to a plugin version, e.g.
1.0.0.0
. -
Version
property of the plugin must match tag that it comes from. This means that binary available under tag1.2.3.4
must present itself as1.2.3.4
. - Each tag should have appropriate release available on GitHub with zip file release asset that includes your plugin binary files. The binary files that include your
IPlugin
classes should be available in the root directory within the zip file.
This will allow the mechanism in ASF to:
- Resolve current
Version
of your plugin, e.g.1.0.1
. - Use GitHub API to fetch latest
tag
available inRepositoryName
repo, e.g.1.0.2
. - Determine that
1.0.2
>1.0.1
, check release of1.0.2
tag to find.zip
file with the plugin update. - Download that
.zip
file, extract it, and put its content in the directory that includedYourPlugin.dll
before. - Restart ASF to load your plugin in
1.0.2
version.
Notas adicionais:
- Plugin updates may require appropriate ASF config values, namely
PluginsUpdateMode
and/orPluginsUpdateList
. - Our plugin template already includes everything you need, simply rename the plugin to proper values, and it'll work out of the box.
- You can use combination of latest release as well as pre-releases, they'll be used as per
UpdateChannel
the user has defined. - There is
CanUpdate
boolean property you can override for disabling/enabling plugins update on your side, for example if you want to skip updates temporarily, or through any other reason. - It's possible to have multiple zip files in the release if you want to target different ASF versions. See
GetTargetReleaseAsset()
remarks. For example, you can haveMyPlugin-V6-0.zip
as well asMyPlugin.zip
, which will cause ASF in versionV6.0.x.x
to pick the first one, while all other ASF versions will use the second one. - If the above is not sufficient to you and you require custom logic for picking the correct
.zip
file, you can overrideGetTargetReleaseAsset()
function and provide the artifact for ASF to use. - If your plugin needs to do some extra work before/after update, you can override
OnPluginUpdateProceeding()
and/orOnPluginUpdateFinished()
methods.
This interface allows you to implement custom logic for updates if by any reason IGithubPluginUpdates
is not sufficient to you, for example because you have tags that do not parse to versions, or because you're not using GitHub platform at all.
There is a single GetTargetReleaseURL()
function for you to override, which expects from you Uri?
of target plugin update location. ASF provides you some core variables that relate to the context the function was called with, but other than that, you're responsible for implementing everything you need in that function and providing ASF the URL that should be used, or null
if the update is not available.
Other than that, it's similar to GitHub updates, where the URL should point to a .zip
file with the binary files available in the root directory. You also have OnPluginUpdateProceeding()
and OnPluginUpdateFinished()
methods available.
- 🏡 Início
- 🔧 Configuração
- 💬 Perguntas frequentes
- ⚙️ Primeiros passos (comece aqui)
- 👥 Ativador de códigos em segundo plano
- 📢 Comandos
- 🛠️ Compatibilidade
- 🧩 ItemsMatcherPlugin
- 📋 Gerenciamento
- ⏱️ Desempenho
- 📡 Comunicação remota
- 👪 Compartilhamento de Biblioteca Steam
- 🔄 Trocas
- ⌨️ Argumentos de linha de comando
- 🚧 Depreciação
- 🐳 Docker
- 🤔 Perguntas frequentes adicionais
- 🚀 Configuração de alto desempenho
- 🔗 IPC
- 🌐 Localização
- 📝 Registros
- 💾 Configuração para baixo consumo de memória
- 🕵🏼♂️ MonitoringPlugin
- 🔌 Plugins
- 🔐 Segurança
- 🧩 SteamTokenDumperPlugin
- 📦 Aplicativos de terceiros
- 📵 Autenticação em duas etapas