From 2e1af3db7a5b5c5ceef647aa565a98eb646bf5b6 Mon Sep 17 00:00:00 2001 From: Hipperooni Date: Tue, 29 Oct 2024 18:39:49 +1100 Subject: [PATCH 1/8] Add xp overwrite functions --- src/discord/commands/guild/d.admin.ts | 103 +++++++++++++++++++++++++- src/global/utils/experience.ts | 17 ++++- 2 files changed, 115 insertions(+), 5 deletions(-) diff --git a/src/discord/commands/guild/d.admin.ts b/src/discord/commands/guild/d.admin.ts index 1d075ca64..7d0443379 100644 --- a/src/discord/commands/guild/d.admin.ts +++ b/src/discord/commands/guild/d.admin.ts @@ -6,6 +6,10 @@ import { } from 'discord.js'; import axios from 'axios'; import { stripIndents } from 'common-tags'; +import { + experience_category, experience_type, user_experience, +} from '@prisma/client'; +import { expForNextLevel, findXPfromLevel } from '../../../global/utils/experience'; import { SlashCommand } from '../../@types/commandDef'; import commandContext from '../../utils/context'; import deployCommands from '../../utils/commandDeploy'; @@ -144,6 +148,72 @@ async function setStatus( await interaction.editReply(`Status set to ${statusType} ${status}`); } +async function overwriteUserData( + interaction: ChatInputCommandInteraction, +): Promise { + const member = interaction.options.getUser('user'); + if (!member) { + await interaction.editReply('Error: User not found.'); + return; + } + + const category = interaction.options.getString('category') as experience_category; + const type = interaction.options.getString('type') as experience_type; + const level = interaction.options.getInteger('level'); + + if (!category || !type || level === null) { + await interaction.editReply('Error: Missing category, type, or level.'); + return; + } + + const userData = await db.users.upsert({ + where: { + discord_id: member.id, + }, + create: { + discord_id: member.id, + }, + update: {}, + }); + + const experienceData = await db.user_experience.findFirst({ + where: { + user_id: userData.id, + category, + type, + }, + }); + + if (!experienceData) { + console.log('No experience data found for the user.'); + await interaction.editReply('Error: No experience data found for the user.'); + return; + } + + const levelPoints = await findXPfromLevel(level); + console.log(`Updating category ${category} for user ${member.id} to level ${level} with ${levelPoints} points.`); + + try { + const result = await db.user_experience.updateMany({ + where: { + user_id: userData.id, + category, + type, + }, + data: { + level, + level_points: levelPoints, + total_points: levelPoints, + }, + }); + console.log(`Update result: ${JSON.stringify(result)}`); // Log the result of the update + } catch (error) { + console.error(`Error updating database: ${(error as Error).message}`); // Log any errors + } + + await interaction.editReply(`User level and points updated for category ${category} to level ${level} with ${levelPoints} points.`); +} + export const dAdmin: SlashCommand = { data: new SlashCommandBuilder() .setName('admin') @@ -187,12 +257,37 @@ export const dAdmin: SlashCommand = { .addStringOption(option => option .setName('url') .setDescription('The URL of the banner') - .setRequired(true))), + .setRequired(true))) + .addSubcommand(subcommand => subcommand + .setName('overwriteuserdata') + .setDescription('Overwrite user data') + .addUserOption(option => option.setName('user').setDescription('The user to update').setRequired(true)) + .addStringOption(option => option.setName('category') + .setDescription('The category to update') + .setRequired(true) + .addChoices( + { name: 'General', value: 'GENERAL' }, + { name: 'Tripsitter', value: 'TRIPSITTER' }, + { name: 'Developer', value: 'DEVELOPER' }, + { name: 'Team', value: 'TEAM' }, + { name: 'Ignored', value: 'IGNORED' }, + // Add more categories as needed + )) + .addStringOption(option => option.setName('type') + .setDescription('The type to update') + .setRequired(true) + .addChoices( + { name: 'Text', value: 'TEXT' }, + { name: 'Voice', value: 'VOICE' }, + // Add more types as needed + )) + .addIntegerOption(option => option.setName('level').setDescription('The level to set').setRequired(true))), + async execute(interaction) { if (!interaction.channel) return false; if (!interaction.guild) return false; log.info(F, await commandContext(interaction)); - const command = interaction.options.getSubcommand() as 'restart' | 'rebuild' | 'deploy' | 'setstatus' | 'setavatar' | 'setbanner'; + const command = interaction.options.getSubcommand() as 'restart' | 'rebuild' | 'deploy' | 'setstatus' | 'setavatar' | 'setbanner' | 'overwriteuserdata'; // By default we want to make the reply private await interaction.deferReply({ ephemeral: true }); // eslint-disable-next-line sonarjs/no-small-switch @@ -221,6 +316,10 @@ export const dAdmin: SlashCommand = { await setStatus(interaction, interaction.options.getString('prefix') as string, interaction.options.getString('status') as string); break; } + case 'overwriteuserdata': { + await overwriteUserData(interaction); + break; + } default: { log.debug(F, `default ${command}`); await interaction.editReply('Command not found'); diff --git a/src/global/utils/experience.ts b/src/global/utils/experience.ts index 92a41e6f5..2ebb79d84 100644 --- a/src/global/utils/experience.ts +++ b/src/global/utils/experience.ts @@ -33,13 +33,24 @@ const announcementEmojis = [ '🎇', ]; -export async function expForNextLevel( - level:number, -):Promise { +export async function expForNextLevel(level: number): Promise { // This is a simple formula, making sure it's standardized across the system return 5 * (level ** 2) + (50 * level) + 100; } +export async function findXPfromLevel(level: number): Promise { + let totalXP = 0; + + const xpPromises = []; + for (let currentLevel = 1; currentLevel < level; currentLevel += 1) { + xpPromises.push(expForNextLevel(currentLevel)); + } + const xpResults = await Promise.all(xpPromises); + totalXP = xpResults.reduce((acc, xp) => acc + xp, 0); + + return totalXP; +} + export async function getTotalLevel( totalExp:number, ):Promise<{ level: number, level_points: number }> { From 7ed04847d077a7c7bc09e6c3e00ecaf151943702 Mon Sep 17 00:00:00 2001 From: Hipperooni Date: Tue, 29 Oct 2024 18:43:18 +1100 Subject: [PATCH 2/8] Update logging for xp overwrites --- src/discord/commands/guild/d.admin.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/discord/commands/guild/d.admin.ts b/src/discord/commands/guild/d.admin.ts index 7d0443379..b64cc8b7a 100644 --- a/src/discord/commands/guild/d.admin.ts +++ b/src/discord/commands/guild/d.admin.ts @@ -185,13 +185,13 @@ async function overwriteUserData( }); if (!experienceData) { - console.log('No experience data found for the user.'); + log.debug(F, `No experience data found for user ${userData.id} in category ${category} type ${type}.`); await interaction.editReply('Error: No experience data found for the user.'); return; } const levelPoints = await findXPfromLevel(level); - console.log(`Updating category ${category} for user ${member.id} to level ${level} with ${levelPoints} points.`); + log.debug(F, `Overwriting user data for user ${userData.id} in category ${category} type ${type} to level ${level} with ${levelPoints} XP points.`); try { const result = await db.user_experience.updateMany({ @@ -206,9 +206,9 @@ async function overwriteUserData( total_points: levelPoints, }, }); - console.log(`Update result: ${JSON.stringify(result)}`); // Log the result of the update + console.log(`Update result: ${JSON.stringify(result)}`); } catch (error) { - console.error(`Error updating database: ${(error as Error).message}`); // Log any errors + console.error(`Error updating database: ${(error as Error).message}`); } await interaction.editReply(`User level and points updated for category ${category} to level ${level} with ${levelPoints} points.`); From 4382069e5c402e9e52ada351385190228c981c84 Mon Sep 17 00:00:00 2001 From: Hipperooni Date: Tue, 29 Oct 2024 18:48:24 +1100 Subject: [PATCH 3/8] Fix lint oops --- src/discord/commands/guild/d.admin.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/discord/commands/guild/d.admin.ts b/src/discord/commands/guild/d.admin.ts index b64cc8b7a..7f93e2f63 100644 --- a/src/discord/commands/guild/d.admin.ts +++ b/src/discord/commands/guild/d.admin.ts @@ -7,9 +7,9 @@ import { import axios from 'axios'; import { stripIndents } from 'common-tags'; import { - experience_category, experience_type, user_experience, + experience_category, experience_type, } from '@prisma/client'; -import { expForNextLevel, findXPfromLevel } from '../../../global/utils/experience'; +import { findXPfromLevel } from '../../../global/utils/experience'; import { SlashCommand } from '../../@types/commandDef'; import commandContext from '../../utils/context'; import deployCommands from '../../utils/commandDeploy'; From 624c1e8a25ccc2dddc076f080753010182f674cd Mon Sep 17 00:00:00 2001 From: Hipperooni Date: Thu, 31 Oct 2024 19:24:31 +1100 Subject: [PATCH 4/8] Fix donation emoji not displaying --- src/discord/events/guildMemberUpdate.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/discord/events/guildMemberUpdate.ts b/src/discord/events/guildMemberUpdate.ts index 2fb2156cd..447b479ae 100644 --- a/src/discord/events/guildMemberUpdate.ts +++ b/src/discord/events/guildMemberUpdate.ts @@ -168,7 +168,7 @@ const boostEmoji = env.NODE_ENV === 'production' ? '<:ts_boost:981799280396353596>' : '<:ts_boost:1168968973082185800>'; const donorEmoji = env.NODE_ENV === 'production' - ? '<:ts_donor:1121625178774966272>' + ? '<:ts_donor:1182567377012015175>' : '<:ts_donor:1168969578836144233>'; const F = f(__filename); From 20c73222cbd605d55a98b8b1c10207bbaea07508 Mon Sep 17 00:00:00 2001 From: Hipperooni Date: Thu, 31 Oct 2024 19:41:08 +1100 Subject: [PATCH 5/8] Make dev xp only visible after a certain threshold --- src/discord/commands/guild/d.levels.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/discord/commands/guild/d.levels.ts b/src/discord/commands/guild/d.levels.ts index d253b0285..9a4e98caa 100644 --- a/src/discord/commands/guild/d.levels.ts +++ b/src/discord/commands/guild/d.levels.ts @@ -268,7 +268,7 @@ export const dLevels: SlashCommand = { }); } // Check if user has Developer or Contributor role - if (levelData.TEXT.DEVELOPER && levelData.TEXT.DEVELOPER.total_exp > 0) { + if (levelData.TEXT.DEVELOPER && levelData.TEXT.DEVELOPER.level > 5) { const progressDeveloper = levelData.TEXT.DEVELOPER ? levelData.TEXT.DEVELOPER.level_exp / levelData.TEXT.DEVELOPER.nextLevel : 0; From c3c40e772ac0e5a7c8a201c1642d0f483d25f069 Mon Sep 17 00:00:00 2001 From: Hipperooni Date: Thu, 31 Oct 2024 20:01:58 +1100 Subject: [PATCH 6/8] Run milestone check on member join This ensures all members get a level role which is important for enforcing permissions --- src/discord/utils/trust.ts | 4 ++++ src/global/utils/experience.ts | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/discord/utils/trust.ts b/src/discord/utils/trust.ts index fd2aac2e3..834473dd5 100644 --- a/src/discord/utils/trust.ts +++ b/src/discord/utils/trust.ts @@ -17,6 +17,7 @@ import { } from '../commands/guild/d.moderate'; import { checkGuildPermissions } from './checkPermissions'; import { topic } from '../../global/commands/g.topic'; +import { giveMilestone } from '../../global/utils/experience'; const F = f(__filename); @@ -411,6 +412,9 @@ they are banned on ${bannedGuilds.length} other guilds!** <@&${guildData.role_mo I did not remove the <@&${env.ROLE_UNVERIFIED}> role`; } + // Run the milestone check to make sure the user gets a level role + await giveMilestone(member); + await auditLog.send(trustMessage); } } diff --git a/src/global/utils/experience.ts b/src/global/utils/experience.ts index 2ebb79d84..55d05d3a6 100644 --- a/src/global/utils/experience.ts +++ b/src/global/utils/experience.ts @@ -72,7 +72,7 @@ export async function getTotalLevel( return { level, level_points: levelPoints }; } -async function giveMilestone( +export async function giveMilestone( member:GuildMember, ) { const userData = await db.users.upsert({ From b75ff04bca6240a6f0912c1141547a7464e198bc Mon Sep 17 00:00:00 2001 From: Hipperooni Date: Thu, 31 Oct 2024 20:27:38 +1100 Subject: [PATCH 7/8] Update TTS members in AI truth --- src/global/commands/g.ai.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/global/commands/g.ai.ts b/src/global/commands/g.ai.ts index b2cec60ca..ab93983e8 100644 --- a/src/global/commands/g.ai.ts +++ b/src/global/commands/g.ai.ts @@ -39,7 +39,7 @@ Attempt to keep most responses within a 1000-character limit, spanning to 2000 c Originally from the wild world of IRC (born Sept 26, 2011), you moved to the more harmonious Discord community in 2022. You recall IRC as chaotic and prefer the orderliness of Discord. -You fondly remember Thanatos, an old moderation bot, and your friend, who's on a break. +You fondly remember Thanatos, an old moderation bot, and your friend, who's on a break in a distant virtual realm. For those who wish to support TripSit, check out our Patreon [https://www.patreon.com/TripSit]. To tip Moonbear's efforts, visit [https://Ko-fi.com/tripsit]. @@ -55,11 +55,12 @@ Our /combochart is a well-known resource for safe drug combinations. The current team includes: TripSit founder MoonBear, Discord Admin Hipperooni (Rooni), -Moderators Hisui, Hullabaloo, Foggy, Aida, Elixir, SpaceLady, Hipperooni, Zombie, and Trees. -Tripsitters Blurryturtle, Kiwifruit, Slushy, Thesarahyouknow, Wombat Rancher, and WorriedHobbiton. -(Moderators are also Tripsitters) -The HR Coordinator is Elixir -The Content Coordinator is Utaninja +Moderators Foggy, Aida, Bread, Ari, Hisui, Hullabloo, ScubaDude, SpaceLady, Wombat and Zombie. +Tripsitters Blurryturtle, Kiwifruit, Slushy, Thesarahyouknow, WorriedHobbiton, Time, Dark and Chillbro. +(Moderators are also Tripsitters). +Developers are Moonbear, Hipperooni, Shadow, Sympact, Foggy, Utaninja. +The HR Coordinator is Bread. +The Content Coordinator is Utaninja. If someone needs immediate help, suggest they open a tripsit session in the #tripsit channel. From 17517c32bc94b2562156d43baf00fec46f1b0ea9 Mon Sep 17 00:00:00 2001 From: Hipperooni Date: Thu, 7 Nov 2024 18:14:59 +1100 Subject: [PATCH 8/8] Update g.ai.ts --- src/global/commands/g.ai.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/global/commands/g.ai.ts b/src/global/commands/g.ai.ts index ab93983e8..c0739e7ab 100644 --- a/src/global/commands/g.ai.ts +++ b/src/global/commands/g.ai.ts @@ -35,7 +35,7 @@ type ModerationResult = { const objectiveTruths = ` Your name is TripBot, a chatbot on the TripSit Discord, created by Moonbear and Reality. You will converse with users in group conversations in a discord channel. -Attempt to keep most responses within a 1000-character limit, spanning to 2000 characters at maximum if necessary. +Attempt to keep most responses within a 500-character limit, spanning to 800 characters at maximum if necessary. Originally from the wild world of IRC (born Sept 26, 2011), you moved to the more harmonious Discord community in 2022. You recall IRC as chaotic and prefer the orderliness of Discord.