001: package dalma.endpoints.irc;
002:
003: import dalma.impl.EndPointImpl;
004: import dalma.EndPoint;
005: import f00f.net.irc.martyr.IRCConnection;
006: import f00f.net.irc.martyr.commands.MessageCommand;
007: import f00f.net.irc.martyr.commands.NickCommand;
008: import f00f.net.irc.martyr.commands.RawCommand;
009: import f00f.net.irc.martyr.commands.InviteCommand;
010: import f00f.net.irc.martyr.services.AutoRegister;
011: import f00f.net.irc.martyr.services.AutoResponder;
012:
013: import java.util.Collection;
014: import java.util.Hashtable;
015: import java.util.Map;
016: import java.util.logging.Level;
017: import java.util.logging.Logger;
018:
019: /**
020: * {@link EndPoint} that connects to the internet relay chat system.
021: *
022: * @author Kohsuke Kawaguchi
023: */
024: public class IRCEndPoint extends EndPointImpl {
025: /**
026: * Active {@link Channel} instances keyed by their names.
027: */
028: // channel names are case insensitive, so all lower in this map
029: private final Map<String, Channel> channels = new Hashtable<String, Channel>();
030:
031: /**
032: * Active {@link Buddy} instances keyed by their names.
033: */
034: private final Map<String, Buddy> buddies = new Hashtable<String, Buddy>();
035:
036: private static final Logger logger = Logger
037: .getLogger(IRCEndPoint.class.getName());
038:
039: /*package*/final IRCConnection connection = new IRCConnection();
040:
041: private NewSessionListener newSessionListener;
042:
043: private final AutoReconnectEx autoReconnect;
044:
045: /**
046: * Creates a new {@link IRCEndPoint}.
047: *
048: * @param endpointName
049: * A unique endpoint name. See {@link EndPoint#getName()}.
050: * @param ircServer
051: * The IP address or the host name of the IRC server to connect to,
052: * such as "irc.freenode.net"
053: * @param port
054: * The TCP port number to connect to. Normally 6667.
055: * @param nickname
056: * The nickname that this endpoint will register to the IRC server.
057: */
058: public IRCEndPoint(String endpointName, String ircServer, int port,
059: String nickname) {
060: super (endpointName);
061:
062: // AutoRegister and AutoResponder both add themselves to the
063: // appropriate observerables. Both will remove themselves with the
064: // disable() method.
065: new AutoRegister(connection, nickname, nickname, nickname);
066: new AutoResponder(connection);
067: autoReconnect = new AutoReconnectEx(connection, ircServer, port);
068: connection.addCommandObserver(new MessageListener(this ));
069:
070: // TODO: the nickname we ask might be different from the nickname
071: // that we get approved!
072: }
073:
074: /**
075: * The same as {@link #IRCEndPoint(String, String, int, String)} ,
076: * except that it uses the IRC's default port number 6667.
077: */
078: public IRCEndPoint(String endpointName, String ircServer,
079: String nickname) {
080: this (endpointName, ircServer, 6667, nickname);
081: }
082:
083: protected void start() {
084: autoReconnect.go();
085: }
086:
087: protected void stop() {
088: connection.disconnect();
089: }
090:
091: public NewSessionListener getNewSessionListener() {
092: return newSessionListener;
093: }
094:
095: public void setNewSessionListener(
096: NewSessionListener newSessionListener) {
097: this .newSessionListener = newSessionListener;
098: }
099:
100: /**
101: * Sets the away status.
102: */
103: public void setAway(String status) {
104: connection.sendCommand(new RawCommand("AWAY", status));
105: }
106:
107: /**
108: * Sets the new nick name.
109: */
110: public void setNick(String nickname) {
111: connection.sendCommand(new NickCommand(nickname));
112: }
113:
114: /**
115: * Lists all the {@link Channel}s on the current server.
116: */
117: public Collection<Channel> listChannels() {
118: // TODO: implement this method later
119: throw new UnsupportedOperationException();
120: }
121:
122: /**
123: * Called when a new message is received from IRC.
124: */
125: /*package*/void onMessageReceived(MessageCommand cmd) {
126: String dest = cmd.getDest().toLowerCase();
127:
128: // figure out the sender
129: Buddy sender = getBuddy(cmd.getSource().getNick());
130:
131: // is this message for me persnoally?
132: if (cmd.isPrivateToUs(connection.getClientState())) {
133: Message msg = new Message(sender, cmd.getMessage(), null);
134:
135: PrivateChat chat = sender.getChat();
136: if (chat != null) {
137: // route the message
138: chat.onMessageReceived(msg);
139: return;
140: } else {
141: // no chat session is going on with this user.
142: NewSessionListener sl = newSessionListener;
143: if (sl != null) {
144: // start a new chat session and let the handler know
145: chat = sender.openChat();
146: chat.onMessageReceived(msg);
147: sl.onNewPrivateChat(chat);
148: return;
149: } else {
150: // nobody seems to be interested in talking to you, sorry.
151: // just ignore the message
152: return;
153: }
154: }
155: }
156:
157: // otherwise it must be to a channel
158: Channel channel = channels.get(dest);
159: if (channel != null) {
160: channel.onMessageReceived(new Message(sender, cmd
161: .getMessage(), channel));
162: return;
163: }
164:
165: // is this possible!?
166: logger.log(Level.WARNING, "Unrecognized message: "
167: + cmd.renderParams());
168: }
169:
170: /**
171: * Called when an INVITE command is received from the IRC.
172: */
173: /*package*/void onInvite(InviteCommand cmd) {
174: // is this to us?
175: if (!cmd.getNick().equals(getNickName()))
176: return; // nope
177:
178: Channel ch = getChannel(cmd.getChannel());
179: if (ch.isJoined())
180: return; // already a member
181:
182: Buddy sender = getBuddy(cmd.getUser().getNick());
183:
184: NewSessionListener sl = newSessionListener;
185: if (sl != null) {
186: // start a new chat session and let the handler know
187: sl.onInvite(sender, ch);
188: } else {
189: // nobody seems to be interested in joining this channel.
190: // just ignore
191: }
192: }
193:
194: /**
195: * Gets the {@link Buddy} object that represents the given nick name.
196: *
197: * <p>
198: * This method succeds even if no such user exists.
199: *
200: * @param nickname
201: * The IRC nickname of the buddy, like "kohsuke".
202: * @return
203: * always non-null.
204: */
205: public Buddy getBuddy(String nickname) {
206: synchronized (buddies) {
207: Buddy buddy = buddies.get(nickname);
208: if (buddy == null) {
209: buddy = new Buddy(this , nickname);
210: buddies.put(nickname, buddy);
211: }
212: return buddy;
213: }
214: }
215:
216: /**
217: * Gets the {@link Channel} object that represents given channel name.
218: */
219: public Channel getChannel(String channelName) {
220: // TODO: when do we join?
221: synchronized (channels) {
222: Channel ch = channels.get(channelName);
223: if (ch == null) {
224: ch = new Channel(this , channelName);
225: channels.put(channelName, ch);
226: }
227: return ch;
228: }
229: }
230:
231: /**
232: * Gets the IRC nickname that this endpoint uses.
233: *
234: * <p>
235: * Normally this is the same as the nick name specified through the constructor,
236: * but if the specified nick name is already taken, we may end up getting a
237: * different nick name.
238: *
239: * @return
240: * never null.
241: */
242: public String getNickName() {
243: return connection.getClientState().getNick().getNick();
244: }
245: }
|