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);
잘되네요