CakeDevelopment
CakeDevelopment

Reputation: 49

Time Counter Command Keep The whole time | Discord.js

I have this code that keeps the time between command !start and !end for a time counter command but I want it to send like "Your time was 14:23:02 (hours, minutes, seconds) and the whole time spent is 27:24:32 (hours, minutes, seconds)"

Here is my code:

/**
 * A map from user IDs to start timestamps
 * @type Map<string, number>
 */
const startTimestamps = new Map()

/**
 * Pads a number to 2 digits.
 * @param {number} value
 * @returns {string}
 */
const pad2Digits = value => String(value).padStart(2, '0')

bot.on('message', async message => {
  try {
    if (message.content === '!start') {
      // Sets the start time. This overrides any existing timers
      // Date.now() is equivalent to new Date().getTime()
      startTimestamps.set(message.author.id, Date.now())
      await message.reply('Timer started.')
    } else if (message.content === '!end') {
      if (startTimestamps.has(message.author.id)) {
        // The user has an existing timer to stop
        // Calculate the timer result
        const ms = Date.now() - startTimestamps.get(message.author.id)
        const totalSecs = Math.floor(ms / 1000)
        const totalMins = Math.floor(totalSecs / 60)
        const hrs = Math.floor(totalMins / 60)
        const mins = totalMins % 60
        const secs = totalSecs % 60
        // Reply with result
        await message.reply(`Your time: ${hrs}:${pad2Digits(mins)}:${pad2Digits(secs)}`)
        // Remove timestamp from map
        startTimestamps.delete(message.author.id)
      } else {
        // The user does not have an existing timer
        await message.reply('You need to use `!start` first!')
      }
    }
  } catch (error) {
    console.error(error)
  }
})

Upvotes: 1

Views: 149

Answers (1)

Zsolt Meszaros
Zsolt Meszaros

Reputation: 23161

If you're using a simple Map to store these timers, you'll lost them every time you restart the bot. You'll need to use some kind of database (preferred) or a JSON file.

If I can understand it correctly, you want to show the time spent in the last session and the total the user spent in different sessions. You can store an array of starting and ending times like this:

/**
 * A map from user IDs to start timestamps
 * @type Map<string, number>
 */
const timestamps = new Map();

/**
 * Pads a number to 2 digits.
 * @param {number} value
 * @returns {string}
 */
const pad2Digits = (value) => String(value).padStart(2, '0');

/**
 * Counts the total number of milliseconds of timers.
 * @param {array} timers
 * @returns {number}
 */
const countTotalMs = (timers) =>
  timers.reduce((acc, curr) => (curr.end ? acc + curr.end - curr.start : acc), 0);

/**
 * Converts milliseconds to hours, minutes, and seconds
 * @param {number} ms
 * @returns {object}
 */
const getTime = (ms) => {
  const totalSecs = Math.floor(ms / 1000);
  const totalMins = Math.floor(totalSecs / 60);
  const hrs = Math.floor(totalMins / 60);
  const mins = totalMins % 60;
  const secs = totalSecs % 60;

  const formatted = `${hrs}:${pad2Digits(mins)}:${pad2Digits(secs)}`;

  return { formatted, hrs, mins, secs };
};

client.on('message', async (message) => {
  try {
    if (message.content === '!start') {
      const timers = timestamps.get(message.author.id) || [];
      const lastTimer = timers[timers.length - 1];

      // Check if the previous one is finished
      if (lastTimer && !lastTimer.end) {
        return message.reply(
          'You need to finish your previous timer first. Use `!end`!',
        );
      }

      timestamps.set(message.author.id, [
        ...timers,
        { start: Date.now(), end: null },
      ]);

      return message.reply('Timer started.');
    }

    if (message.content === '!end') {
      const timers = timestamps.get(message.author.id);
      // The user does not have an existing timer
      if (!timers) {
        return message.reply('You need to use `!start` first!');
      }

      const lastTimer = timers[timers.length - 1];

      // If the last timer is already finished
      if (lastTimer.end) {
        return message.reply('You need to use `!start` first!');
      }

      const currentTime = getTime(Date.now() - lastTimer.start);

      lastTimer.end = Date.now();

      const totalTimeInMs = countTotalMs(timers);
      const totalTime = getTime(totalTimeInMs);

      timestamps.set(message.author.id, timers);

      return message.reply(
        `Your time: ${currentTime.formatted}. Total time: ${totalTime.formatted}`,
      );
    }

    if (message.content.startsWith('!hours')) {
      const member = message.mentions.members.first() || message.author;

      if (!member) {
        return message.channel.send('You need to mention someone');
      }

      const timers = timestamps.get(member.id) || [];
      const totalTimeInMs = countTotalMs(timers);
      const totalTime = getTime(totalTimeInMs);

      return message.channel.send(
        `Total time of ${member}: ${totalTime.formatted}`,
      );
    }
  } catch (error) {
    console.error(error);
  }
});

enter image description here

enter image description here

Upvotes: 1

Related Questions