1- const {
2- ActivityType,
1+ const fs = require ( 'node:fs' ) ;
2+ const path = require ( 'node:path' ) ;
3+ const {
34 Client,
45 ChannelType,
56 GatewayIntentBits,
6- Partials,
7- EmbedBuilder } = require ( 'discord.js' ) ;
8- const { GoogleSpreadsheet } = require ( 'google-spreadsheet' ) ;
9- const config = require ( `${ __dirname } /config.json` ) ;
10- const moment = require ( 'moment' ) ;
7+ Partials } = require ( 'discord.js' ) ;
8+ const config = require ( './config.json' ) ;
9+ const { sendEmbedMessage, formatTime } = require ( './utils/core' ) ;
10+ const { sendData } = require ( './utils/database' ) ;
1111
1212require ( 'dotenv' ) . config ( ) ;
1313
@@ -33,29 +33,6 @@ const client = new Client({
3333 ]
3434} ) ;
3535
36- // load spreadsheet
37- const doc = new GoogleSpreadsheet ( process . env . GOOGLE_SPREADSHEET_ID ) ;
38-
39- /**
40- * send embed message
41- * @param {string } message
42- * @returns pre-defined embed style
43- */
44- const sendEmbedMessage = ( message ) => {
45- return new EmbedBuilder ( )
46- . setDescription ( message )
47- . setColor ( `#f213a4` ) ;
48- }
49-
50- /**
51- * get username from ownerid
52- * @param {number } id
53- * @returns
54- */
55- const getUsernameFromId = async ( id ) => {
56- return ( await client . users . fetch ( id ) ) . username ;
57- }
58-
5936// listen to post messages
6037client . on ( 'messageCreate' , async ( message ) => {
6138 if ( message . author . bot ) return ;
@@ -89,15 +66,12 @@ client.on('messageCreate', async (message) => {
8966 if ( typeof post . availableTags !== 'undefined' ) {
9067 // filter the tags to get the resolution tag name ID
9168 const resolutionTag = post . availableTags . filter ( ( item ) => { return item . name == config . tag_name_resolve } ) ;
69+ const closeTag = post . availableTags . filter ( ( item ) => { return item . name == config . tag_name_close } ) ;
9270 // get the existing tags of the post
9371 const postTags = message . channel . appliedTags ;
94-
95- // collect tags
96- let initialTags = [ resolutionTag [ 0 ] . id , ...postTags ] ;
97- let tags = [ ...new Set ( initialTags ) ] ;
9872
9973 // check if the command has the prefix and includes "resolve"
100- if ( message . content . startsWith ( config . command_prefix ) && message . content . includes ( config . command_resolve ) ) {
74+ if ( message . content . startsWith ( config . command_prefix ) ) {
10175 await message . delete ( ) ; // delete the commmand message
10276
10377 // check if the message is in the forum post and from the support role
@@ -106,39 +80,91 @@ client.on('messageCreate', async (message) => {
10680 // check if the post has fewer tags
10781 if ( postTags . length < 5 ) {
10882
109- // send embed message before resolving the post
110- await message . channel . send ( { embeds : [
111- sendEmbedMessage ( `${ config . reminder_resolve } ` )
112- ] ,
113- content : `🔔 <@${ message . channel . ownerId } >`
114- } )
115-
116- // then archive and lock it
117- message . channel . edit ( {
118- appliedTags : tags ,
119- archived : true
120- } ) ;
121-
12283 // gather data
12384 const postId = message . channel . id ;
12485 const resolutionTime = formatTime ( message . createdTimestamp ) ;
12586 const resolvedBy = member . user . username ;
12687
127- // check if there's a mentioned user
128- if ( mention . users . first ( ) ) {
129- // send the data, use the mentioned user as resolvedBy
130- sendData ( {
131- post_id : postId ,
132- resolution_time : resolutionTime ,
133- resolved_by : mention . users . first ( ) . username ,
134- } , config . datasheet_resolve ) ;
135- } else {
136- // send the data with the one who sends the command
137- sendData ( {
138- post_id : postId ,
139- resolution_time : resolutionTime ,
140- resolved_by : resolvedBy
141- } , config . datasheet_resolve ) ;
88+ // functions for resolve command
89+ if ( message . content . includes ( config . command_resolve ) ) {
90+
91+ // data for resolve command
92+ // collect tags
93+ let initialTags = [ resolutionTag [ 0 ] . id , ...postTags ] ;
94+ let tags = [ ...new Set ( initialTags ) ] ;
95+
96+ // send embed message upon executing the resolve command
97+ await message . channel . send ( {
98+ embeds : [
99+ sendEmbedMessage ( `${ config . reminder_resolve } ` )
100+ ] ,
101+ content : `🔔 <@${ message . channel . ownerId } >`
102+ } ) ;
103+
104+ // then archive and lock it
105+ message . channel . edit ( {
106+ appliedTags : tags ,
107+ archived : true
108+ } ) ;
109+
110+ // check if there's a mentioned user
111+ if ( mention . users . first ( ) ) {
112+ // send the data, use the mentioned user as resolvedBy
113+ sendData ( {
114+ post_id : postId ,
115+ resolution_time : resolutionTime ,
116+ resolved_by : mention . users . first ( ) . username ,
117+ } , config . datasheet_resolve ) ;
118+ } else {
119+ // send the data with the one who sends the command
120+ sendData ( {
121+ post_id : postId ,
122+ resolution_time : resolutionTime ,
123+ resolved_by : resolvedBy
124+ } , config . datasheet_resolve ) ;
125+ }
126+
127+ }
128+
129+ // functions for close command
130+ if ( message . content . includes ( config . command_close ) ) {
131+
132+ // data for resolve command
133+ // collect tags
134+ let initialTags = [ closeTag [ 0 ] . id , ...postTags ] ;
135+ let tags = [ ...new Set ( initialTags ) ] ;
136+
137+ // send embed message upon executing the close command
138+ await message . channel . send ( {
139+ embeds : [
140+ sendEmbedMessage ( `${ config . reminder_close } ` )
141+ ] ,
142+ content : `🔔 <@${ message . channel . ownerId } >`
143+ } ) ;
144+
145+ // then archive and lock it
146+ message . channel . edit ( {
147+ appliedTags : tags ,
148+ archived : true
149+ } ) ;
150+
151+ // check if there's a mentioned user
152+ if ( mention . users . first ( ) ) {
153+ // send the data, use the mentioned user as resolvedBy
154+ sendData ( {
155+ post_id : postId ,
156+ close_time : resolutionTime ,
157+ closed_by : mention . users . first ( ) . username ,
158+ } , config . datasheet_close ) ;
159+ } else {
160+ // send the data with the one who sends the command
161+ sendData ( {
162+ post_id : postId ,
163+ close_time : resolutionTime ,
164+ closed_by : resolvedBy
165+ } , config . datasheet_close ) ;
166+ }
167+
142168 }
143169
144170 } else {
@@ -234,8 +260,10 @@ client.on('threadCreate', async post => {
234260 const tags = forumTags . join ( ', ' ) ;
235261 const firstResponse = `=IFERROR(VLOOKUP(A2:A,${ config . datasheet_response } !A2:B,2,0))` ;
236262 const resolutionTime = `=IFERROR(VLOOKUP(A2:A,${ config . datasheet_resolve } !A2:B,2,0))` ;
263+ const closeTime = `=IFERROR(VLOOKUP(A2:A,${ config . datasheet_close } !A2:B,2,0))` ;
237264 const responder = `=IFERROR(VLOOKUP(A2:A,{${ config . datasheet_response } !A2:A,${ config . datasheet_response } !C2:C},2,0))` ;
238265 const resolvedBy = `=IFERROR(VLOOKUP(A2:A,{${ config . datasheet_resolve } !A2:A,${ config . datasheet_resolve } !C2:C},2,0))` ;
266+ const closedBy = `=IFERROR(VLOOKUP(A2:A,{${ config . datasheet_close } !A2:A,${ config . datasheet_close } !C2:C},2,0))` ;
239267
240268 // send the data
241269 sendData ( {
@@ -248,66 +276,25 @@ client.on('threadCreate', async post => {
248276 responder : responder ,
249277 first_response : firstResponse ,
250278 resolution_time : resolutionTime ,
251- resolved_by : resolvedBy
279+ resolved_by : resolvedBy ,
280+ close_time : closeTime ,
281+ closed_by : closedBy
252282 } , config . datasheet_init ) ;
253283} ) ;
254284
255- /**
256- * sends data to the spreadsheet
257- * @param {object } data - data being added as row in the spreadsheet
258- * @param {string } datasheet - name of sheet where data being sent e.g. init, response, resolve
259- */
260- const sendData = async ( data , datasheet ) => {
261- // authenticate
262- await doc . useServiceAccountAuth ( {
263- client_email : process . env . GOOGLE_SERVICE_ACCOUNT_EMAIL ,
264- private_key : process . env . GOOGLE_PRIVATE_KEY . replace ( / \\ n / g, "\n" ) ,
265- } ) ;
266- // load the "initial" sheet
267- await doc . loadInfo ( ) ;
268- const sheet = doc . sheetsByTitle [ datasheet ] ;
269-
270- // check if the data will be send to init sheet
271- if ( datasheet === config . datasheet_init ) {
272- await sheet . addRow ( data ) ;
273- } ;
274-
275- // check if the data will be send to response sheet
276- if ( datasheet === config . datasheet_response ) {
277- await sheet . addRow ( data ) ;
285+ // reading events file
286+ const eventsPath = path . join ( __dirname , 'events' ) ;
287+ const eventFiles = fs . readdirSync ( eventsPath ) . filter ( file => file . endsWith ( '.js' ) ) ;
288+
289+ for ( const file of eventFiles ) {
290+ const filePath = path . join ( eventsPath , file ) ;
291+ const event = require ( filePath ) ;
292+ if ( event . once ) {
293+ client . once ( event . name , ( ...args ) => event . execute ( ...args ) ) ;
294+ } else {
295+ client . on ( event . name , ( ...args ) => event . execute ( ...args ) ) ;
278296 }
279-
280- // check if the data will be send to resolve sheet
281- if ( datasheet === config . datasheet_resolve ) {
282- await sheet . addRow ( data ) ;
283- } ;
284- }
285-
286- /**
287- * format time according to UTC
288- * @param {number } date - epoch timestamp
289- * @returns time and date format
290- */
291- const formatTime = ( date ) => {
292- return moment . utc ( date ) . utcOffset ( config . utc_offset ) . format ( 'M/DD/YYYY HH:mm:ss' ) ;
293297}
294298
295- // discord error log event
296- client . on ( 'error' , ( err ) => {
297- console . log ( err ) ;
298- } ) ;
299-
300- // discord log event
301- client . once ( 'ready' , bot => {
302- client . user ?. setPresence ( {
303- activities : [ {
304- name : 'for support.' ,
305- type : ActivityType . Watching
306- } ]
307- } ) ;
308-
309- console . log ( `Ready! Logged in as ${ bot . user . tag } ` ) ;
310- } ) ;
311-
312299// log in to Discord with your client's token
313300client . login ( token ) ;
0 commit comments