히치키치

시간이 측정되고 DB에 저장된다 본문

Cognisle

시간이 측정되고 DB에 저장된다

히치키치 2024. 3. 18. 09:17

목표 체크

✅ 캠스터디 음성 채널 입장 & 캠스터디 음성 채널 퇴장 & 캠스터디 음성 채널 접속 유지 시간

✅ supabase DB에 캠스터디 접속 시간과 해당 유저 정보 저장

✅ 하드 코딩이 아닌 환경 변수로 코드 작성

 

코드 리뷰

 

1. 필요한 모듈 가져오기

discord.js : 디스코드 API와 상호작용 

@supabase/supabase-js : Supabase DB와 상호 작용

dotenv : 환경 변수 관리

 

const { Client, GatewayIntentBits, REST, Routes } = require("discord.js");
const { createClient } = require("@supabase/supabase-js");
const dotenv =require("dotenv");
dotenv.config();

 

2. discord 클라이트 생성

서버, 서버 음성 상태, 서버 메세지 상태, 서버 메세지 내용을 추적하도록 설정

const client = new Client({
 intents: [
  GatewayIntentBits.Guilds,
  GatewayIntentBits.GuildVoiceStates,
  GatewayIntentBits.GuildMessages,
  GatewayIntentBits.MessageContent,
 ],
});

 

3. supabase 클라이언트 생성

환경 변수에서 supabase URL과 API 키를 제공해야 함

const supabase = createClient(
 process.env.SUPABASE_URL,
 process.env.SUPABASE_API_KEY
);

 

4. 디스코드 봇의 사용 준비 완료~!

client.once("ready", () => {
 console.log("Bot is ready!");
});

 

5. 음성 채널에서 사용자 접속 측정

 

5-1. 봇이 추적 해야하는 음성 채널 지정

환경 변수에서 해당 채널의 ID 값을 가져옴

// YOUR_VOICE_CHANNEL_ID
const channelToTrack = process.env.VOICE_CHANNEL;

 

5-2. 사용자의 음성 시작 시간, 종료 시간, 총 시간을 저장하는 Map 객체 생성

const userDurations = new Map();

 

5-3. 사용자 채널 입장에 대한 처리

startDuration 함수 : 사용자Id 기반으로 사용자의 채널 입장에 대한 정보를 Map 객체에 저장함

인자 : 사용자 고유 ID

객체 저장 값:

음성 시작 시간 : (현재 시간 설정)

종료 시간 : 지정 되지 않음 (null)

총 시간 : 지정 되지 않음 (0)

 // Record the start time
 userDurations.set(userId, {
   startTime: new Date(), // 음성 시작 시간 : 현재 시간
   endTime: null, // 음성 종료 시간 : 지정되지 않음 null
   totalDuration: 0, // 총 시간 : 지정되지 않음 0
 });
}

 

 

5-4. 사용자 채널 퇴장에 대한 처리

endDuration 함수 : 사용자Id 기반으로 음성 채널 퇴장에 대한 정보를 Map 객체에 저장

인자 : 사용자 고유 ID

객체 저장 값: 

종료 시간 : (현재 시간 설정)

총 시간 : 종료 시간 - 시작 시간

function endDuration(durationInfo) {
 durationInfo.endTime = new Date();
 durationInfo.totalDuration = Math.floor(
   (durationInfo.endTime.getTime() - durationInfo.startTime.getTime()) / 1000
 );
}

 

5-5. 사용자 음성 상태 변경에 대한 이벤트 등록

사용자의 음성 상태가 변경될 때마다 호출되는 voiceStateUpdate 이벤트 리스너 설정

콜백 함수로 이벤트 발생 전 상태 (oldState)와 이벤트 발생 후 상태 (newState)를 전달 받음

입장한 경우 

  • 상태 : 유저 존재 & 이벤트 발생 후 해당 채널==공부방 음성 채널 
  • 처리 : startDuration 

퇴장한 경우

  • 상태 : 유저 존재 & 이벤트 발생 전 해당 채널==공부방 음성 채널 & 이벤트 발생 후 채널 == 없음
  • (퇴장한 이후임으로 이벤트에 채널이 없는 상태)
  • 처리 : endDuration -> saveDuration
client.on("voiceStateUpdate", (oldState, newState) => {
 const user = newState.member;
 // console.log(user)

 if (user && newState.channelId === channelToTrack) {
     // 이벤트 발생이 최초로 입장한 유저의 경우
     if(userDurations.has(user.id)===false){
       // User joined the specified voice channel
       console.log(
         `${user.user.tag} joined ${newState.channel?.name}: ${new Date()}`
       );
       startDuration(user.id);
     }

 } else if (
   user &&
   oldState.channelId === channelToTrack &&
   newState.channel === null
 ) {
   // User left the specified voice channel

   const { id: userUId, tag: userTag, globalName: userGlobalName } = user.user;
       console.log(`${userGlobalName} left ${oldState.channel?.name}: ${new Date()}`);

   if (userDurations.has(userUId)) {
     const durationInfo = userDurations.get(userUId);
     endDuration(durationInfo);
     const { startTime, endTime, totalDuration } = durationInfo;
     saveDuration(
       userUId,
       userGlobalName,
       userTag,
       startTime,
       endTime,
      totalDuration
     );
     // Remove the user from the tracking map
     userDurations.delete(user.id);
   }
   


 }
});

 

5-6. 객체에 담긴 유저의 입퇴장 정보 DB 저장

saveDuration 함수 : Map 객체에 사용자Id 기반 음성 채널 입퇴장에 대한 정보를 supabase에 연결

user 테이블에 { dsUId, dsGlobalName, dsTag, start, end, duration } 칼럼에 해당 레코드 값 매칭

async function saveDuration(dsUId, dsGlobalName, dsTag, start, end, duration) {
  // Save the duration to the Superbase database

  const { data, error, status } = await supabase
    .from("user")
    .insert({ dsUId, dsGlobalName, dsTag, start, end, duration });

  if (error) {
    console.log(error);
  } else {
    console.log(data, status);
  }
}

 

 

5-7. 디스코드 봇의 디스코드 서버 로그인

디스코드 봇의 인증 코드를 환경 변수에서 가져옴

디스코드 서버에 로그인 해 봇 기능 수행 준비

client.login(process.env.DISCORD_TOKEN);

 

6. DB 구조

 

7. 실행 결과

 

하하하 아주 잘 되고 있다~~

 

8. 앞으로는? 

❓노트북으로 매번 서버 구동하지 않고 자동으로 계속 꺼지지 않고 돌아가도록 with 경제적인 가격

❓index.js에 모든 코드 다 있음... 정리 필요

❓환경 변수와 package script에서 테스트 디스코드 서버와 배포 디스코드 서버 분리 필요

❓다양한 기능 아이디어와 동시에 DB 구조 확인

Comments