How to send a message to a Discord Channel every X seconds?

1.5k views Asked by At

I created a Discord Bot where I can send text commands. For example, typing !start, would send the message that has the start parameter set. The bot works, I can send the commands, I'm just having a hard time figuring out how to set the timer and tie it to the command. Maybe setting it up as a command is the wrong way to go about it, but I can't figure out another way to send a message.

Here is my code so far. I've looked in several places online. Many resources say I need to include the System.Timer using, which I have. I'm just having a hard time figuring out how to tie the timer, the command itself, and which channel it goes into, all together. Any help would be appreciated.

using Discord.Commands;
using System.Timers;

namespace MessageTimerBot.Modules
{

    public class Commands : ModuleBase<SocketCommandContext>
    {
        [Command("start")]
        public async Task StartMessageTimer()
        {

        }

        public class MessageTimer
        {

            private static System.Timers.Timer messageTimer;

            public static void StartTimer()
            {
                messageTimer = new System.Timers.Timer(30000);
                messageTimer.Elapsed += OnTimerElapsed;
                messageTimer.AutoReset = true;
                messageTimer.Enabled = true;
            }

            public static void OnTimerElapsed(object source, ElapsedEventArgs e)
            {
                Console.WriteLine("Test Message");
            }
        }
    }
}
2

There are 2 answers

0
Der_Floh On

If I'm understanding it correctly you just need to make your OnTimerElapsed method async:

public static async void OnTimerElapsed(...)

Then create a global variable SocketCommandContext _Context; in your MessageTimer Class, add the attribute SocketCommandContext context to the StartTime method and set _Context to context

And then Call await _Context.Channel.SendMessageAsync("Test Message"); in your OnTimerElapsed method:

public static Class MessageTimer
{
   private static System.Timers.Timer messageTimer;
   private static SocketCommandContext _Context;

   public static void StartTimer(SocketCommandContext context)
   {
      _Context = context;

      messageTimer = new System.Timers.Timer(30000);
      messageTimer.Elapsed += OnTimerElapsed;
      messageTimer.AutoReset = true;
      messageTimer.Enabled = true;
   }

   public static void OnTimerElapsed(object source, ElapsedEventArgs e)
   {
      _Context.Channel.SendMessageAsync("Test Message");
      Console.WriteLine("Test Message");
   }
}

Now you just need to call MessageTimer.StartTimer(Context); in your start command

public class Commands : ModuleBase<SocketCommandContext>
{
   [Command("start")]
   public async Task StartMessageTimer()
   {
      MessageTimer.StartTimer(Context);
   }
}
0
NotNameless On

I followed this example to set up my automated message system. It still works on the latest version of Discord.NET (v 3.6.1 as of the date this post goes up)

using System;
using System.Threading; // 1) Add this namespace
using System.Threading.Tasks;
using Discord.Commands;

public class TimerService
{
    private readonly Timer _timer; // 2) Add a field like this
    // This example only concerns a single timer.
    // If you would like to have multiple independant timers,
    // you could use a collection such as List<Timer>,
    // or even a Dictionary<string, Timer> to quickly get
    // a specific Timer instance by name.

    public TimerService(DiscordSocketClient client)
    {
        _timer = new Timer(async _ =>
        {
            // 3) Any code you want to periodically run goes here, for example:
            var chan = client.GetChannel(knownId) as IMessageChannel;
            if (chan != null)
                await chan.SendMessageAsync("hi");
        },
        null,
        TimeSpan.FromMinutes(10),  // 4) Time that message should fire after the timer is created
        TimeSpan.FromMinutes(30)); // 5) Time after which message should repeat (use `Timeout.Infinite` for no repeat)
    }

    public void Stop() // 6) Example to make the timer stop running
    {
        _timer.Change(Timeout.Infinite, Timeout.Infinite);
    }

    public void Restart() // 7) Example to restart the timer
    {
        _timer.Change(TimeSpan.FromMinutes(10), TimeSpan.FromMinutes(30));
    }
}

public class TimerModule : ModuleBase
{
    private readonly TimerService _service;

    public TimerModule(TimerService service) // Make sure to configure your DI with your TimerService instance
    {
        _service = service;
    }

    // Example commands
    [Command("stoptimer")]
    public async Task StopCmd()
    {
        _service.Stop();
        await ReplyAsync("Timer stopped.");
    }

    [Command("starttimer")]
    public async Task RestartCmd()
    {
        _service.Restart();
        await ReplyAsync("Timer (re)started.");
    }
}

You would need to set up Dependency Injection in your startup file for this to work correctly. If you don't know how to do that, the Discord.NET and Microsoft docs should give you a good idea of what to do.