mirror of
				https://gitlab.com/Kwoth/nadekobot.git
				synced 2025-11-04 00:34:26 -05:00 
			
		
		
		
	added: Added .afk <msg>? command which sets an afk message which will trigger whenever someone pings a user.
				
					
				
			This commit is contained in:
		@@ -402,11 +402,10 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
#if GLOBAL_NADEKO
 | 
			
		||||
    [OwnerOnly]
 | 
			
		||||
#endif
 | 
			
		||||
    public async Task ExprsImport([Leftover] string input = null)
 | 
			
		||||
    {
 | 
			
		||||
        // todo cooldown on public bot for 1 day, limit 100
 | 
			
		||||
        
 | 
			
		||||
        if (!AdminInGuildOrOwnerInDm())
 | 
			
		||||
        {
 | 
			
		||||
            await Response().Error(strs.expr_insuff_perms).SendAsync();
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										148
									
								
								src/NadekoBot/Modules/Utility/AfkService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								src/NadekoBot/Modules/Utility/AfkService.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,148 @@
 | 
			
		||||
using NadekoBot.Common.ModuleBehaviors;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Modules.Utility;
 | 
			
		||||
 | 
			
		||||
public sealed class AfkService : INService, IReadyExecutor
 | 
			
		||||
{
 | 
			
		||||
    private readonly IBotCache _cache;
 | 
			
		||||
    private readonly DiscordSocketClient _client;
 | 
			
		||||
    private readonly MessageSenderService _mss;
 | 
			
		||||
 | 
			
		||||
    private static readonly TimeSpan _maxAfkDuration = 8.Hours();
 | 
			
		||||
    public AfkService(IBotCache cache, DiscordSocketClient client, MessageSenderService mss)
 | 
			
		||||
    {
 | 
			
		||||
        _cache = cache;
 | 
			
		||||
        _client = client;
 | 
			
		||||
        _mss = mss;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static TypedKey<string> GetKey(ulong userId)
 | 
			
		||||
        => new($"afk:msg:{userId}");
 | 
			
		||||
 | 
			
		||||
    public async Task<bool> SetAfkAsync(ulong userId, string text)
 | 
			
		||||
    {
 | 
			
		||||
        var added = await _cache.AddAsync(GetKey(userId), text, _maxAfkDuration, overwrite: true);
 | 
			
		||||
 | 
			
		||||
        async Task StopAfk(SocketMessage socketMessage)
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                if (socketMessage.Author?.Id == userId)
 | 
			
		||||
                {
 | 
			
		||||
                    await _cache.RemoveAsync(GetKey(userId));
 | 
			
		||||
                    _client.MessageReceived -= StopAfk;
 | 
			
		||||
 | 
			
		||||
                    // write the message saying afk status cleared
 | 
			
		||||
 | 
			
		||||
                    if (socketMessage.Channel is ITextChannel tc)
 | 
			
		||||
                    {
 | 
			
		||||
                        _ = Task.Run(async () =>
 | 
			
		||||
                        {
 | 
			
		||||
                            var msg = await _mss.Response(tc).Confirm("AFK message cleared!").SendAsync();
 | 
			
		||||
 | 
			
		||||
                            msg.DeleteAfter(5);
 | 
			
		||||
                        });
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                Log.Warning("Unexpected error occurred while trying to stop afk: {Message}", ex.Message);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        _client.MessageReceived += StopAfk;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        _ = Task.Run(async () =>
 | 
			
		||||
        {
 | 
			
		||||
            await Task.Delay(_maxAfkDuration);
 | 
			
		||||
            _client.MessageReceived -= StopAfk;
 | 
			
		||||
        });
 | 
			
		||||
        
 | 
			
		||||
        return added;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Task OnReadyAsync()
 | 
			
		||||
    {
 | 
			
		||||
        _client.MessageReceived += TryTriggerAfkMessage;
 | 
			
		||||
 | 
			
		||||
        return Task.CompletedTask;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Task TryTriggerAfkMessage(SocketMessage arg)
 | 
			
		||||
    {
 | 
			
		||||
        if (arg.Author.IsBot)
 | 
			
		||||
            return Task.CompletedTask;
 | 
			
		||||
 | 
			
		||||
        if (arg is not IUserMessage uMsg || uMsg.Channel is not ITextChannel tc)
 | 
			
		||||
            return Task.CompletedTask;
 | 
			
		||||
        
 | 
			
		||||
        if ((arg.MentionedUsers.Count is 0 or > 3) && uMsg.ReferencedMessage is null)
 | 
			
		||||
            return Task.CompletedTask;
 | 
			
		||||
 | 
			
		||||
        _ = Task.Run(async () =>
 | 
			
		||||
        {
 | 
			
		||||
            var botUser = await tc.Guild.GetCurrentUserAsync();
 | 
			
		||||
 | 
			
		||||
            var perms = botUser.GetPermissions(tc);
 | 
			
		||||
 | 
			
		||||
            if (!perms.SendMessages)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            ulong mentionedUserId = 0;
 | 
			
		||||
 | 
			
		||||
            if (arg.MentionedUsers.Count <= 3)
 | 
			
		||||
            {
 | 
			
		||||
                foreach (var uid in uMsg.MentionedUserIds)
 | 
			
		||||
                {
 | 
			
		||||
                    if (uid == arg.Author.Id)
 | 
			
		||||
                        continue;
 | 
			
		||||
 | 
			
		||||
                    if (arg.Content.StartsWith($"<@{uid}>") || arg.Content.StartsWith($"<@!{uid}>"))
 | 
			
		||||
                    {
 | 
			
		||||
                        mentionedUserId = uid;
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (mentionedUserId == 0)
 | 
			
		||||
            {
 | 
			
		||||
                if (uMsg.ReferencedMessage?.Author?.Id is not ulong repliedUserId)
 | 
			
		||||
                {
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
                mentionedUserId = repliedUserId;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                var result = await _cache.GetAsync(GetKey(mentionedUserId));
 | 
			
		||||
                if (result.TryPickT0(out var msg, out _))
 | 
			
		||||
                {
 | 
			
		||||
                    var st = SmartText.CreateFrom(msg);
 | 
			
		||||
                    
 | 
			
		||||
                    st = "The user is AFK: " + st;
 | 
			
		||||
                    
 | 
			
		||||
                    var toDelete = await _mss.Response(arg.Channel)
 | 
			
		||||
                                             .Message(uMsg)
 | 
			
		||||
                                             .Text(st)
 | 
			
		||||
                                             .Sanitize(false)
 | 
			
		||||
                                             .SendAsync();
 | 
			
		||||
 | 
			
		||||
                    toDelete.DeleteAfter(30);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            catch (HttpException ex)
 | 
			
		||||
            {
 | 
			
		||||
                Log.Warning("Error in afk service: {Message}", ex.Message);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        return Task.CompletedTask;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using LinqToDB.Reflection;
 | 
			
		||||
using NadekoBot.Modules.Utility.Services;
 | 
			
		||||
using Newtonsoft.Json;
 | 
			
		||||
using System.Diagnostics;
 | 
			
		||||
@@ -7,6 +7,7 @@ using System.Text.Json;
 | 
			
		||||
using System.Text.Json.Serialization;
 | 
			
		||||
using Microsoft.CodeAnalysis.CSharp.Scripting;
 | 
			
		||||
using Microsoft.CodeAnalysis.Scripting;
 | 
			
		||||
using NadekoBot.Modules.Games.Hangman;
 | 
			
		||||
using NadekoBot.Modules.Searches.Common;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Modules.Utility;
 | 
			
		||||
@@ -41,6 +42,7 @@ public partial class Utility : NadekoModule
 | 
			
		||||
    private readonly IHttpClientFactory _httpFactory;
 | 
			
		||||
    private readonly VerboseErrorsService _veService;
 | 
			
		||||
    private readonly IServiceProvider _services;
 | 
			
		||||
    private readonly AfkService _afkService;
 | 
			
		||||
 | 
			
		||||
    public Utility(
 | 
			
		||||
        DiscordSocketClient client,
 | 
			
		||||
@@ -50,7 +52,8 @@ public partial class Utility : NadekoModule
 | 
			
		||||
        DownloadTracker tracker,
 | 
			
		||||
        IHttpClientFactory httpFactory,
 | 
			
		||||
        VerboseErrorsService veService,
 | 
			
		||||
        IServiceProvider services)
 | 
			
		||||
        IServiceProvider services,
 | 
			
		||||
        AfkService afkService)
 | 
			
		||||
    {
 | 
			
		||||
        _client = client;
 | 
			
		||||
        _coord = coord;
 | 
			
		||||
@@ -60,6 +63,7 @@ public partial class Utility : NadekoModule
 | 
			
		||||
        _httpFactory = httpFactory;
 | 
			
		||||
        _veService = veService;
 | 
			
		||||
        _services = services;
 | 
			
		||||
        _afkService = afkService;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
@@ -99,7 +103,7 @@ public partial class Utility : NadekoModule
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    [RequireContext(ContextType.Guild)]
 | 
			
		||||
    public async Task WhosPlaying([Leftover] string game)
 | 
			
		||||
    public async Task WhosPlaying([Leftover] string? game)
 | 
			
		||||
    {
 | 
			
		||||
        game = game?.Trim().ToUpperInvariant();
 | 
			
		||||
        if (string.IsNullOrWhiteSpace(game))
 | 
			
		||||
@@ -140,7 +144,7 @@ public partial class Utility : NadekoModule
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    [RequireContext(ContextType.Guild)]
 | 
			
		||||
    [Priority(0)]
 | 
			
		||||
    public async Task InRole(int page, [Leftover] IRole role = null)
 | 
			
		||||
    public async Task InRole(int page, [Leftover] IRole? role = null)
 | 
			
		||||
    {
 | 
			
		||||
        if (--page < 0)
 | 
			
		||||
            return;
 | 
			
		||||
@@ -178,7 +182,7 @@ public partial class Utility : NadekoModule
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    [RequireContext(ContextType.Guild)]
 | 
			
		||||
    [Priority(1)]
 | 
			
		||||
    public Task InRole([Leftover] IRole role = null)
 | 
			
		||||
    public Task InRole([Leftover] IRole? role = null)
 | 
			
		||||
        => InRole(1, role);
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
@@ -218,7 +222,7 @@ public partial class Utility : NadekoModule
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    [RequireContext(ContextType.Guild)]
 | 
			
		||||
    public async Task UserId([Leftover] IGuildUser target = null)
 | 
			
		||||
    public async Task UserId([Leftover] IGuildUser? target = null)
 | 
			
		||||
    {
 | 
			
		||||
        var usr = target ?? ctx.User;
 | 
			
		||||
        await Response()
 | 
			
		||||
@@ -248,7 +252,7 @@ public partial class Utility : NadekoModule
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    [RequireContext(ContextType.Guild)]
 | 
			
		||||
    public async Task Roles(IGuildUser target, int page = 1)
 | 
			
		||||
    public async Task Roles(IGuildUser? target, int page = 1)
 | 
			
		||||
    {
 | 
			
		||||
        var guild = ctx.Guild;
 | 
			
		||||
 | 
			
		||||
@@ -301,7 +305,7 @@ public partial class Utility : NadekoModule
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    [RequireContext(ContextType.Guild)]
 | 
			
		||||
    public async Task ChannelTopic([Leftover] ITextChannel channel = null)
 | 
			
		||||
    public async Task ChannelTopic([Leftover] ITextChannel? channel = null)
 | 
			
		||||
    {
 | 
			
		||||
        if (channel is null)
 | 
			
		||||
            channel = (ITextChannel)ctx.Channel;
 | 
			
		||||
@@ -382,7 +386,7 @@ public partial class Utility : NadekoModule
 | 
			
		||||
    [BotPerm(GuildPerm.ManageEmojisAndStickers)]
 | 
			
		||||
    [UserPerm(GuildPerm.ManageEmojisAndStickers)]
 | 
			
		||||
    [Priority(0)]
 | 
			
		||||
    public async Task EmojiAdd(string name, string url = null)
 | 
			
		||||
    public async Task EmojiAdd(string name, string? url = null)
 | 
			
		||||
    {
 | 
			
		||||
        name = name.Trim(':');
 | 
			
		||||
 | 
			
		||||
@@ -456,10 +460,10 @@ public partial class Utility : NadekoModule
 | 
			
		||||
    [RequireContext(ContextType.Guild)]
 | 
			
		||||
    [BotPerm(GuildPerm.ManageEmojisAndStickers)]
 | 
			
		||||
    [UserPerm(GuildPerm.ManageEmojisAndStickers)]
 | 
			
		||||
    public async Task StickerAdd(string name = null, string description = null, params string[] tags)
 | 
			
		||||
    public async Task StickerAdd(string? name = null, string? description = null, params string[] tags)
 | 
			
		||||
    {
 | 
			
		||||
        string format;
 | 
			
		||||
        Stream stream = null;
 | 
			
		||||
        Stream? stream = null;
 | 
			
		||||
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
@@ -696,6 +700,19 @@ public partial class Utility : NadekoModule
 | 
			
		||||
            await Response().Confirm(strs.verbose_errors_disabled).SendAsync();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    public async Task Afk([Leftover] string text = "No reason specified.")
 | 
			
		||||
    {
 | 
			
		||||
        var succ = await _afkService.SetAfkAsync(ctx.User.Id, text);
 | 
			
		||||
 | 
			
		||||
        if (succ)
 | 
			
		||||
        {
 | 
			
		||||
            await Response()
 | 
			
		||||
                  .Confirm(strs.afk_set)
 | 
			
		||||
                  .SendAsync();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    [NoPublicBot]
 | 
			
		||||
    [OwnerOnly]
 | 
			
		||||
 
 | 
			
		||||
@@ -1410,4 +1410,6 @@ honeypot:
 | 
			
		||||
coins:
 | 
			
		||||
  - coins
 | 
			
		||||
  - crypto
 | 
			
		||||
  - cryptos
 | 
			
		||||
  - cryptos
 | 
			
		||||
afk:
 | 
			
		||||
  - afk
 | 
			
		||||
@@ -4577,4 +4577,15 @@ coins:
 | 
			
		||||
    - '2'
 | 
			
		||||
  params:
 | 
			
		||||
    - page:
 | 
			
		||||
        desc: "Page number to show. Starts at 1."
 | 
			
		||||
        desc: "Page number to show. Starts at 1."
 | 
			
		||||
afk:
 | 
			
		||||
  desc: |-
 | 
			
		||||
    Toggles AFK status for yourself with the specified message. 
 | 
			
		||||
    If you don't provide a message it default to a generic one.
 | 
			
		||||
    Anyone @ mentioning you in any server will receive the afk message.
 | 
			
		||||
    This will only work if the other user's message starts with the mention.
 | 
			
		||||
  ex:
 | 
			
		||||
    - ''
 | 
			
		||||
  params:
 | 
			
		||||
    - msg:
 | 
			
		||||
        desc: "The message to send when someone pings you."
 | 
			
		||||
@@ -1104,5 +1104,6 @@
 | 
			
		||||
  "queue_search_results": "Type the number of the search result to queue up that track.",
 | 
			
		||||
  "overloads": "Overloads",
 | 
			
		||||
  "honeypot_on": "Honeypot enabled on this channel." ,
 | 
			
		||||
  "honeypot_off": "Honeypot disabled."
 | 
			
		||||
  "honeypot_off": "Honeypot disabled.",
 | 
			
		||||
  "afk_set": "AFK message set. Type a message in any channel to clear."
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user