From 20a25d2aa813e41f922bdea013ac09fcb75784e8 Mon Sep 17 00:00:00 2001 From: =?utf8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Mon, 8 Apr 2013 21:26:24 +0200 Subject: [PATCH 1/1] Add current state of core. --- .../java/net/pterodactylus/xdcc/core/Core.java | 239 +++++++++++++++++++++ 1 file changed, 239 insertions(+) create mode 100644 src/main/java/net/pterodactylus/xdcc/core/Core.java diff --git a/src/main/java/net/pterodactylus/xdcc/core/Core.java b/src/main/java/net/pterodactylus/xdcc/core/Core.java new file mode 100644 index 0000000..d5a45b1 --- /dev/null +++ b/src/main/java/net/pterodactylus/xdcc/core/Core.java @@ -0,0 +1,239 @@ +/* + * XdccDownloader - Core.java - Copyright © 2013 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.xdcc.core; + +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.logging.Level; +import java.util.logging.Logger; + +import net.pterodactylus.irc.Connection; +import net.pterodactylus.irc.ConnectionBuilder; +import net.pterodactylus.irc.event.ChannelMessageReceived; +import net.pterodactylus.irc.event.ConnectionEstablished; +import net.pterodactylus.irc.util.MessageCleaner; +import net.pterodactylus.irc.util.RandomNickname; +import net.pterodactylus.xdcc.data.Bot; +import net.pterodactylus.xdcc.data.Channel; +import net.pterodactylus.xdcc.data.Network; +import net.pterodactylus.xdcc.data.Pack; +import net.pterodactylus.xdcc.data.Server; + +import com.beust.jcommander.internal.Maps; +import com.beust.jcommander.internal.Sets; +import com.google.common.base.Optional; +import com.google.common.collect.HashBasedTable; +import com.google.common.collect.Lists; +import com.google.common.collect.Table; +import com.google.common.eventbus.EventBus; +import com.google.common.eventbus.Subscribe; +import com.google.common.util.concurrent.AbstractIdleService; +import com.google.inject.Inject; + +/** + * The core of XDCC Downloader. + * + * @author David ‘Bombe’ Roden + */ +public class Core extends AbstractIdleService { + + /** The logger. */ + private static final Logger logger = Logger.getLogger(Core.class.getName()); + + /** The event bus. */ + private final EventBus eventBus; + + /** The channels that should be monitored. */ + private final Collection channels = Sets.newHashSet(); + + /** The current network connections. */ + private final Map networkConnections = Collections.synchronizedMap(Maps.newHashMap()); + + /** The currently known bots. */ + private final Table networkBots = HashBasedTable.create(); + + /** + * Creates a new core. + * + * @param eventBus + * The event bus + */ + @Inject + public Core(EventBus eventBus) { + this.eventBus = eventBus; + } + + // + // ACTIONS + // + + /** + * Adds a channel to monitor. + * + * @param channel + * The channel to monitor + */ + public void addChannel(Channel channel) { + channels.add(channel); + } + + // + // ABSTRACTIDLESERVICE METHODS + // + + @Override + protected void startUp() { + for (Channel channel : channels) { + logger.info(String.format("Connecting to Channel %s on Network %s…", channel.name(), channel.network().name())); + if (!networkConnections.containsKey(channel.network())) { + /* select a random server. */ + List servers = Lists.newArrayList(channel.network().servers()); + Server server = servers.get((int) (Math.random() * servers.size())); + Connection connection = new ConnectionBuilder(eventBus).connect(server.hostname()).port(server.unencryptedPorts().iterator().next()).build(); + connection.username(RandomNickname.get()).realName(RandomNickname.get()); + networkConnections.put(channel.network(), connection); + connection.start(); + } + } + } + + @Override + protected void shutDown() { + } + + // + // EVENT HANDLERS + // + + /** + * If a connection to a network has been established, the channels associated + * with this network are joined. + * + * @param connectionEstablished + * The connection established event + */ + @Subscribe + public void connectionEstablished(ConnectionEstablished connectionEstablished) { + + /* get network for connection. */ + Optional network = getNetwork(connectionEstablished.connection()); + + /* found network? */ + if (!network.isPresent()) { + return; + } + + /* join all channels on this network. */ + for (Channel channel : channels) { + if (channel.network().equals(network)) { + try { + connectionEstablished.connection().joinChannel(channel.name()); + } catch (IOException ioe1) { + logger.log(Level.WARNING, String.format("Could not join %s on %s!", channel.name(), network.get().name()), ioe1); + } + } + } + } + + /** + * If a message on a channel is received, it is parsed for pack information + * with is then added to a bot. + * + * @param channelMessageReceived + * The channel message received event + */ + @Subscribe + public void channelMessageReceived(ChannelMessageReceived channelMessageReceived) { + String message = MessageCleaner.getDefaultInstance().clean(channelMessageReceived.message()); + if (!message.startsWith("#")) { + /* most probably not a pack announcement. */ + return; + } + + Optional network = getNetwork(channelMessageReceived.connection()); + if (!network.isPresent()) { + /* message for unknown connection? */ + return; + } + + Bot bot; + synchronized (networkBots) { + if (!networkBots.contains(network.get(), channelMessageReceived.source().nick().get())) { + networkBots.put(network.get(), channelMessageReceived.source().nick().get(), new Bot(network.get()).name(channelMessageReceived.source().nick().get())); + } + bot = networkBots.get(network.get(), channelMessageReceived.source().nick().get()); + } + + /* parse pack information. */ + Optional pack = parsePack(message); + if (!pack.isPresent()) { + return; + } + + /* add pack. */ + bot.addPack(pack.get()); + logger.fine(String.format("Bot %s now has %d packs.", bot, bot.packs().size())); + } + + // + // PRIVATE METHODS + // + + /** + * Searches all current connections for the given connection, returning the + * associated network. + * + * @param connection + * The connection to get the network for + * @return The network belonging to the connection, or {@link + * Optional#absent()} + */ + private Optional getNetwork(Connection connection) { + for (Entry networkConnectionEntry : networkConnections.entrySet()) { + if (networkConnectionEntry.getValue().equals(connection)) { + return Optional.of(networkConnectionEntry.getKey()); + } + } + return Optional.absent(); + } + + /** + * Parses {@link Pack} information from the given message. + * + * @param message + * The message to parse pack information from + * @return The parsed pack, or {@link Optional#absent()} if the message could + * not be parsed into a pack + */ + private Optional parsePack(String message) { + int squareOpen = message.indexOf('['); + int squareClose = message.indexOf(']', squareOpen); + if ((squareOpen == -1) && (squareClose == -1)) { + return Optional.absent(); + } + String packSize = message.substring(squareOpen + 1, squareClose); + String packName = message.substring(message.lastIndexOf(' ') + 1); + String packIndex = message.substring(0, message.indexOf(' ')).substring(1); + return Optional.of(new Pack(packIndex, packSize, packName)); + } + +} -- 2.7.4