Exemple #1
0
        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);
        }
Exemple #2
0
 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);
        }
Exemple #5
0
        /// <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);
        }
Exemple #8
0
        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);
        }
Exemple #9
0
 /// <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);
Exemple #10
0
        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);
        }
Exemple #13
0
 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);
 }
Exemple #17
0
        public override Task <bool> Validate(IWolfClient client, CommandMessage message)
        {
            var urls = Urls(message.Message.Content);

            return(Task.FromResult(urls.Length > 0));
        }
Exemple #18
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));
Exemple #20
0
 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)
 {
 }
Exemple #22
0
        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;
 }
Exemple #26
0
 private static void _client_OnConnected(IWolfClient client)
 {
     Console.WriteLine("Client connected");
 }
Exemple #27
0
 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));
Exemple #30
0
 /// <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);