1- import { MongoClient } from "mongodb" ;
2-
3- // ===== CONSTANTS =====
4-
5- const CONFIG = {
6- COLLECTION_NAMES : {
7- BANK : "bank" ,
8- BANK_AUDIT_LOG : "bank_audit_log" ,
9- BANK_SETTINGS : "bank_settings" ,
10- } ,
11- } ;
12-
13- const ERROR_MESSAGES = {
14- DATABASE_ERROR : "An error occurred while accessing the database." ,
15- } ;
16-
17- // ===== DATABASE UTILITIES =====
18-
19- async function connectToDatabase ( ) {
20- try {
21- const client = new MongoClient ( process . env . MONGODB_URI , {
22- retryWrites : true ,
23- writeConcern : "majority" ,
24- maxPoolSize : 10 ,
25- minPoolSize : 2 ,
26- maxIdleTimeMS : 30000 ,
27- serverSelectionTimeoutMS : 5000 ,
28- socketTimeoutMS : 45000 ,
29- } ) ;
30-
31- await client . connect ( ) ;
32- return client ;
33- } catch ( error ) {
34- console . error ( "Failed to connect to MongoDB:" , error ) ;
35- throw new Error ( "Database connection failed" ) ;
36- }
37- }
38-
39- async function withDatabase ( operation ) {
40- let client = null ;
41- try {
42- client = await connectToDatabase ( ) ;
43- return await operation ( client ) ;
44- } catch ( error ) {
45- console . error ( "Database operation failed:" , error ) ;
46- throw error ;
47- } finally {
48- if ( client ) {
49- try {
50- await client . close ( ) ;
51- } catch ( closeError ) {
52- console . error ( "Error closing database connection:" , closeError ) ;
53- }
54- }
55- }
56- }
57-
58- // ===== BANK UTILITIES =====
59-
60- async function migrateBankSettings ( client , channel ) {
61- const settingsCollection = client . db ( ) . collection ( CONFIG . COLLECTION_NAMES . BANK_SETTINGS ) ;
62-
63- // Check if settings already exist for this channel
64- const existingSettings = await settingsCollection . findOne ( { channel } ) ;
65- if ( existingSettings ) {
66- return ; // Already migrated or settings exist
67- }
68-
69- // Migrate from old bank_fees collection
70- try {
71- const bankFeesCollection = client . db ( ) . collection ( "bank_fees" ) ;
72- const oldFeeConfig = await bankFeesCollection . findOne ( { channel, currencySystem : "dnd" } ) ;
73-
74- // Migrate from old bank_decimal_currency_config collection
75- const decimalConfigCollection = client . db ( ) . collection ( "bank_decimal_currency_config" ) ;
76- const oldDecimalConfig = await decimalConfigCollection . findOne ( { channel, currencySystem : "decimal" } ) ;
77-
78- const newSettings = {
79- channel,
80- createdAt : new Date ( ) ,
81- updatedAt : new Date ( ) ,
82- } ;
83-
84- // Add DND settings if they exist
85- if ( oldFeeConfig ) {
86- newSettings . currencySystem = "dnd" ;
87- newSettings . dnd = {
88- feeRate : oldFeeConfig . feeRate ,
89- updatedAt : oldFeeConfig . updatedAt ,
90- updatedBy : oldFeeConfig . updatedBy ,
91- } ;
92- }
93-
94- // Add decimal settings if they exist
95- if ( oldDecimalConfig ) {
96- newSettings . currencySystem = oldDecimalConfig . currencySystem ;
97- newSettings . decimal = {
98- prefix : oldDecimalConfig . prefix ,
99- suffix : oldDecimalConfig . suffix ,
100- prefixSpaceAfter : oldDecimalConfig . prefixSpaceAfter ,
101- suffixSpaceBefore : oldDecimalConfig . suffixSpaceBefore ,
102- updatedAt : oldDecimalConfig . updatedAt ,
103- updatedBy : oldDecimalConfig . updatedBy ,
104- } ;
105- }
106-
107- // Insert the new settings document if there's anything to migrate
108- if ( oldFeeConfig || oldDecimalConfig ) {
109- await settingsCollection . insertOne ( newSettings ) ;
110- }
111- } catch ( error ) {
112- console . error ( "Error during bank settings migration:" , error ) ;
113- // Don't throw error, migration failure shouldn't block operations
114- }
115- }
116-
117- async function getDecimalCurrencyFormat ( client , channel ) {
118- const settingsCollection = client . db ( ) . collection ( CONFIG . COLLECTION_NAMES . BANK_SETTINGS ) ;
119-
120- // First, migrate any existing config documents from old collections
121- await migrateBankSettings ( client , channel ) ;
122-
123- const settings = await settingsCollection . findOne ( { channel, currencySystem : "decimal" } ) ;
124- return {
125- prefix : settings ?. decimal ?. prefix || "$" ,
126- suffix : settings ?. decimal ?. suffix || "" ,
127- prefixSpaceAfter : settings ?. decimal ?. prefixSpaceAfter || false ,
128- suffixSpaceBefore : settings ?. decimal ?. suffixSpaceBefore !== undefined ? settings . decimal . suffixSpaceBefore : true
129- } ;
130- }
131-
132- function formatDecimalCurrency ( amount , prefix = "$" , suffix = "" , prefixSpaceAfter = false , suffixSpaceBefore = true ) {
133- const formattedAmount = amount . toFixed ( 2 ) ;
134- const prefixPart = prefix + ( prefixSpaceAfter && prefix ? " " : "" ) ;
135- const suffixPart = ( suffixSpaceBefore && suffix ? " " : "" ) + suffix ;
136- return `${ prefixPart } ${ formattedAmount } ${ suffixPart } ` ;
137- }
138-
139- async function recordDecimalBankAuditLog ( client , channel , action , userId , username , details = { } ) {
140- try {
141- const auditCollection = client . db ( ) . collection ( CONFIG . COLLECTION_NAMES . BANK_AUDIT_LOG ) ;
142-
143- const logEntry = {
144- channel,
145- action,
146- userId,
147- username,
148- currencySystem : "decimal" ,
149- timestamp : new Date ( ) ,
150- details,
151- } ;
152-
153- await auditCollection . insertOne ( logEntry ) ;
154- } catch ( error ) {
155- console . error ( "Failed to record decimal bank audit log:" , error ) ;
156- }
157- }
158-
159- // ===== COMMAND HANDLER =====
160-
161- /**
162- * Handles decimal bank deposit subcommand
163- * @param {ChatInputCommandInteraction } interaction - Discord interaction
164- */
165- async function handleDecimalBankDeposit ( interaction ) {
166- try {
167- const amount = parseFloat ( interaction . options . getNumber ( "amount" ) ) ;
168-
169- if ( amount <= 0 ) {
170- await interaction . editReply ( "Amount must be greater than 0." ) ;
171- return ;
172- }
173-
174- await withDatabase ( async ( client ) => {
175- const collection = client . db ( ) . collection ( CONFIG . COLLECTION_NAMES . BANK ) ;
176- const format = await getDecimalCurrencyFormat ( client , interaction . channel . id ) ;
177-
178- const updateResult = await collection . findOneAndUpdate (
179- { channel : interaction . channel . id , currency : "decimal" , currencySystem : "decimal" } ,
180- {
181- $inc : { amount } ,
182- $setOnInsert : {
183- channel : interaction . channel . id ,
184- currency : "decimal" ,
185- currencySystem : "decimal" ,
186- createdAt : new Date ( ) ,
187- } ,
188- $set : { updatedAt : new Date ( ) } ,
189- } ,
190- { upsert : true , returnDocument : "after" }
191- ) ;
192-
193- const oldAmount = updateResult . amount - amount ;
194-
195- // Record audit log
196- await recordDecimalBankAuditLog (
197- client ,
198- interaction . channel . id ,
199- "deposit" ,
200- interaction . user . id ,
201- interaction . user . username ,
202- {
203- amount,
204- oldAmount,
205- newAmount : updateResult . amount ,
206- }
207- ) ;
208-
209- await interaction . editReply (
210- `Deposited ${ formatDecimalCurrency ( amount , format . prefix , format . suffix , format . prefixSpaceAfter , format . suffixSpaceBefore ) } . Balance: ${ formatDecimalCurrency ( oldAmount , format . prefix , format . suffix , format . prefixSpaceAfter , format . suffixSpaceBefore ) } → ${ formatDecimalCurrency ( updateResult . amount , format . prefix , format . suffix , format . prefixSpaceAfter , format . suffixSpaceBefore ) } .`
211- ) ;
212- } ) ;
213- } catch ( error ) {
214- console . error ( "Error in decimal bank deposit:" , error ) ;
215- await interaction . editReply ( ERROR_MESSAGES . DATABASE_ERROR ) ;
216- }
217- }
1+ import { handleDecimalBankDeposit } from '../shared/decimalOperations.js' ;
2182
2193export { handleDecimalBankDeposit } ;
0 commit comments