Wolfringo v2.0
Wolfringo 2.0 brought some significant changes - most notably introduction of WolfClient and CommandsService builders, separation of cache and client logic, help commands support. Command requirements also got a little overhaul. And of course, it also includes numerous smaller changes.
WolfClient and CommandsService builders
In previous versions of Wolfringo, creating WolfClient and CommandsService could get a bit confusing due to the requirement of manual service collection populating. To address this, Wolfringo 2.0 introduces WolfClientBuilder and CommandsServiceBuilder.
Note: the most commonly used constructors for WolfClient and CommandsService are now marked as obsolete, and will be removed in future versions. More complex constructors were removed completely, and need to be replaces with builders.
WolfClientBuilder
Creating WolfClient with logging enabled looked like this:
ILoggerFactory logFactory = CreateLoggerFactory();
IServiceCollection services = new ServiceCollection()
.AddSingleton<ILoggerFactory>(logFactory);
_client = new WolfClient(logFactory.CreateLogger<WolfClient>());
_client.AddMessageListener<WelcomeEvent>(OnWelcome);
Now to do the same, create your WolfClient as follows:
_client = new WolfClientBuilder()
.WithLogging(CreateLoggerFactory())
.Build();
_client.AddMessageListener<WelcomeEvent>(OnWelcome);
CommandsServiceBuilder
Creating Commands Service now works similar:
CommandsService commands = new CommandsServiceBuilder()
.WithLogging(CreateLoggerFactory())
.WithWolfClient(_client)
.WithPrefix("!")
.Build();
await commands.StartAsync();
Now you can also add commands directly to WolfClientBuilder - this way, client and its logging will be automatically added, and StartAsync() called for you!
_client = new WolfClientBuilder()
.WithLogging(CreateLoggerFactory())
.WithCommands(commands =>
{
commands.WithPrefix("!");
})
.Build();
Additional services
There are 2 ways of adding additional services for Dependency Injection purposes.
- The most common one is using
WithSingletonService
(on CommandsServiceBuilder) orWithService
(on WolfClientBuilder) methods that will register the service with the collection. - Alternatively, both CommandsServiceBuilder and WolfClientBuilder can optionally take
IServiceCollection
through their constructor.
Commands Changes
Building Help Command
Help commands are a common need in bots, and Wolfringo now has features that make creating them much easier. See Help Command Guide to learn more.
If you already have a custom help command, you can upgrade it easily, but don't worry - it's not required! Help Command feature is opt-in, and your existing help command will work just fine!
Getting Raw Arguments Text
Previously, to get raw text of command arguments, you could use 2 approaches, both of which had issues:
- Get Message Text - required you to manually remove prefix and command name.
- Use
string[]
as argument - included all parsed arguments, but they were missing grouping characters, such as parentheses.
Now there's a better way. You can simply mark a string argument with [ArgumentsText] attribute. See Arguments in Wolfringo Commands for an example and more info.
Recipient info in Error Messages
[MissingError] attribute and [ConvertingError] attribute now support {{RecipientID}}
and {{RecipientName}}
placeholders - they'll be replaced with bot's ID/nickname for private messages and group's ID/name for group messages.
Getting Command Options
Previously getting command options for given command would require manually checking attributes and global options instance.
Since Wolfringo 2.0, you can inject a new ICommandOptions interface to your command method. This options instance will contain the actual prefix etc values for given command.
Skipping Commands
Wolfringo Commands System no longer has a strict rule that one message received = only one command handler attempted. Command execution now can be skipped, making CommandsService attempt to try another command. This is opt-in, so unless you start using this feature, your commands will function just like they did before.
To do so, either return Task<ICommandResult>
or ICommandResult
with status of Skip in your command, or tell the requirement attribute to skip on failure. See Skipping using Requirements and Skipping using return value for more information.
Command Timeouts
Commands execution now times out after 1 day by default. This is a huge timeout, and isn't infinite only to help prevent any memory leaks. You can however specify timeout for both Standard and Regex commands by using Timeout
property.
In order to use the timeout, inject @System.Threading.CancellationToken into your command method, and use it in async calls. Alternatively you can also call ThrowIfCancellationRequested
method.
[Command("timeout", Timeout = 1000)]
public async Task CmdTimeoutAsync(CancellationToken cancellationToken)
{
// simulate long running task
await Task.Delay(1500, cancellationToken);
// check timeout
cancellationToken.ThrowIfCancellationRequested();
// oops! This will time out!
Console.WriteLine("This will never be written to console, because long-running task takes longer than Timeout!");
}
Regex commands also support a separate timeout value which is then provided to Regex Engine. This exists to prevent user attacks exploiting complex regexes in your commands. Default value is 1000ms which should be generous enough for most regexes, but if you wish to change it, you can use RegexTimeout
property.
Note
Note that values of both Timeout
and RegexTimeout
properties are specified in milliseconds. So for 1 second timeout, set the value to 1000
.
Command Requirement changes
If you created custom command requirements, you'll need to change the return type of CheckAsync method from Task<bool>
to Task<ICommandResult>
.
ICommandResult changes
If you used ICommandResult for any purpose in your bot, there are a few changes that you should be aware of:
IsSuccess
is now obsolete and will be removed in later Wolfringo version.Status
property has been added instead.Exception
property has been removed. Wolfringo Commands System will simply throw exceptions now.- To support above changes, previous constructors of built-in result implementations have been replaced with new ones.
- Built-in result implementations have been changed from
struct
toclass
.
Achievement changes
Group Achievements
Group achievements are now supported. Yay!
Sender now has a GetGroupAchievementsAsync method for it. UserAchievementListResponse
has also been replaced with EntityAchievementListResponse.
Nullable received times
WOLF server now can report achievement with null receive date. For this reason GetUserAchievementsAsync return type has changed slightly, and you'll need to update your variable types.
// old way:
IReadOnlyDictionary<WolfAchievement, DateTime> achievements = await _client.GetUserAchievementsAsync(2644384, WolfLanguage.English);
// new way:
IReadOnlyDictionary<WolfAchievement, DateTime?> achievements = await _client.GetUserAchievementsAsync(2644384, WolfLanguage.English);
Caching Configuration
Caching and WolfClient have been separated in Wolfringo 2.0. This means that you can implement your own cache and add it using Dependency Injection.
If you didn't disable caching, you'll notice no change at all.
However, if you were disabling cache or its parts, you'll need to make a few adjustments.
Properties for enabling and disabling caching have been removed from WolfClient. Instead, you can configure caching using WolfClientBuilder:
_client = new WolfClientBuilder()
.WithDefaultCaching(new WolfCacheOptions()
{
// set to false to disable
options.UsersCachingEnabled = true;
options.GroupsCachingEnabled = true;
options.CharmsCachingEnabled = true;
options.AchievementsCachingEnabled = true;
})
.Build();
WolfCacheOptions is present in TehGM.Wolfringo.Caching
namespace.
WolfClient defaults
DefaultServerURL
, BetaServerURL
and DefaultDevice
constants have been moved from WolfClient to WolfClientOptions.
Ignoring Own Messages
WolfClient will still ignore its own messages by default, but if you used to disable this feature, you'll find that IgnoreOwnChatMessages
property is now read-only. To change it, you need to use WolfClientOptions class.
_client = new WolfClientBuilder()
.ConfigureOptions(options =>
{
// set to false to disable
options.IgnoreOwnChatMessages = true;
})
.Build();
Removed ServiceProvider classes
SimpleServiceProvider
and CombinedServiceProvider
classes were used internally to help with fallback when library user didn't provide some required services. This is now handled by builders which use framework-provided @Microsoft.Extensions.DependencyInjection.IServiceCollection and @System.IServiceProvider, so both SimpleServiceProvider
and CombinedServiceProvider
were removed.
If you used these classes, you'll need to remove references to them, and replace either with framework-provided solutions, or create your own implementations.
Other changes
There also has been a large number of smaller changes that shouldn't affect most people unless they're Customizing Wolfringo. Please refer to changelog and pull request on GitHub for more details.