node.js

[노드js] 기상청 api를 이용해 디스코드 봇을 만들어 보자

윤만석 2023. 7. 25. 10:05

기상청에서 제공하는 api를 이용해 디스코드 봇을 만들어보겠습니다.

 

단기 기상정보에서 지역 id를 반환하는 함수 getShortTermRegId 입니다.

//외부와 통신을 하는 동기화 함수입니다. async함수는 promise를 반환합니다.
const getShortTermRegId = async (str) => {
  try {
  //js에서 한글이 깨지는 경우가 있습니다. 디코딩하는 과정입니다.
    const response = await axios.get(
      encodeURI(
        `https://apihub.kma.go.kr/api/typ01/url/fct_shrt_reg.php?tmfc=0&authKey=bkPKWXM3RQmDyllzN-UJmA`
      ),
      { responseType: "arraybuffer", responseEncoding: "binary" }
    );
    const decoder = new TextDecoder("euc-kr");
    const content = decoder.decode(response.data);
    //텍스트 자체가 JSON이나 XML이 아니라 공백을 기준으로 나눈 텍스트이므로, 
    //줄바꿈을 기준으로 직접 파싱합니다
    let arr = [];
    let data = content.split("\n");
	//지역명과 동일한 지역을 발견하면 regId를 반환합니다.
    for (let i = 11; i < data.length; i++) {
      let temp = data[i].split(" ");
      for (s of str) {
        if (s.includes(temp[9])) {
          arr.push([temp[0], temp[9]]);
        }
      }
    }
    return arr;
  } catch (e) {
    console.log(e);
  }
};

 

다음은 위의 함수로 받은 regId를 이용해 단기 기상정보를 받아오는 함수입니다.

const getShortTermData = async (regId) => {
  try {
  //마찬가지로 디코딩 후, 파싱해줍니다.
    const response = await axios.get(
      encodeURI(
        `https://apihub.kma.go.kr/api/typ01/url/fct_afs_dl.php?reg=${regId}&disp=0&help=0&authKey=muOqY1EjRTajqmNRI7U20g`
      ),
      { responseType: "arraybuffer", responseEncoding: "binary" }
    );
    const decoder = new TextDecoder("euc-kr");
    const content = decoder.decode(response.data);
    let arr = [];
    let data = content.split(`\n`);
    for (let i = 2; i <= data.length - 3; i++) {
    //공백을 기준으로 데이터들을 저장해줍니다.
      let [
        regId,
        TM_FC,
        TM_EF,
        MOD,
        NE,
        STN,
        C,
        MAN_ID,
        MAN_FC,
        W1,
        T,
        W2,
        TA,
        ST,
        SKY,
        PREP,
        WF,
        WF2,
        WF3,
      ] = data[i].split(" ").filter((str) => str.length > 0);
      let W = (WF || " ") + (WF2 || " ") + (WF3 || "");
      let temp = {
        regId,
        TM_FC,
        TM_EF,
        MOD,
        NE,
        STN,
        C,
        MAN_ID,
        MAN_FC,
        W1,
        T,
        W2,
        TA,
        ST,
        SKY,
        PREP,
        W,
      };
      arr.push(temp);
    }

    return arr;
  } catch (e) {
    console.log(e);
  }
};

 

이제 나머지는 discord.js를 사용해 통신하는일만 남았습니다.

아래는 index.js입니다.

const {
  Client,
  GatewayIntentBits,
  EmbedBuilder,
  Embed,
} = require("discord.js");
//토큰 정보는 config.json에 저장합니다.
const { token } = require("./config.json");
//위에서 작성한 함수 2개입니다.
const { getShortTermData, getShortTermRegId } = require("./api/api");
//날짜를 파싱하는 date, 요일을 계산하는 calDay함수와 , discord Imo를 반환하는 weatherImo함수입니다.
const { date, weatherImo, calDay } = require("./function/function.js");
const celsius = "\u00B0C";
//디스코드 봇의 권한을 부여합니다.
const client = new Client({
  intents: [
    GatewayIntentBits.DirectMessages,
    GatewayIntentBits.Guilds,
    GatewayIntentBits.GuildMessages,
    GatewayIntentBits.MessageContent,
  ],
});

client.on("ready", () => {});
//discord에 메세지가 입력하면 수행합니다.
client.on("messageCreate", (msg) => {
//만약 메세지 안에 "날씨"와 "?"가 들어있다면
  if (msg.content.includes("날씨") && msg.content.includes("?")) {
  //주어진 문자열을 "날씨"를 기준으로 나누어줍니다.
    let str = msg.content.split("날씨");
    async function getLTT() {
    //str을 기준으로 나눠진 문자열로 RegId를 구합니다.
      let RegId = await getShortTermRegId(str);
      //만약 없다면 오류입니다.
      if (RegId.length == 0) {
        msg.reply("지역명을 찾을 수 없어..");
        return;
      }
      //첫번째 값을 이용해 날씨를 구합니다.
      msg.reply(RegId[0][1] + " 날씨를 알려줄게~");
      let LTT = await getShortTermData(RegId[0][0]);
		
      for (data of LTT) {
        let {
          regId,
          TM_FC,
          TM_EF,
          MOD,
          NE,
          STN,
          C,
          MAN_ID,
          MAN_FC,
          W1,
          T,
          W2,
          TA,
          ST,
          SKY,
          PREP,
          W,
        } = data;
		//날짜를 파싱합니다.
        let { year, month, day, time } = date(TM_EF);
        //discord의 기능인 Embed를 생성합니다.
        const embed = new EmbedBuilder();

        msg.channel.send({
          embeds: [
            {
              fields: [
                {
                  name: `${year}/${month}/${day} (${calDay(
                    year,
                    month,
                    day
                  )}) ${time}시 ${
                    time == 0 ? `:crescent_moon:` : `:sun_with_face:`
                  }`,
                  value: `기온 : ${TA}°C  강수 확률 : ${ST}% 개요 : ${W} ${weatherImo(
                    SKY,
                    PREP
                  )} `,
                },
              ],
            },
          ],
        });
      }
    }
    getLTT();
  }
});
//토큰을 이용해 디스코드 봇을 연결합니다.
client.login(token);

잘되네요