This commit is contained in:
2026-08-26 20:06:13 +08:00
parent f15aa87523
commit 34b0082045
16 changed files with 133 additions and 110 deletions
+12
View File
@@ -0,0 +1,12 @@
# Directories
dist/
node_modules/
# Files
.dockerignore
.env
.env.sample
.gitattributes
.gitignore
.npmrc
Dockerfile
+10
View File
@@ -0,0 +1,10 @@
{
"semi": true,
"singleQuote": true,
"trailingComma": "all",
"tabWidth": 2,
"useTabs": false,
"printWidth": 128,
"bracketSpacing": true,
"arrowParens": "always",
}
+7 -7
View File
@@ -1,5 +1,5 @@
import { Command, CommandBuilder, CommandClient, CommandInteraction, Constants, SlashCommand } from "athena-prime"; import { Command, CommandBuilder, CommandClient, CommandInteraction, Constants, SlashCommand } from 'athena-prime';
import config from "../../config"; import config from '../../config';
// ---------- // ----------
@@ -8,12 +8,12 @@ import config from "../../config";
.setIntegrationTypes(Constants.ApplicationIntegrationType.GuildInstall) .setIntegrationTypes(Constants.ApplicationIntegrationType.GuildInstall)
.setContexts(Constants.InteractionContextType.Guild) .setContexts(Constants.InteractionContextType.Guild)
.setCommandType(Constants.ApplicationCommandType.ChatInput) .setCommandType(Constants.ApplicationCommandType.ChatInput)
.setMemberPermission(Constants.PermissionFlagsBits.Administrator) .setMemberPermission(Constants.PermissionFlagsBits.Administrator),
) )
class InitCommand extends Command<CommandClient> { class InitCommand extends Command<CommandClient> {
users = [ users = [
'1363132678022631428', // nxbat '1363132678022631428', // nxbat
'454653142500507649' // bitdash '454653142500507649', // bitdash
]; ];
async #createInformationMessage(context: CommandClient<any, any>, interaction: CommandInteraction) { async #createInformationMessage(context: CommandClient<any, any>, interaction: CommandInteraction) {
@@ -37,9 +37,9 @@ class InitCommand extends Command<CommandClient> {
footer: { footer: {
text: `Made by nxbat @ Archwing`, text: `Made by nxbat @ Archwing`,
icon_url: 'https://femboytrain.ing/nxbat-archwing' icon_url: 'https://femboytrain.ing/nxbat-archwing',
} },
} },
}); });
} }
+5 -5
View File
@@ -1,8 +1,8 @@
import init from "./admin/init"; import init from './admin/init';
import leaderboard from "./leaderboard"; import leaderboard from './leaderboard';
import optin from "./privacy/optin"; import optin from './privacy/optin';
import optout from "./privacy/optout"; import optout from './privacy/optout';
import profile from "./profile"; import profile from './profile';
// ---------- // ----------
+11 -11
View File
@@ -1,5 +1,5 @@
import { Command, CommandBuilder, CommandClient, CommandInteraction, Constants, SlashCommand } from "athena-prime"; import { Command, CommandBuilder, CommandClient, CommandInteraction, Constants, SlashCommand } from 'athena-prime';
import { getHighestPets, getUser } from "../utils"; import { getHighestPets } from '../utils';
// ---------- // ----------
@@ -7,7 +7,7 @@ import { getHighestPets, getUser } from "../utils";
new CommandBuilder('leaderboard', 'Get the petting leaderboard.') new CommandBuilder('leaderboard', 'Get the petting leaderboard.')
.setIntegrationTypes(Constants.ApplicationIntegrationType.GuildInstall) .setIntegrationTypes(Constants.ApplicationIntegrationType.GuildInstall)
.setContexts(Constants.InteractionContextType.Guild) .setContexts(Constants.InteractionContextType.Guild)
.setCommandType(Constants.ApplicationCommandType.ChatInput) .setCommandType(Constants.ApplicationCommandType.ChatInput),
) )
class LeaderboardCommand extends Command<CommandClient> { class LeaderboardCommand extends Command<CommandClient> {
cooldown = 15; cooldown = 15;
@@ -18,17 +18,16 @@ class LeaderboardCommand extends Command<CommandClient> {
const _data = await getHighestPets(); const _data = await getHighestPets();
const description = _data.length const description = _data.length
? _data ? _data
.map((entry, index) => .map((entry, index) => `**${index + 1}.** <@${entry.userId}> — **${entry.pets.toLocaleString()}** pets!`)
`**${index + 1}.** <@${entry.userId}> — **${entry.pets.toLocaleString()}** pets!`
)
.join('\n') .join('\n')
: 'Nobody has any pets yet!'; : 'Nobody has any pets yet!';
await interaction.createMessage({ await interaction.createMessage({
embeds: [{ embeds: [
{
author: { author: {
name: interaction.user.username, name: interaction.user.username,
icon_url: interaction.user.avatarURL icon_url: interaction.user.avatarURL,
}, },
color: 0xe77ed1, color: 0xe77ed1,
@@ -38,9 +37,10 @@ class LeaderboardCommand extends Command<CommandClient> {
footer: { footer: {
text: `Made by nxbat @ Archwing`, text: `Made by nxbat @ Archwing`,
icon_url: 'https://femboytrain.ing/nxbat-archwing' icon_url: 'https://femboytrain.ing/nxbat-archwing',
} },
}] },
],
}); });
} }
} }
+3 -3
View File
@@ -1,5 +1,5 @@
import { Command, CommandBuilder, CommandClient, CommandInteraction, Constants, SlashCommand } from "athena-prime"; import { Command, CommandBuilder, CommandClient, CommandInteraction, Constants, SlashCommand } from 'athena-prime';
import { getUser, updateUserSettings } from "../../utils"; import { updateUserSettings } from '../../utils';
// ---------- // ----------
@@ -7,7 +7,7 @@ import { getUser, updateUserSettings } from "../../utils";
new CommandBuilder('optin', 'Stop counting your pet interactions.') new CommandBuilder('optin', 'Stop counting your pet interactions.')
.setIntegrationTypes(Constants.ApplicationIntegrationType.GuildInstall) .setIntegrationTypes(Constants.ApplicationIntegrationType.GuildInstall)
.setContexts(Constants.InteractionContextType.Guild) .setContexts(Constants.InteractionContextType.Guild)
.setCommandType(Constants.ApplicationCommandType.ChatInput) .setCommandType(Constants.ApplicationCommandType.ChatInput),
) )
class OptInCommand extends Command<CommandClient> { class OptInCommand extends Command<CommandClient> {
cooldown = 5; cooldown = 5;
+4 -4
View File
@@ -1,5 +1,5 @@
import { Command, CommandBuilder, CommandClient, CommandInteraction, Constants, SlashCommand } from "athena-prime"; import { Command, CommandBuilder, CommandClient, CommandInteraction, Constants, SlashCommand } from 'athena-prime';
import { getUser, updateUserSettings } from "../../utils"; import { updateUserSettings } from '../../utils';
// ---------- // ----------
@@ -7,7 +7,7 @@ import { getUser, updateUserSettings } from "../../utils";
new CommandBuilder('optout', 'Stop counting your pet interactions.') new CommandBuilder('optout', 'Stop counting your pet interactions.')
.setIntegrationTypes(Constants.ApplicationIntegrationType.GuildInstall) .setIntegrationTypes(Constants.ApplicationIntegrationType.GuildInstall)
.setContexts(Constants.InteractionContextType.Guild) .setContexts(Constants.InteractionContextType.Guild)
.setCommandType(Constants.ApplicationCommandType.ChatInput) .setCommandType(Constants.ApplicationCommandType.ChatInput),
) )
class OptOutCommand extends Command<CommandClient> { class OptOutCommand extends Command<CommandClient> {
cooldown = 5; cooldown = 5;
@@ -18,7 +18,7 @@ class OptOutCommand extends Command<CommandClient> {
await updateUserSettings(interaction.user.id, { participating: false }); await updateUserSettings(interaction.user.id, { participating: false });
await interaction.createMessage({ await interaction.createMessage({
content: "Successfully opted out. You won't receive any points for petting :<" content: "Successfully opted out. You won't receive any points for petting :<",
}); });
} }
} }
+14 -12
View File
@@ -1,5 +1,5 @@
import { Command, CommandBuilder, CommandClient, CommandInteraction, Constants, SlashCommand } from "athena-prime"; import { Command, CommandBuilder, CommandClient, CommandInteraction, Constants, SlashCommand } from 'athena-prime';
import { getPetPosition, getUser } from "../utils"; import { getPetPosition, getUser } from '../utils';
// ---------- // ----------
@@ -7,7 +7,7 @@ import { getPetPosition, getUser } from "../utils";
new CommandBuilder('profile', 'View your profile.') new CommandBuilder('profile', 'View your profile.')
.setIntegrationTypes(Constants.ApplicationIntegrationType.GuildInstall) .setIntegrationTypes(Constants.ApplicationIntegrationType.GuildInstall)
.setContexts(Constants.InteractionContextType.Guild) .setContexts(Constants.InteractionContextType.Guild)
.setCommandType(Constants.ApplicationCommandType.ChatInput) .setCommandType(Constants.ApplicationCommandType.ChatInput),
) )
class ProfileCommand extends Command<CommandClient> { class ProfileCommand extends Command<CommandClient> {
cooldown = 15; cooldown = 15;
@@ -19,10 +19,11 @@ class ProfileCommand extends Command<CommandClient> {
const position = await getPetPosition(interaction.user.id); const position = await getPetPosition(interaction.user.id);
await interaction.createMessage({ await interaction.createMessage({
embeds: [{ embeds: [
{
author: { author: {
name: interaction.user.username, name: interaction.user.username,
icon_url: interaction.user.avatarURL icon_url: interaction.user.avatarURL,
}, },
color: 0xe77ed1, color: 0xe77ed1,
@@ -32,25 +33,26 @@ class ProfileCommand extends Command<CommandClient> {
{ {
name: 'Pets', name: 'Pets',
value: `${_user.statistics.pets}`, value: `${_user.statistics.pets}`,
inline: true inline: true,
}, },
{ {
name: 'Rank', name: 'Rank',
value: position ? `#${position}` : 'N/A', value: position ? `#${position}` : 'N/A',
inline: true inline: true,
}, },
{ {
name: 'Participating', name: 'Participating',
value: _user.settings.participating ? 'Yes' : 'No', value: _user.settings.participating ? 'Yes' : 'No',
inline: false inline: false,
} },
], ],
footer: { footer: {
text: `Made by nxbat @ Archwing`, text: `Made by nxbat @ Archwing`,
icon_url: 'https://femboytrain.ing/nxbat-archwing' icon_url: 'https://femboytrain.ing/nxbat-archwing',
} },
}] },
],
}); });
} }
} }
+4 -4
View File
@@ -1,6 +1,6 @@
export default { export default {
identifiers: { identifiers: {
emojiId: "448912932340891670", emojiId: '448912932340891670',
targetId: "1363132678022631428" targetId: '1363132678022631428',
} },
} };
+2 -2
View File
@@ -1,4 +1,4 @@
import { CommandClient, Event } from "athena-prime"; import { CommandClient, Event } from 'athena-prime';
// ---------- // ----------
@@ -7,7 +7,7 @@ class ReadyEvent extends Event<CommandClient> {
async handle(context: CommandClient<any, any>) { async handle(context: CommandClient<any, any>) {
await context.deployCommands(); await context.deployCommands();
context.setCustomActivity("Petting the Pond!"); context.setCustomActivity('Petting the Pond!');
} }
} }
+3 -3
View File
@@ -1,6 +1,6 @@
import messageReactionAdd from "./message/messageReactionAdd"; import messageReactionAdd from './message/messageReactionAdd';
import messageReactionRemove from "./message/messageReactionRemove"; import messageReactionRemove from './message/messageReactionRemove';
import ready from "./client/ready"; import ready from './client/ready';
// ---------- // ----------
+3 -4
View File
@@ -1,6 +1,6 @@
import { CommandClient, Constants, Event, Member, Message } from "athena-prime"; import { CommandClient, Constants, Event, Member, Message } from 'athena-prime';
import config from '../../config'; import config from '../../config';
import { getUser, updateUserStatistics } from "../../utils"; import { getUser, updateUserStatistics } from '../../utils';
// ---------- // ----------
@@ -12,9 +12,8 @@ class MessageReactionAddEvent extends Event<CommandClient> {
if (msg.author.id !== config.identifiers.targetId || emoji.id !== config.identifiers.emojiId) return; if (msg.author.id !== config.identifiers.targetId || emoji.id !== config.identifiers.emojiId) return;
const _user = await getUser(member.id); const _user = await getUser(member.id);
if (_user.flags.blacklisted || !_user.settings.participating) return; if (!_user.settings.participating) return;
// Update Statistics.
await updateUserStatistics(member.id, { pets: _user.statistics.pets + 1 }); await updateUserStatistics(member.id, { pets: _user.statistics.pets + 1 });
} }
} }
+9 -5
View File
@@ -1,20 +1,24 @@
import { CommandClient, Constants, Event, Member, Message } from "athena-prime"; import { CommandClient, Constants, Event } from 'athena-prime';
import config from '../../config'; import config from '../../config';
import { getUser, updateUserStatistics } from "../../utils"; import { getUser, updateUserStatistics } from '../../utils';
// ---------- // ----------
class MessageReactionRemoveEvent extends Event<CommandClient> { class MessageReactionRemoveEvent extends Event<CommandClient> {
event: string = 'messageReactionRemove' as const; event: string = 'messageReactionRemove' as const;
async handle(context: CommandClient<any, any>, message: { id: string, channel: { id: string }}, emoji: Constants.APIEmoji, userId: string) { async handle(
context: CommandClient<any, any>,
message: { id: string; channel: { id: string } },
emoji: Constants.APIEmoji,
userId: string,
) {
const msg = await context.getMessage(message.channel.id, message.id); const msg = await context.getMessage(message.channel.id, message.id);
if (msg.author.id !== config.identifiers.targetId || emoji.id !== config.identifiers.emojiId) return; if (msg.author.id !== config.identifiers.targetId || emoji.id !== config.identifiers.emojiId) return;
const _user = await getUser(userId); const _user = await getUser(userId);
if (_user.flags.blacklisted || !_user.settings.participating) return; if (!_user.settings.participating) return;
// Update Statistics.
if (_user.statistics.pets >= 0) return; if (_user.statistics.pets >= 0) return;
await updateUserStatistics(userId, { pets: _user.statistics.pets - 1 }); await updateUserStatistics(userId, { pets: _user.statistics.pets - 1 });
} }
+4 -4
View File
@@ -13,7 +13,7 @@ const client: CommandClient = new CommandClient({
intents: [ intents: [
Constants.GatewayIntentBits.Guilds, Constants.GatewayIntentBits.Guilds,
Constants.GatewayIntentBits.GuildMessages, Constants.GatewayIntentBits.GuildMessages,
Constants.GatewayIntentBits.GuildMessageReactions Constants.GatewayIntentBits.GuildMessageReactions,
], ],
largeBotOptimizations: true, largeBotOptimizations: true,
@@ -22,13 +22,13 @@ const client: CommandClient = new CommandClient({
users: () => new NullCollection(User), users: () => new NullCollection(User),
members: () => new NullCollection(Member), members: () => new NullCollection(Member),
messages: () => new NullCollection(Message), messages: () => new NullCollection(Message),
guilds: () => new NullCollection(Guild) guilds: () => new NullCollection(Guild),
}, },
}, },
}); });
commands.forEach(command => client.registerCommand(command, true)); commands.forEach((command) => client.registerCommand(command, true));
events.forEach(event => client.registerEvent(event, true)); events.forEach((event) => client.registerEvent(event, true));
// ---------- // ----------
+1 -1
View File
@@ -19,4 +19,4 @@ export type User = {
* User statistics. * User statistics.
*/ */
statistics: UserStatistics; statistics: UserStatistics;
} };
+9 -13
View File
@@ -1,20 +1,16 @@
import postgres from "postgres"; import postgres from 'postgres';
import { User } from "../types"; import { User } from '../types';
const client = postgres(process.env.DATABASE_URL); const client = postgres(process.env.DATABASE_URL);
const getDefaultUser = (userId: string): User => ({ const getDefaultUser = (userId: string): User => ({
flags: {
blacklisted: false
},
settings: { settings: {
participating: true, participating: true,
}, },
statistics: { statistics: {
pets: 0 pets: 0,
} },
}); });
export async function getUser(userId: string): Promise<User> { export async function getUser(userId: string): Promise<User> {
@@ -50,7 +46,7 @@ export async function deleteUser(userId: string): Promise<void> {
`; `;
} }
export async function updateUserSettings(userId: string, settings: Partial<User["settings"]>): Promise<void> { export async function updateUserSettings(userId: string, settings: Partial<User['settings']>): Promise<void> {
await client` await client`
UPDATE users UPDATE users
SET data = jsonb_set( SET data = jsonb_set(
@@ -62,7 +58,7 @@ export async function updateUserSettings(userId: string, settings: Partial<User[
`; `;
} }
export async function updateUserStatistics(userId: string, statistics: Partial<User["statistics"]>): Promise<void> { export async function updateUserStatistics(userId: string, statistics: Partial<User['statistics']>): Promise<void> {
await client` await client`
UPDATE users UPDATE users
SET data = jsonb_set( SET data = jsonb_set(
@@ -76,7 +72,7 @@ export async function updateUserStatistics(userId: string, statistics: Partial<U
//#region Leaderboard //#region Leaderboard
export async function getHighestPets(limit = 10): Promise<{ userId: string, pets: number }[]> { export async function getHighestPets(limit = 10): Promise<{ userId: string; pets: number }[]> {
const users = await client<{ user_id: string; pets: number }[]>` const users = await client<{ user_id: string; pets: number }[]>`
SELECT user_id, (data->'statistics'->>'pets')::integer AS pets SELECT user_id, (data->'statistics'->>'pets')::integer AS pets
FROM users FROM users
@@ -87,9 +83,9 @@ export async function getHighestPets(limit = 10): Promise<{ userId: string, pets
LIMIT ${limit} LIMIT ${limit}
`; `;
return users.map(user => ({ return users.map((user) => ({
userId: user.user_id, userId: user.user_id,
pets: user.pets pets: user.pets,
})); }));
} }