static async Task Main(string[] args) { // register to unhandled exceptions handling // this allows logging exceptions that were not handled with a try-catch block AppDomain.CurrentDomain.UnhandledException += OnUnhandledException; // optional: create a logger ILogger <WolfClient> log = CreateLogger <WolfClient>(); // create client and listen to events we're interested in _client = new WolfClient(log); _client.MessageReceived += OnMessageReceived; // This event is raised when client receives and parses any message type. _client.AddMessageListener <WelcomeEvent>(OnWelcome); // these 2 callbacks are invoked if received message is a WolfEvent (first callback) _client.AddMessageListener <ChatMessage>(OnChatMessage); // or a chat message (second callback). // reconnector is part of Wolfringo.Utilities package WolfClientReconnector reconnector = new WolfClientReconnector(_client); reconnector.FailedToReconnect += OnFailedToReconnect; // start connection and prevent the application from closing await _client.ConnectAsync(); await Task.Delay(-1); }
public void CmdDependencyInjection(IWolfClient client, ChatMessage message, IServiceProvider services, ILogger log, CommandsOptions options) { log.LogInformation("Current user: {UserID}", client.CurrentUserID.Value); log.LogInformation("Message contents: {Message}", message.Text); log.LogInformation("Type of IServiceProvider: {Type}", services.GetType().Name); log.LogInformation("Prefix: {Prefix}", options.Prefix); }
public SizeCheckingHandler(IWolfClient client, ILogger <SizeCheckingHandler> logger, IHostEnvironment environment, IHttpClientFactory httpClientFactory, IUserDataStore userDataStore, IGroupConfigStore groupConfigStore, IOptionsMonitor <SizeCheckingOptions> picSizeOptions, IOptionsMonitor <BotOptions> botOptions, IOptionsMonitor <CommandsOptions> commandsOptions) { // store all services this._client = client; this._log = logger; this._environment = environment; this._httpClientFactory = httpClientFactory; this._userDataStore = userDataStore; this._groupConfigStore = groupConfigStore; this._picSizeOptions = picSizeOptions; this._botOptions = botOptions; this._commandsOptions = commandsOptions; this._cts = new CancellationTokenSource(); // add client listeners this._client.AddMessageListener <ChatMessage>(OnChatMessage); // read options this.OnPicSizeOptionsReload(picSizeOptions.CurrentValue); picSizeOptions.OnChange(this.OnPicSizeOptionsReload); }
/// <summary>Initializes a command service.</summary> /// <param name="client">WOLF client. Required.</param> /// <param name="options">Commands options that will be used as default when running a command. Required.</param> /// <param name="services">Services provider that will be used by all commands. Null will cause a backup provider to be used.</param> /// <param name="log">Logger to log messages and errors to. If null, all logging will be disabled.</param> /// <param name="cancellationToken">Cancellation token that can be used for cancelling all tasks.</param> public CommandsService(IWolfClient client, CommandsOptions options, ILogger log, IServiceProvider services = null, CancellationToken cancellationToken = default) { // init private this._commands = new Dictionary <ICommandInstanceDescriptor, ICommandInstance>(); this._lock = new SemaphoreSlim(1, 1); this._cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); this._disposableServices = new List <IDisposable>(2); this._started = false; // init required this._client = client ?? services?.GetService <IWolfClient>() ?? throw new ArgumentNullException(nameof(client)); this._options = options ?? services?.GetService <CommandsOptions>() ?? throw new ArgumentNullException(nameof(options)); // init optionals this._log = log ?? services?.GetService <ILogger <CommandsService> >() ?? services?.GetService <ILogger <ICommandsService> >() ?? services.GetService <ILogger>(); this._argumentConverterProvider = services?.GetService <IArgumentConverterProvider>() ?? CreateAsDisposable <ArgumentConverterProvider>(); this._handlerProvider = services?.GetService <ICommandsHandlerProvider>() ?? CreateAsDisposable <CommandsHandlerProvider>(); this._argumentsParser = services?.GetService <IArgumentsParser>() ?? new ArgumentsParser(); this._parameterBuilder = services?.GetService <IParameterBuilder>() ?? new ParameterBuilder(); this._initializers = services?.GetService <ICommandInitializerProvider>() ?? new CommandInitializerProvider(); this._commandsLoader = services?.GetService <ICommandsLoader>() ?? new CommandsLoader(this._initializers, this._log); // init service provider - use combine, to use fallback one as well this._fallbackServices = this.CreateFallbackServiceProvider(); this._services = CombinedServiceProvider.Combine(services, this._fallbackServices); // register event handlers this._client.AddMessageListener <ChatMessage>(OnMessageReceived); }
/// <summary>Awaits next message of given type that matches specified conditions.</summary> /// <param name="client">Client to await message from.</param> /// <param name="conditions">Conditions that received message needs to match.</param> /// <param name="cancellationToken">Token that can cancel the awaiting.</param> /// <returns>Next message.</returns> /// <seealso cref="AwaitNextInGroupAsync(IWolfClient, uint, CancellationToken)"/> /// <seealso cref="AwaitNextGroupByUserAsync(IWolfClient, uint, uint, CancellationToken)"/> /// <seealso cref="AwaitNextPrivateByUserAsync(IWolfClient, uint, CancellationToken)"/> public static Task <T> AwaitNextAsync <T>(this IWolfClient client, Func <T, bool> conditions, CancellationToken cancellationToken = default) where T : IWolfMessage { IInteractiveListener <T> listener = new InteractiveListener <T>(conditions); return(listener.AwaitNextAsync(client, cancellationToken)); }
static async Task Main(string[] args) { // register to unhandled exceptions handling // this allows logging exceptions that were not handled with a try-catch block AppDomain.CurrentDomain.UnhandledException += OnUnhandledException; // optional: create a logger factory ILoggerFactory logFactory = CreateLoggerFactory(); // optional: enable DI - add logger factory to it as well IServiceCollection services = new ServiceCollection() .AddSingleton <ILoggerFactory>(logFactory); // create client and listen to events we're interested in _client = new WolfClient(logFactory.CreateLogger <WolfClient>()); _client.AddMessageListener <WelcomeEvent>(OnWelcome); // these 2 callbacks are invoked if received message is a WolfEvent (first callback) // initialize commands system CommandsOptions options = new CommandsOptions() { Prefix = "!", // set prefix RequirePrefix = PrefixRequirement.Always, // make prefix always required - can also for example require it in group only CaseSensitivity = false // make commands case insensitive }; CommandsService commands = new CommandsService(_client, options, logFactory.CreateLogger <CommandsService>(), services.BuildServiceProvider()); await commands.StartAsync(); // calling StartAsync causes reload of all commands // start connection and prevent the application from closing await _client.ConnectAsync(); await Task.Delay(-1); }
public BotAdminHandler(IWolfClient client) { // store all services this._client = client; this._cts = new CancellationTokenSource(); // add client listeners this._client.AddMessageListener <WelcomeEvent>(OnLoggedIn); // init relog requests track this._reconnectRequests = new List <CommandContext>(1); }
public MentionsHandler(IWolfClient client, IMentionConfigStore mentionConfigStore, IOptionsMonitor <BotOptions> botOptions, IOptionsMonitor <MentionsOptions> mentionsOptions, ILogger <MentionsHandler> logger, IHostEnvironment environment) { // store all services this._log = logger; this._mentionsOptions = mentionsOptions; this._botOptions = botOptions; this._client = client; this._mentionConfigStore = mentionConfigStore; this._environment = environment; // add client listeners this._client.AddMessageListener <ChatMessage>(OnChatMessage); }
/// <summary>Awaits next message of given type that matches specified conditions.</summary> /// <remarks><para>This method actually uses a <see cref="CancellationTokenSource"/> under the hood, and simply cancels the token /// after specified <paramref name="timeout"/>.</para> /// <para>If the task is cancelled a default (most likely null> value will be returned. /// <see cref="TaskCanceledException"/> will not be thrown.</para></remarks> /// <param name="client">Client to await message from.</param> /// <param name="conditions">Conditions that received message needs to match.</param> /// <param name="timeout">Timeout after which waiting will be aborted.</param> /// <param name="cancellationToken">Token that can cancel the awaiting.</param> /// <returns>Next message.</returns> /// <seealso cref="AwaitNextGroupByUserAsync(IWolfClient, uint, uint, TimeSpan, CancellationToken)"/> /// <seealso cref="AwaitNextInGroupAsync(IWolfClient, uint, TimeSpan, CancellationToken)"/> /// <seealso cref="AwaitNextPrivateByUserAsync(IWolfClient, uint, TimeSpan, CancellationToken)"/> public static async Task <T> AwaitNextAsync <T>(this IWolfClient client, Func <T, bool> conditions, TimeSpan timeout, CancellationToken cancellationToken = default) where T : IWolfMessage { try { using (CancellationTokenSource cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken)) { Task <T> task = AwaitNextAsync(client, conditions, cts.Token); cts.CancelAfter(timeout); return(await task.ConfigureAwait(false)); } } catch (OperationCanceledException) { return(default);
private async Task <bool> SaveGroupConfigAsync(IWolfClient client, uint groupID, uint currentID, CancellationToken cancellationToken = default) { try { GroupConfig groupConfig = await _groupConfigStore.GetGroupConfigAsync(groupID, cancellationToken).ConfigureAwait(false); groupConfig.CurrentGuesswhatGameID = currentID; await _groupConfigStore.SetGroupConfigAsync(groupConfig, false, cancellationToken).ConfigureAwait(false); return(true); } catch (Exception ex) when(ex.LogAsError(_log, "Failed saving group config for group {GroupID} in the database", groupID)) { await client.SendGroupTextMessageAsync(groupID, "/alert Failed saving group config in the database.", cancellationToken).ConfigureAwait(false); return(false); } }
/// <inheritdoc/> public async Task <T> AwaitNextAsync(IWolfClient client, CancellationToken cancellationToken = default) { TaskCompletionSource <T> tcs = new TaskCompletionSource <T>(); using (cancellationToken.Register(() => tcs.TrySetCanceled(cancellationToken))) { Action <T> callback = null; callback = message => { if (!this._conditions(message)) { return; } client.RemoveMessageListener <T>(callback); tcs.TrySetResult(message); }; client.AddMessageListener(callback); return(await tcs.Task); } }
/// <summary>Checks if group members are downloaded. If not, it'll attempt to repopulate it.</summary> /// <param name="client">Client to request group members with.</param> /// <param name="group">Group to validate members of.</param> /// <param name="cancellationToken">Token to cancel the task.</param> /// <returns>True if members list is valid after the operation; otherwise false.</returns> public static async Task <bool> RevalidateGroupMembersAsync(this IWolfClient client, WolfGroup group, CancellationToken cancellationToken = default) { if (group == null) { throw new ArgumentNullException(nameof(group)); } if (group.Members?.Any() == true) { return(true); } if (client == null) { throw new ArgumentNullException(nameof(client)); } try { GroupMembersListResponse membersResponse = await client.SendAsync <GroupMembersListResponse>( new GroupMembersListMessage(group.ID), cancellationToken).ConfigureAwait(false); // client should be configured to intercept this response // however, just in case it's not (like when caching is disabled), do it here as well if (membersResponse?.GroupMembers?.Any() == true) { try { EntityModificationHelper.ReplaceAllGroupMembers(group, membersResponse.GroupMembers); } catch (NotSupportedException) { return(false); } return(true); } } // handle case when requesting profiles for group the user is not in catch (MessageSendingException ex) when(ex.StatusCode == System.Net.HttpStatusCode.Forbidden) { } return(false); }
public override Task <bool> Validate(IWolfClient client, CommandMessage message) { var(msg, _) = message; return(Task.FromResult(msg.MimeType == "text/image_link")); }
/// <summary>Creates instance of reconnector using default config values.</summary> /// <param name="client">Client to automatically reconnect.</param> public WolfClientReconnector(IWolfClient client) : this(client, new ReconnectorConfig()) { }
/// <summary>Creates instance of reconnector.</summary> /// <param name="client">Client to automatically reconnect.</param> /// <param name="config">Reconnector configuration.</param> public WolfClientReconnector(IWolfClient client, ReconnectorConfig config) { this._client = client ?? throw new ArgumentNullException(nameof(client)); this.Config = config ?? throw new ArgumentNullException(nameof(config)); this._client.Disconnected += OnClientDisconnected; }
// can also be IWolfClient public HostedMessageHandler(IWolfClient client) { this._client = client; this._client.AddMessageListener <ChatMessage>(OnChatMessage); }
public override Task <bool> Validate(IWolfClient client, CommandMessage message) { var urls = Urls(message.Message.Content); return(Task.FromResult(urls.Length > 0)); }
/// <summary>Awaits next chat message in specified group.</summary> /// <param name="client">Client to await message from.</param> /// <param name="groupID">Group ID to await the message from.</param> /// <param name="cancellationToken">Token that can cancel the awaiting.</param> /// <returns>Next message.</returns> /// <seealso cref="AwaitNextPrivateByUserAsync(IWolfClient, uint, CancellationToken)"/> /// <seealso cref="AwaitNextGroupByUserAsync(IWolfClient, uint, uint, CancellationToken)"/> /// <seealso cref="AwaitNextAsync{T}(IWolfClient, Func{T, bool}, CancellationToken)"/> public static Task <ChatMessage> AwaitNextInGroupAsync(this IWolfClient client, uint groupID, CancellationToken cancellationToken = default) => AwaitNextAsync <ChatMessage>(client, message => message.IsGroupMessage && message.RecipientID == groupID, cancellationToken);
/// <summary>Removes event listener.</summary> /// <remarks>Provided type <typeparamref name="T"/> must be the same as the type used when adding the listener.</remarks> /// <param name="callback">Callback to remove.</param> public static void RemoveMessageListener <T>(this IWolfClient client, Action <T> callback) where T : IWolfMessage => client.RemoveMessageListener(new TypedMessageCallback <T>(callback));
private static void Client_OnReconnected(IWolfClient client, int reconnectionCount) { Console.WriteLine("Reconnect(ed/ing)... " + reconnectionCount); }
/// <summary>Initializes a command service.</summary> /// <param name="client">WOLF client. Required.</param> /// <param name="options">Commands options that will be used as default when running a command. Required.</param> /// <param name="services">Services provider that will be used by all commands. Null will cause a default to be used.</param> /// <param name="cancellationToken">Cancellation token that can be used for cancelling all tasks.</param> public CommandsService(IWolfClient client, CommandsOptions options, IServiceProvider services = null, CancellationToken cancellationToken = default) : this(client, options, null, services, cancellationToken) { }
public override Task <bool> Validate(IWolfClient client, CommandMessage message) { var(_, user) = message; return(Task.FromResult(user.Id == "43681734")); }
// this constructor won't be used, because the other constructor has [CommandHandlerConstructor] attribute public ExamplePersistentCommandsHandler(IWolfClient client, string specialValue) : this(client) { }
public ExamplePersistentCommandsHandler(IWolfClient client, ILogger <ExamplePersistentCommandsHandler> log = null) { this._client = client; this._client.Disconnected += OnClientDisconnected; this._log = log; }
/// <summary>Creates a command context.</summary> /// <param name="message">Chat message that triggered the command.</param> /// <param name="client">WOLF client that received the message.</param> /// <param name="options">Default options to use for processing the command.</param> public CommandContext(ChatMessage message, IWolfClient client, CommandsOptions options) { this.Message = message; this.Client = client; this.Options = options; }
private static void _client_OnConnected(IWolfClient client) { Console.WriteLine("Client connected"); }
public static Task SendGroupMembersBugNoticeAsync(this IWolfClient client, ChatMessage message, CancellationToken cancellationToken = default) { return(client.ReplyTextAsync(message, "/alert WOLF servers are broken and refused to let me check if you're an admin in this group.\n" + "Please tell WOLF server developers to fix this already. :(", cancellationToken)); }
/// <summary>Sends message, and waits for response from the server.</summary> /// <remarks><para>If client uses <see cref="DefaultResponseTypeResolver"/>, the type of response provided with /// <see cref="ResponseTypeAttribute"/> on <paramref name="message"/> will be used for deserialization.<br/> /// Otherwise, response will be deserialized as<see cref="WolfResponse"/>.</para></remarks> /// <param name="message">Message to send.</param> /// <returns>Sending response.</returns> /// <exception cref="MessageSendingException">Server responded with error.</exception> public static Task <WolfResponse> SendAsync(this IWolfClient client, IWolfMessage message, CancellationToken cancellationToken = default) => client.SendAsync <WolfResponse>(message, cancellationToken);
/// <summary>Adds event listener, invoking when received message is of correct type and has matching command.</summary> /// <typeparam name="T">Type of received message to invoke callback for.</typeparam> /// <param name="command">Message command that has to match for callback to be invoked.</param> /// <param name="callback">Callback to invoke on event.</param> public static void AddMessageListener <T>(this IWolfClient client, string command, Action <T> callback) where T : IWolfMessage => client.AddMessageListener(new CommandMessageCallback <T>(command, callback));
/// <summary>Awaits next chat message in PM from specified user.</summary> /// <param name="client">Client to await message from.</param> /// <param name="userID">User ID to await the message from.</param> /// <param name="cancellationToken">Token that can cancel the awaiting.</param> /// <returns>Next message.</returns> /// <seealso cref="AwaitNextInGroupAsync(IWolfClient, uint, CancellationToken)"/> /// <seealso cref="AwaitNextGroupByUserAsync(IWolfClient, uint, uint, CancellationToken)"/> /// <seealso cref="AwaitNextAsync{T}(IWolfClient, Func{T, bool}, CancellationToken)"/> public static Task <ChatMessage> AwaitNextPrivateByUserAsync(this IWolfClient client, uint userID, CancellationToken cancellationToken = default) => AwaitNextAsync <ChatMessage>(client, message => message.IsPrivateMessage && message.SenderID == userID, cancellationToken);