add US Trading hours notifier
This commit is contained in:
parent
0736e8f211
commit
47c9145575
87
app.js
87
app.js
|
|
@ -1,28 +1,31 @@
|
|||
import dotenv from 'dotenv';
|
||||
import * as util from './utils.js'
|
||||
import * as util from './feature/utility.js';
|
||||
import { REST, Routes } from 'discord.js';
|
||||
import { Client, GatewayIntentBits } from 'discord.js';
|
||||
import { EmbedBuilder } from 'discord.js';
|
||||
import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from 'discord.js';
|
||||
import { CronJob } from 'cron';
|
||||
|
||||
|
||||
dotenv.config();
|
||||
const TOKEN = process.env.TOKEN;
|
||||
const CLIENT_ID = process.env.CLI_ID;
|
||||
|
||||
// const commands = [
|
||||
// {
|
||||
// name: 'ping',
|
||||
// description: 'Replies with Pong!',
|
||||
// },
|
||||
// ];
|
||||
const commands = [
|
||||
{
|
||||
name: 'ping',
|
||||
description: 'Pong! 으로 대답합니다.',
|
||||
},
|
||||
{
|
||||
name: 'color',
|
||||
description: '아이디 색상을 변경합니다.',
|
||||
},
|
||||
];
|
||||
|
||||
// const rest = new REST({ version: '10' }).setToken(TOKEN);
|
||||
|
||||
// try {
|
||||
// console.log('Started refreshing application (/) commands.');
|
||||
|
||||
// await rest.put(Routes.applicationCommands(CLIENT_ID), { body: commands });
|
||||
|
||||
// console.log('Successfully reloaded application (/) commands.');
|
||||
// } catch (error) {
|
||||
// console.error(error);
|
||||
|
|
@ -51,11 +54,37 @@ const client = new Client({
|
|||
]
|
||||
});
|
||||
|
||||
|
||||
client.on('ready', () => {
|
||||
console.log(`Logged in as ${client.user.tag}!`);
|
||||
|
||||
// 서머타임 여부에 따라 시간 설정
|
||||
console.log(util.isDaylightSavingTime() ? '서머타임 적용 중' : '서머타임 미적용 중');
|
||||
const openTime = util.isDaylightSavingTime() ? '30 22 * * *' : '30 23 * * *'; // 거래 시작 시간
|
||||
const closeTime = util.isDaylightSavingTime() ? '0 5 * * *' : '0 6 * * *'; // 거래 종료 시간
|
||||
|
||||
// 거래 시작
|
||||
const job1 = new CronJob(openTime, async () => {
|
||||
const channel = client.channels.cache.get(process.env.CH_FINANCE);
|
||||
if (channel) {
|
||||
await channel.send('미국 주식 시장이 열렸습니다.');
|
||||
}
|
||||
}, null, true, 'Asia/Seoul');
|
||||
|
||||
// 거래 종료
|
||||
const job2 = new CronJob(closeTime, async () => {
|
||||
const channel = client.channels.cache.get(process.env.CH_FINANCE);
|
||||
if (channel) {
|
||||
await channel.send('미국 주식 시장이 닫혔습니다.');
|
||||
}
|
||||
}, null, true, 'Asia/Seoul');
|
||||
|
||||
job1.start();
|
||||
job2.start();
|
||||
});
|
||||
|
||||
|
||||
|
||||
client.on('interactionCreate', async interaction => {
|
||||
if (!interaction.isChatInputCommand()) return;
|
||||
|
||||
|
|
@ -66,7 +95,7 @@ client.on('interactionCreate', async interaction => {
|
|||
|
||||
|
||||
client.on('messageCreate', async message => {
|
||||
const urlPattern = /https?:\/\/(www\.)?gall\.dcinside\.com\/[^\s]*/g;
|
||||
const urlPattern = /https?:\/\/(www\.)?(m\.)?gall\.dcinside\.com\/[^\s]*/g;
|
||||
const urls = message.content.match(urlPattern);
|
||||
|
||||
if (urls && urls.length > 0) {
|
||||
|
|
@ -109,30 +138,32 @@ client.on('messageCreate', async message => {
|
|||
}
|
||||
});
|
||||
|
||||
client.on('interactionCreate', async interaction => {
|
||||
if (!interaction.isChatInputCommand()) return;
|
||||
if (interaction.commandName === 'color') {
|
||||
const sentMessage = await interaction.reply({
|
||||
content: '아이디로 보여질 색상을 선택하세요.',
|
||||
fetchReply: true, // 메시지 ID 저장을 위해 reply 객체 반환
|
||||
});
|
||||
|
||||
// 'send-reactions' 커맨드를 받았을 때 리액션이 포함된 메시지를 보냄
|
||||
client.on('messageCreate', async message => {
|
||||
if (message.content === '!색상') {
|
||||
const sentMessage = await message.channel.send('색상을 선택하세요.');
|
||||
botMessageId = sentMessage.id; // 메시지 ID 저장
|
||||
|
||||
// 'roles' 객체에서 이모지 키들을 추출하고, 각 이모지에 대해 리액션 추가
|
||||
Object.keys(roles).forEach(async emoji => {
|
||||
if (emoji !== '🔁') { // '리셋' 이모지는 마지막에 추가하기 위해 여기서는 제외
|
||||
// 이모지 추가 (🔄 포함)
|
||||
await Promise.all(
|
||||
Object.keys(roles).map(async emoji => {
|
||||
await sentMessage.react(emoji);
|
||||
}
|
||||
});
|
||||
await sentMessage.react('🔁');
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// 리액션 추가 이벤트 처리
|
||||
client.on('messageReactionAdd', async (reaction, user) => {
|
||||
if (reaction.message.partial) await reaction.message.fetch();
|
||||
if (reaction.partial) await reaction.fetch();
|
||||
if (user.bot) return; // 봇의 리액션 무시
|
||||
|
||||
// if (reaction.message.id === botMessageId) {
|
||||
if (reaction.message.author.bot) {
|
||||
await reaction.users.remove(user.id); // 리액션 바로 원복
|
||||
|
||||
|
|
@ -154,11 +185,21 @@ client.on('messageReactionAdd', async (reaction, user) => {
|
|||
const role = reaction.message.guild.roles.cache.get(roleId);
|
||||
if (role) {
|
||||
await member.roles.add(role);
|
||||
console.log(`${user.username}에게 ${role.name}역할이 부여되었습니다.`);
|
||||
console.log(`${user.username}에게 ${role.name} 역할이 부여되었습니다.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
client.on('messageCreate', async message => {
|
||||
// '!역할확인' 명령어를 통해 역할과 ID 출력
|
||||
if (message.content === '!역할확인') {
|
||||
const roles = message.guild.roles.cache.map(role => `${role.name}: ${role.id}`);
|
||||
console.log(roles.join('\n'));
|
||||
message.channel.send(`서버 역할 목록:\n${roles.join('\n')}`);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
client.login(TOKEN);
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
import axios from 'axios';
|
||||
import cheerio from 'cheerio';
|
||||
|
||||
export async function getMetaData(url) {
|
||||
try {
|
||||
const response = await axios.get(url);
|
||||
const html = response.data;
|
||||
const $ = cheerio.load(html);
|
||||
|
||||
const title = $('meta[property="og:title"]').attr('content');
|
||||
const description = $('meta[property="og:description"]').attr('content');
|
||||
const image = $('meta[property="og:image"]').attr('content');
|
||||
|
||||
return {
|
||||
title: title || 'No title',
|
||||
description: description || 'No description',
|
||||
image: image || ''
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error fetching URL:', error.message);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export function isDaylightSavingTime() {
|
||||
const now = new Date();
|
||||
const jan = new Date(now.getFullYear(), 0, 1);
|
||||
const jul = new Date(now.getFullYear(), 6, 1);
|
||||
const standardOffset = Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset());
|
||||
return now.getTimezoneOffset() < standardOffset; // 서머타임 여부 반환
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@
|
|||
"dependencies": {
|
||||
"axios": "^1.6.8",
|
||||
"cheerio": "^1.0.0-rc.12",
|
||||
"cron": "^3.5.0",
|
||||
"discord.js": "^14.14.1",
|
||||
"dotenv": "^16.4.5"
|
||||
}
|
||||
|
|
@ -151,6 +152,11 @@
|
|||
"npm": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/luxon": {
|
||||
"version": "3.4.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.4.2.tgz",
|
||||
"integrity": "sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA=="
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.11.30",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.30.tgz",
|
||||
|
|
@ -243,6 +249,15 @@
|
|||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/cron": {
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/cron/-/cron-3.5.0.tgz",
|
||||
"integrity": "sha512-0eYZqCnapmxYcV06uktql93wNWdlTmmBFP2iYz+JPVcQqlyFYcn1lFuIk4R54pkOmE7mcldTAPZv6X5XA4Q46A==",
|
||||
"dependencies": {
|
||||
"@types/luxon": "~3.4.0",
|
||||
"luxon": "~3.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/css-select": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
|
||||
|
|
@ -444,6 +459,14 @@
|
|||
"resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz",
|
||||
"integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw=="
|
||||
},
|
||||
"node_modules/luxon": {
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz",
|
||||
"integrity": "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/magic-bytes.js": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.10.0.tgz",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "discord_allnight",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.1",
|
||||
"main": "app.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
|
@ -11,6 +11,7 @@
|
|||
"dependencies": {
|
||||
"axios": "^1.6.8",
|
||||
"cheerio": "^1.0.0-rc.12",
|
||||
"cron": "^3.5.0",
|
||||
"discord.js": "^14.14.1",
|
||||
"dotenv": "^16.4.5"
|
||||
}
|
||||
|
|
|
|||
25
utils.js
25
utils.js
|
|
@ -1,25 +0,0 @@
|
|||
import axios from 'axios';
|
||||
import cheerio from 'cheerio';
|
||||
|
||||
async function getMetaData(url) {
|
||||
try {
|
||||
const response = await axios.get(url);
|
||||
const html = response.data;
|
||||
const $ = cheerio.load(html);
|
||||
|
||||
const title = $('meta[property="og:title"]').attr('content');
|
||||
const description = $('meta[property="og:description"]').attr('content');
|
||||
const image = $('meta[property="og:image"]').attr('content');
|
||||
|
||||
return {
|
||||
title: title || 'No title',
|
||||
description: description || 'No description',
|
||||
image: image || ''
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error fetching URL:', error.message);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
export { getMetaData };
|
||||
Loading…
Reference in New Issue