discord-tickets / bot

The most popular open-source and self-hosted ticket management bot for Discord - a free alternative to the premium and white-label plans of other popular ticketing bots.
https://discordtickets.app
GNU General Public License v3.0
880 stars 459 forks source link

fix: fix activity name update #503

Closed VinDotRun closed 8 months ago

VinDotRun commented 8 months ago

Versioning information

Is this related to an issue?

No.

Changes made

In the client/ready.js listener, the activities object was being referenced instead of copied. This caused the activities' names to be set on bot startup, and they would not change even after the 15 minutes cache TTL expired. I have changed it to create a shallow copy of the client.config.presence.activities[next] object using the spread operator, instead of making a reference to the original object.

Confirmations

eartharoid commented 8 months ago

Can you explain more about what the problem is? client.config.presence.activities shouldn't change so I'm not sure how this change makes a difference.

VinDotRun commented 8 months ago

If the amount of open tickets, total tickets, average response time or average resolution time changes, the activity will not be updated with the new information, even after the 15 minutes TTL of the cache. This happens because here the client.config.presence.activities is being referenced, instead of copied. When the replace() method is called for that activity, it replaces the original object.

When the setPresence() function is executed after its first execution, the activity.name property is no longer {openTickets} because it has been replaced on the first execution, so there is nothing to replace anymore.

You can use this code to reproduce the issue:

        const setPresence = async () => {
            const cacheKey = 'cache/presence';
            let cached = await client.keyv.get(cacheKey);
            if (!cached) {
                const tickets = await client.prisma.ticket.findMany({
                    select: {
                        closedAt: true,
                        createdAt: true,
                        firstResponseAt: true,
                    },
                });
                const closedTicketsWithResponse = tickets.filter(t => t.firstResponseAt && t.closedAt);
                const closedTickets = tickets.filter(t => t.closedAt);
                cached = {
                    avgResolutionTime: ms(getAvgResolutionTime(closedTicketsWithResponse)),
                    avgResponseTime: ms(getAvgResponseTime(closedTicketsWithResponse)),
                    openTickets: tickets.length - closedTickets.length,
                    totalTickets: tickets.length,
                };
                await client.keyv.set(cacheKey, cached, ms('15m'));
            }
            const activity = client.config.presence.activities[next];

            client.log.debug('Activity name before replace: ' + activity.name);

            activity.name = activity.name
                .replace(/{+avgResolutionTime}+/gi, cached.avgResolutionTime)
                .replace(/{+avgResponseTime}+/gi, cached.avgResponseTime)
                .replace(/{+openTickets}+/gi, cached.openTickets)
                .replace(/{+totalTickets}+/gi, cached.totalTickets);

            client.log.debug('Activity name after replace: ' + activity.name);

            client.user.setPresence({
                activities: [activity],
                status: client.config.presence.status,
            });
            next++;
            if (next === client.config.presence.activities.length) next = 0;
        };

When the bot starts up, it will print:

{openTickets} tickets
5 tickets

When the function runs again, it will print:

5 tickets
5 tickets

This article may help understand what's happening: https://javascript.info/object-copy

eartharoid commented 8 months ago

Ohhhh I didn't notice the activity.name = activity.name 🤦‍♂️