diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..f74c661 --- /dev/null +++ b/.env.example @@ -0,0 +1 @@ +TOKEN= \ No newline at end of file diff --git a/pom.xml b/pom.xml index dc7de8d..9c2f1dd 100644 --- a/pom.xml +++ b/pom.xml @@ -20,6 +20,12 @@ JDA 5.0.0-alpha.12 + + + io.github.cdimascio + dotenv-java + 2.2.4 + \ No newline at end of file diff --git a/src/main/java/com/technovision/tutorialbot/TutorialBot.java b/src/main/java/com/technovision/tutorialbot/TutorialBot.java index af07e18..d3259b6 100644 --- a/src/main/java/com/technovision/tutorialbot/TutorialBot.java +++ b/src/main/java/com/technovision/tutorialbot/TutorialBot.java @@ -1,9 +1,16 @@ package com.technovision.tutorialbot; +import com.technovision.tutorialbot.commands.CommandManager; +import com.technovision.tutorialbot.listeners.EventListener; +import io.github.cdimascio.dotenv.Dotenv; import net.dv8tion.jda.api.OnlineStatus; import net.dv8tion.jda.api.entities.Activity; +import net.dv8tion.jda.api.requests.GatewayIntent; import net.dv8tion.jda.api.sharding.DefaultShardManagerBuilder; import net.dv8tion.jda.api.sharding.ShardManager; +import net.dv8tion.jda.api.utils.ChunkingFilter; +import net.dv8tion.jda.api.utils.MemberCachePolicy; +import net.dv8tion.jda.api.utils.cache.CacheFlag; import javax.security.auth.login.LoginException; @@ -15,6 +22,7 @@ */ public class TutorialBot { + private final Dotenv config; private final ShardManager shardManager; /** @@ -22,13 +30,30 @@ public class TutorialBot { * @throws LoginException occurs when bot token is invalid. */ public TutorialBot() throws LoginException { - String token = "YOUR_BOT_TOKEN"; + // Load environment variables + config = Dotenv.configure().load(); + String token = config.get("TOKEN"); + + // Build shard manager DefaultShardManagerBuilder builder = DefaultShardManagerBuilder.createDefault(token); builder.setStatus(OnlineStatus.ONLINE); builder.setActivity(Activity.watching("TechnoVisionTV")); + builder.enableIntents(GatewayIntent.GUILD_MESSAGES, GatewayIntent.GUILD_MEMBERS, GatewayIntent.GUILD_PRESENCES); + builder.setMemberCachePolicy(MemberCachePolicy.ALL); + builder.setChunkingFilter(ChunkingFilter.ALL); + builder.enableCache(CacheFlag.ONLINE_STATUS); shardManager = builder.build(); + + // Register listeners + shardManager.addEventListener(new EventListener(), new CommandManager()); } + /** + * Retrieves the bot environment variables. + * @return the DotEnv instance for the bot. + */ + public Dotenv getConfig() { return config; } + /** * Retrieves the bot shard manager. * @return the ShardManager instance for the bot. diff --git a/src/main/java/com/technovision/tutorialbot/commands/CommandManager.java b/src/main/java/com/technovision/tutorialbot/commands/CommandManager.java new file mode 100644 index 0000000..7a0c8cd --- /dev/null +++ b/src/main/java/com/technovision/tutorialbot/commands/CommandManager.java @@ -0,0 +1,67 @@ +package com.technovision.tutorialbot.commands; + +import net.dv8tion.jda.api.entities.Role; +import net.dv8tion.jda.api.events.ReadyEvent; +import net.dv8tion.jda.api.events.guild.GuildReadyEvent; +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import net.dv8tion.jda.api.hooks.ListenerAdapter; +import net.dv8tion.jda.api.interactions.commands.build.CommandData; +import net.dv8tion.jda.api.interactions.commands.build.Commands; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +/** + * Registers and manages slash commands. + * + * @author TechnoVision + */ +public class CommandManager extends ListenerAdapter { + + /** + * Listens for slash commands and responds accordingly + */ + @Override + public void onSlashCommandInteraction(@NotNull SlashCommandInteractionEvent event) { + String command = event.getName(); + if (command.equals("welcome")) { + // Run the 'ping' command + String userTag = event.getUser().getAsTag(); + event.reply("Welcome to the server, **" + userTag + "**!").queue(); + } + else if (command.equals("roles")) { + // run the 'roles' command + event.deferReply().queue(); + String response = ""; + for (Role role : event.getGuild().getRoles()) { + response += role.getAsMention() + "\n"; + } + event.getHook().sendMessage(response).queue(); + } + } + + /** + * Registers slash commands as GUILD commands (max 100). + * These commands will update instantly and are great for testing. + */ + @Override + public void onGuildReady(@NotNull GuildReadyEvent event) { + List commandData = new ArrayList<>(); + commandData.add(Commands.slash("welcome", "Get welcomed by the bot")); + commandData.add(Commands.slash("roles", "Display all roles on the server")); + event.getGuild().updateCommands().addCommands(commandData).queue(); + } + + /** + * Registers slash commands as GLOBAL commands (unlimited). + * These commands may take up to an hour to update. + */ + @Override + public void onReady(@NotNull ReadyEvent event) { + List commandData = new ArrayList<>(); + commandData.add(Commands.slash("welcome", "Get welcomed by the bot")); + commandData.add(Commands.slash("roles", "Display all roles on the server")); + event.getJDA().updateCommands().addCommands(commandData).queue(); + } +} \ No newline at end of file diff --git a/src/main/java/com/technovision/tutorialbot/listeners/EventListener.java b/src/main/java/com/technovision/tutorialbot/listeners/EventListener.java new file mode 100644 index 0000000..e3838d8 --- /dev/null +++ b/src/main/java/com/technovision/tutorialbot/listeners/EventListener.java @@ -0,0 +1,74 @@ +package com.technovision.tutorialbot.listeners; + +import net.dv8tion.jda.api.OnlineStatus; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.Role; +import net.dv8tion.jda.api.entities.User; +import net.dv8tion.jda.api.events.guild.member.GuildMemberJoinEvent; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent; +import net.dv8tion.jda.api.events.user.update.UserUpdateOnlineStatusEvent; +import net.dv8tion.jda.api.hooks.ListenerAdapter; +import org.jetbrains.annotations.NotNull; + +/** + * Listens for events and responds with our custom code. + * + * @author TechnoVision + */ +public class EventListener extends ListenerAdapter { + + /** + * Event fires when an emoji reaction is added to a message. + */ + @Override + public void onMessageReactionAdd(@NotNull MessageReactionAddEvent event) { + User user = event.getUser(); + String jumpLink = event.getJumpUrl(); + String emoji = event.getReaction().getReactionEmote().getAsReactionCode(); + String channel = event.getChannel().getAsMention(); + + String message = user.getAsTag() + " reacted to a [message]("+jumpLink+") with " + emoji + " in the " + channel + " channel!"; + event.getGuild().getDefaultChannel().sendMessage(message).queue(); + } + + /** + * Event fires when a message is sent in discord. + * Will require "Guild Messages" gateway intent after August 2022! + */ + @Override + public void onMessageReceived(@NotNull MessageReceivedEvent event) { + String message = event.getMessage().getContentRaw(); + if (message.contains("ping")) { + event.getChannel().sendMessage("pong").queue(); + } + } + + /** + * Event fires when a new member joins a guild + * Requires "Guild Members" gateway intent! + */ + @Override + public void onGuildMemberJoin(@NotNull GuildMemberJoinEvent event) { + Role role = event.getGuild().getRoleById(988342442430443540L); + if (role != null) { + event.getGuild().addRoleToMember(event.getMember(), role).queue(); + } + } + + /** + * Event fires when a user updates their online status + * Requires "Guild Presences" gateway intent AND cache enabled! + */ + @Override + public void onUserUpdateOnlineStatus(@NotNull UserUpdateOnlineStatusEvent event) { + int onlineMembers = 0; + for (Member member : event.getGuild().getMembers()) { + if (member.getOnlineStatus() == OnlineStatus.ONLINE) { + onlineMembers++; + } + } + String message = event.getUser().getAsTag()+"updated their online status! There are "+onlineMembers+" members online now!"; + event.getGuild().getDefaultChannel().sendMessage(message).queue(); + } +}