Browse Source

Add relaybot plugin.

Change-Id: I3155bb06d7a4852394dc86462214b773e35c4b27
Reviewed-on: http://gerrit.dmdirc.com/1003
Automatic-Compile: DMDirc Local Commits <dmdirc@googlemail.com>
Reviewed-by: Chris Smith <chris@dmdirc.com>
tags/0.6.4
Shane Mc Cormack 14 years ago
parent
commit
540968c464

+ 237
- 0
src/com/dmdirc/addons/relaybot/RelayBotPlugin.java View File

@@ -0,0 +1,237 @@
1
+/*
2
+ * Copyright (c) 2006-2009 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.relaybot;
24
+
25
+import com.dmdirc.interfaces.ActionListener;
26
+import com.dmdirc.actions.interfaces.ActionType;
27
+import com.dmdirc.actions.CoreActionType;
28
+import com.dmdirc.actions.ActionManager;
29
+import com.dmdirc.Channel;
30
+import com.dmdirc.Server;
31
+import com.dmdirc.ServerManager;
32
+import com.dmdirc.plugins.Plugin;
33
+import com.dmdirc.config.IdentityManager;
34
+import com.dmdirc.interfaces.ConfigChangeListener;
35
+import com.dmdirc.parser.interfaces.ChannelClientInfo;
36
+import com.dmdirc.parser.interfaces.Parser;
37
+import com.dmdirc.parser.irc.IRCParser;
38
+import java.util.ArrayList;
39
+import java.util.HashMap;
40
+import java.util.Map;
41
+
42
+/**
43
+ * This plugin makes certain relay bots less obnoxious looking.
44
+ *
45
+ * @author shane
46
+ */
47
+public class RelayBotPlugin extends Plugin implements ActionListener, ConfigChangeListener {
48
+    /** Known RelayChannelHandlers. */
49
+    private final Map<Channel, RelayChannelHandler> handlers = new HashMap<Channel, RelayChannelHandler>();
50
+
51
+    /**
52
+     * Creates a new instance of the RelayBotPlugin.
53
+     */
54
+    public RelayBotPlugin() {
55
+        super();
56
+    }
57
+
58
+    /** {@inheritDoc} */
59
+    @Override
60
+    public void onLoad() {
61
+        ActionManager.addListener(this, CoreActionType.CHANNEL_OPENED);
62
+        ActionManager.addListener(this, CoreActionType.CHANNEL_CLOSED);
63
+        ActionManager.addListener(this, CoreActionType.SERVER_CONNECTED);
64
+        ActionManager.addListener(this, CoreActionType.SERVER_DISCONNECTED);
65
+        ActionManager.addListener(this, CoreActionType.CHANNEL_QUIT);
66
+
67
+        // Add ourself to all currently known channels that we should be
68
+        // connected with.
69
+        for (Server server : ServerManager.getServerManager().getServers()) {
70
+            final Parser parser = server.getParser();
71
+            if (parser instanceof IRCParser && !(parser.getCallbackManager() instanceof RelayCallbackManager)) {
72
+                new RelayCallbackManager(this, (IRCParser)parser);
73
+            }
74
+            for (String channel : server.getChannels()) {
75
+                final Channel chan = server.getChannel(channel);
76
+                final String channelName = chan.getServer().getParser().getStringConverter().toLowerCase(chan.getName());
77
+                getHandler(chan);
78
+            }
79
+        }
80
+    }
81
+    
82
+    /** {@inheritDoc} */
83
+    @Override
84
+    public void onUnload() {
85
+        ActionManager.removeListener(this);
86
+
87
+        // Remove RelayCallbackManagers
88
+        for (Server server : ServerManager.getServerManager().getServers()) {
89
+            final Parser parser = server.getParser();
90
+            if (parser instanceof IRCParser && parser.getCallbackManager() instanceof RelayCallbackManager) {
91
+                ((RelayCallbackManager) parser.getCallbackManager()).onSocketClosed(parser);
92
+            }
93
+        }
94
+
95
+        // Remove from all channels.
96
+        synchronized (handlers) {
97
+            for (RelayChannelHandler handler : new ArrayList<RelayChannelHandler>(handlers.values())) {
98
+                handler.restoreCoreChannelHandler();
99
+            }
100
+            handlers.clear();
101
+        }
102
+    }
103
+
104
+    /**
105
+     * Process an event of the specified type.
106
+     *
107
+     * @param type The type of the event to process
108
+     * @param format Format of messages that are about to be sent. (May be null)
109
+     * @param arguments The arguments for the event
110
+     */
111
+    @Override
112
+    public void processEvent(final ActionType type, final StringBuffer format, final Object... arguments) {
113
+        if (type == CoreActionType.CHANNEL_OPENED) {
114
+            final Channel chan = (Channel)arguments[0];
115
+            
116
+            final String channelName = chan.getServer().getParser().getStringConverter().toLowerCase(chan.getName());
117
+            getHandler(chan);
118
+        } else if (type == CoreActionType.CHANNEL_CLOSED) {
119
+            final Channel chan = (Channel)arguments[0];
120
+            removeHandler(chan);
121
+        } else if (type == CoreActionType.CHANNEL_QUIT) {
122
+            final Channel chan = (Channel)arguments[0];
123
+            final Parser parser = chan.getServer().getParser();
124
+            final ChannelClientInfo cci = (ChannelClientInfo)arguments[1];
125
+            final String channelName = parser.getStringConverter().toLowerCase(chan.getName());
126
+
127
+            if (IdentityManager.getGlobalConfig().hasOptionString(getDomain(), channelName)) {
128
+                final String botName = IdentityManager.getGlobalConfig().getOption(getDomain(), channelName);
129
+                if (parser.getStringConverter().equalsIgnoreCase(botName, cci.getClient().getNickname())) {
130
+                    // The bot quit :(
131
+                    final RelayChannelHandler handler = getHandler(chan);
132
+                    if (handler != null) {
133
+                        handler.updateNames();
134
+                    }
135
+                }
136
+            }
137
+        } else if (type == CoreActionType.SERVER_CONNECTED) {
138
+            final Server server = (Server)arguments[0];
139
+
140
+            final Parser parser = server.getParser();
141
+            if (parser instanceof IRCParser && !(parser.getCallbackManager() instanceof RelayCallbackManager)) {
142
+                new RelayCallbackManager(this, (IRCParser)parser);
143
+            }
144
+        } else if (type == CoreActionType.SERVER_DISCONNECTED) {
145
+            final Server server = (Server)arguments[0];
146
+
147
+            // RelayCallbackManager will revert itself when this happens.
148
+
149
+            // Unset any listeners for channels of this server
150
+            for (String channel : server.getChannels()) {
151
+                final Channel chan = server.getChannel(channel);
152
+                final String channelName = chan.getServer().getParser().getStringConverter().toLowerCase(chan.getName());
153
+                removeHandler(chan);
154
+            }
155
+        }
156
+    }
157
+
158
+    /** {@inheritDoc} */
159
+    @Override
160
+    public void configChanged(final String domain, final String key) {
161
+        if (!domain.equals(getDomain())) { return; }
162
+        final boolean wasUnset = !(IdentityManager.getGlobalConfig().hasOptionString(domain, key));
163
+
164
+        for (Server server : ServerManager.getServerManager().getServers()) {
165
+            if (server.hasChannel(key)) {
166
+                final Channel chan = server.getChannel(key);
167
+                if (wasUnset) {
168
+                    removeHandler(chan);
169
+                } else {
170
+                    getHandler(chan);
171
+                }
172
+            }
173
+        }
174
+    }
175
+
176
+    /**
177
+     * Do we have an intercept for the given channel object?
178
+     * If we have one, we will return true.
179
+     * If we should have one (as determined by checking the config) we will add
180
+     * one and return true.
181
+     * 
182
+     * @param channel
183
+     * @return true or false
184
+     */
185
+    public boolean isListening(final Channel channel) {
186
+        return (getHandler(channel) != null);
187
+    }
188
+
189
+    /**
190
+     * Get the RelayChannelHandler for a given Channel.
191
+     * If we have one, we will return it.
192
+     * If we should have one (as determined by checking the config) we will
193
+     * create and return it.
194
+     * Otherwise we will return null.
195
+     *
196
+     * @param channel Channel to get Handler for.
197
+     * @return ChannelHandler or null
198
+     */
199
+    public RelayChannelHandler getHandler(final Channel channel) {
200
+        synchronized (handlers) {
201
+            if (handlers.containsKey(channel)) {
202
+                return handlers.get(channel);
203
+            } else {
204
+                final String channelName = channel.getServer().getParser().getStringConverter().toLowerCase(channel.getName());
205
+                if (IdentityManager.getGlobalConfig().hasOptionString(getDomain(), channelName)) {
206
+                    final RelayChannelHandler handler = new RelayChannelHandler(this, channel);
207
+                    handlers.put(channel, handler);
208
+                    return handler;
209
+                }
210
+            }
211
+        }
212
+
213
+        return null;
214
+    }
215
+
216
+    /**
217
+     * Remove the RelayChannelHandler for a given Channel.
218
+     * If we have one already, we will return it and remove it.
219
+     * Otherwise we will return null.
220
+     *
221
+     * @param channel Channel to remove Handler for.
222
+     * @return Handler that we removed, or null.
223
+     */
224
+    public RelayChannelHandler removeHandler(final Channel channel) {
225
+        synchronized (handlers) {
226
+            if (handlers.containsKey(channel)) {
227
+                final RelayChannelHandler handler = handlers.get(channel);
228
+                handler.unset();
229
+                handlers.remove(channel);
230
+
231
+                return handler;
232
+            }
233
+        }
234
+
235
+        return null;
236
+    }
237
+}

+ 182
- 0
src/com/dmdirc/addons/relaybot/RelayCallbackManager.java View File

@@ -0,0 +1,182 @@
1
+/*
2
+ * Copyright (c) 2006-2009 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.relaybot;
24
+
25
+import com.dmdirc.Channel;
26
+import com.dmdirc.ChannelEventHandler;
27
+import com.dmdirc.parser.common.CallbackManager;
28
+import com.dmdirc.parser.common.CallbackNotFoundException;
29
+import com.dmdirc.parser.common.CallbackObject;
30
+import com.dmdirc.parser.common.CallbackObjectSpecific;
31
+import com.dmdirc.parser.interfaces.Parser;
32
+import com.dmdirc.parser.interfaces.callbacks.CallbackInterface;
33
+import com.dmdirc.parser.interfaces.callbacks.ChannelMessageListener;
34
+import com.dmdirc.parser.irc.IRCCallbackManager;
35
+import com.dmdirc.parser.irc.IRCParser;
36
+import com.dmdirc.parser.interfaces.callbacks.SocketCloseListener;
37
+import java.lang.reflect.Field;
38
+import java.util.Map;
39
+
40
+/**
41
+ * This is a callback manager that when created will replace the callback
42
+ * manager of the given parser with itself.
43
+ *
44
+ * When a new callback is added it will be allowed unless the RelayBotPlugin
45
+ * says otherwise.
46
+ *
47
+ * When the parser disconnects, the original CBM is restored.
48
+ *
49
+ * @author shane
50
+ */
51
+public class RelayCallbackManager extends IRCCallbackManager implements SocketCloseListener {
52
+
53
+    /** Pluign that created this callback manager. */
54
+    private final RelayBotPlugin myPlugin;
55
+
56
+    /** Original CallbackManager */
57
+    private final IRCCallbackManager originalCBM;
58
+
59
+    /**
60
+     * Create a new RelayCallbackManager and replace
61
+     *
62
+     * @param myPlugin
63
+     * @param parser
64
+     */
65
+    public RelayCallbackManager(final RelayBotPlugin myPlugin, final IRCParser parser) {
66
+        super(parser);
67
+
68
+        this.myPlugin = myPlugin;
69
+        this.originalCBM = (IRCCallbackManager) parser.getCallbackManager();
70
+        setCallbackManager(parser, this);
71
+        addCallback(SocketCloseListener.class, this);
72
+    }
73
+
74
+    /** {@inheritDoc} */
75
+    @Override
76
+    public <T extends CallbackInterface> void addCallback(final Class<T> callback, final T o, final String target) throws CallbackNotFoundException {
77
+        // Don't allow the core to give itself a ChannelMessageListener if we
78
+        // already have a listener for this channel.
79
+        if (o instanceof ChannelEventHandler && callback == ChannelMessageListener.class) {
80
+            try {
81
+                // Get the old callback manager
82
+                final Field field = o.getClass().getDeclaredField("owner");
83
+                field.setAccessible(true);
84
+                @SuppressWarnings("unchecked")
85
+                final Channel channel = (Channel) field.get(o);
86
+
87
+                if (myPlugin.isListening(channel)) {
88
+                    return;
89
+                }
90
+            } catch (NoSuchFieldException ex) {
91
+                ex.printStackTrace();
92
+            } catch (SecurityException ex) {
93
+                ex.printStackTrace();
94
+            } catch (IllegalArgumentException ex) {
95
+                ex.printStackTrace();
96
+            } catch (IllegalAccessException ex) {
97
+                ex.printStackTrace();
98
+            }
99
+        }
100
+
101
+        // Unless the plugin says we are listening instead of this channel, then
102
+        // we can add the callback.
103
+        forceAddCallback(callback, o, target);
104
+    }
105
+
106
+    /**
107
+     * Add a callback with a specific target.
108
+     * This method will throw a CallbackNotFoundException if the callback does not exist.
109
+     *
110
+     * @param <T> The type of callback
111
+     * @param callback Type of callback object.
112
+     * @param o instance of ICallbackInterface to add.
113
+     * @param target Parameter to specify that a callback should only fire for specific things
114
+     * @throws CallbackNotFoundException If callback is not found.
115
+     * @throws NullPointerException If 'o' is null
116
+     */
117
+    public <T extends CallbackInterface> void forceAddCallback(final Class<T> callback, final T o, final String target) throws CallbackNotFoundException {
118
+        super.addCallback(callback, o, target);
119
+    }
120
+
121
+
122
+    /**
123
+     * Set the Callback Manager of a given parser.
124
+     *
125
+     * @param parser
126
+     * @param cbm
127
+     */
128
+    private void setCallbackManager(final IRCParser parser, final CallbackManager<IRCParser> cbm) {
129
+        try {
130
+            // Get the old callback manager
131
+            final Field field = parser.getClass().getDeclaredField("myCallbackManager");
132
+            field.setAccessible(true);
133
+            @SuppressWarnings("unchecked")
134
+            final CallbackManager<IRCParser> oldCBM = (CallbackManager<IRCParser>) field.get(parser);
135
+
136
+            // Clone the known CallbackObjects list (horrible code ahoy!)
137
+            // First get the old map of callbacks
138
+            final Field cbField = CallbackManager.class.getDeclaredField("callbackHash");
139
+            cbField.setAccessible(true);
140
+            @SuppressWarnings("unchecked")
141
+            final Map<Class<? extends CallbackInterface>, CallbackObject> oldCallbackHash = (Map<Class<? extends CallbackInterface>, CallbackObject>) cbField.get(oldCBM);
142
+
143
+            // Clear my map of callbacks
144
+            @SuppressWarnings("unchecked")
145
+            final Map<Class<? extends CallbackInterface>, CallbackObject> myCallbackHash = (Map<Class<? extends CallbackInterface>, CallbackObject>) cbField.get(this);
146
+            myCallbackHash.clear();
147
+
148
+            // Now add them all to the new cbm.
149
+            for (CallbackObject callback : oldCallbackHash.values()) {
150
+                // Change their manager to the new one.
151
+                final Field ownerField = CallbackObject.class.getDeclaredField("myManager");
152
+                ownerField.setAccessible(true);
153
+                ownerField.set(callback, cbm);
154
+
155
+                // And add them to the CBM
156
+                cbm.addCallbackType(callback);
157
+            }
158
+
159
+            // Replace the old one with the new one.
160
+            field.set(parser, cbm);
161
+        } catch (IllegalArgumentException ex) {
162
+            ex.printStackTrace();
163
+        } catch (IllegalAccessException ex) {
164
+            ex.printStackTrace();
165
+        } catch (NoSuchFieldException ex) {
166
+            ex.printStackTrace();
167
+        } catch (SecurityException ex) {
168
+            ex.printStackTrace();
169
+        }
170
+    }
171
+
172
+    /**
173
+     * When the socket closes, reset the callback manager.
174
+     *
175
+     * @param parser
176
+     */
177
+    public void onSocketClosed(final Parser parser) {
178
+        if (parser.getCallbackManager() instanceof RelayCallbackManager) {
179
+            setCallbackManager((IRCParser)parser, originalCBM);
180
+        }
181
+    }
182
+}

+ 349
- 0
src/com/dmdirc/addons/relaybot/RelayChannelHandler.java View File

@@ -0,0 +1,349 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.relaybot;
24
+
25
+import com.dmdirc.parser.interfaces.callbacks.ChannelMessageListener;
26
+import com.dmdirc.Channel;
27
+import com.dmdirc.ChannelEventHandler;
28
+import com.dmdirc.config.IdentityManager;
29
+import com.dmdirc.parser.interfaces.ChannelClientInfo;
30
+import com.dmdirc.parser.interfaces.ChannelInfo;
31
+import com.dmdirc.parser.interfaces.Parser;
32
+import com.dmdirc.parser.irc.IRCCallbackManager;
33
+import com.dmdirc.parser.irc.IRCChannelClientInfo;
34
+import com.dmdirc.parser.irc.IRCParser;
35
+import com.dmdirc.plugins.PluginClassLoader;
36
+import com.dmdirc.plugins.PluginInfo;
37
+import com.dmdirc.plugins.PluginManager;
38
+import com.dmdirc.ui.messages.Styliser;
39
+import java.lang.reflect.Field;
40
+import java.lang.reflect.Method;
41
+import java.util.HashMap;
42
+import java.util.Map;
43
+
44
+/**
45
+ * This class replaces the ChannelHandler in the core to allow intercepting
46
+ * callbacks from the parser.
47
+ *
48
+ * This allows us to hide them from the core and send faked ones instead.
49
+ *
50
+ * @author shane
51
+ */
52
+public class RelayChannelHandler implements ChannelMessageListener {
53
+    /** My Channel. */
54
+    private final Channel myChannel;
55
+
56
+    /** Core channel handler. */
57
+    private final ChannelEventHandler coreChannelHandler;
58
+
59
+    /** Plugin that owns this RelayChannelHandler. */
60
+    private final RelayBotPlugin myPlugin;
61
+
62
+    /** Known ChannelClients */
63
+    private final Map<String, IRCChannelClientInfo> channelClients = new HashMap<String, IRCChannelClientInfo>();
64
+
65
+    /**
66
+     * Create a new RelayChannelHandler.
67
+     *
68
+     * @param myPlugin 
69
+     * @param myChannel channel to hax!
70
+     */
71
+    public RelayChannelHandler(final RelayBotPlugin myPlugin, final Channel myChannel) {
72
+        this.myChannel = myChannel;
73
+        this.myPlugin = myPlugin;
74
+        ChannelEventHandler ceh = null;
75
+        try {
76
+            // Get the core Channel handler.
77
+            final Field field = myChannel.getClass().getDeclaredField("eventHandler");
78
+            field.setAccessible(true);
79
+            ceh = (ChannelEventHandler) field.get(myChannel);
80
+        } catch (IllegalArgumentException ex) {
81
+            ex.printStackTrace();
82
+        } catch (IllegalAccessException ex) {
83
+            ex.printStackTrace();
84
+        } catch (NoSuchFieldException ex) {
85
+            ex.printStackTrace();
86
+        } catch (SecurityException ex) {
87
+            ex.printStackTrace();
88
+        }
89
+
90
+        coreChannelHandler = ceh;
91
+
92
+        if (coreChannelHandler != null) {
93
+            final IRCCallbackManager cbm = (IRCCallbackManager) myChannel.getServer().getParser().getCallbackManager();
94
+            cbm.delCallback(ChannelMessageListener.class, coreChannelHandler);
95
+            cbm.addCallback(ChannelMessageListener.class, this, myChannel.getName());
96
+        }
97
+    }
98
+
99
+
100
+    /**
101
+     * Get a ChannelClient object for the given nick@server.
102
+     *
103
+     * @param channel Channel
104
+     * @param nick Nickname to get channel client for.
105
+     * @param sendJoinIfNew
106
+     * @return Requested ChannelClient Info.
107
+     */
108
+    private IRCChannelClientInfo getChannelClient(final ChannelInfo channel, final String nick, final boolean sendJoinIfNew) {
109
+        final Parser parser = channel.getParser();
110
+        final String storeName = parser.getStringConverter().toLowerCase(nick);
111
+        synchronized (channelClients) {
112
+            if (!channelClients.containsKey(storeName)) {
113
+                final RelayClientInfo client = new RelayClientInfo(channel.getParser(), nick);
114
+                final IRCChannelClientInfo newChannelClient = new IRCChannelClientInfo((IRCParser)channel.getParser(), client, channel);
115
+                colourClient(newChannelClient);
116
+
117
+                channelClients.put(storeName, newChannelClient);
118
+                if (sendJoinIfNew && !client.isServer()) {
119
+                    coreChannelHandler.onChannelJoin(parser, channel, newChannelClient);
120
+                    // The nickcolour plugin colours the nicknames on join
121
+                    // and uses nickname@server when colouring rather than
122
+                    // the setting the user wanted, we can recolour here to
123
+                    // fix that.
124
+                    colourClient(newChannelClient);
125
+                }
126
+            }
127
+
128
+            return channelClients.get(storeName);
129
+        }
130
+    }
131
+
132
+    /**
133
+     * Remove a stored ChannelClient
134
+     *
135
+     * @param channel Channel
136
+     * @param nick Nickname to get channel client for.
137
+     */
138
+    private void removeChannelClient(final ChannelInfo channel, final String nick) {
139
+        final Parser parser = channel.getParser();
140
+        final String storeName = parser.getStringConverter().toLowerCase(nick);
141
+        synchronized (channelClients) {
142
+            channelClients.remove(storeName);
143
+        }
144
+    }
145
+
146
+    /**
147
+     * Rename a stored ChannelClient
148
+     *
149
+     * @param channelClient ChannelClient
150
+     * @param newNick new Nickname
151
+     */
152
+    private void renameChannelClient(final IRCChannelClientInfo channelClient, final String newNick) {
153
+        final Parser parser = channelClient.getChannel().getParser();
154
+        final String storeName = parser.getStringConverter().toLowerCase(newNick);
155
+
156
+        synchronized (channelClients) {
157
+            channelClients.remove(channelClient.getClient().toString());
158
+            ((RelayClientInfo) channelClient.getClient()).changeNickname(newNick);
159
+            channelClients.put(storeName,channelClient);
160
+        }
161
+    }
162
+
163
+    /**
164
+     * Restore the callback handling to the coreChannelHandler.
165
+     */
166
+    public void restoreCoreChannelHandler() {
167
+        if (coreChannelHandler != null) {
168
+            final IRCCallbackManager cbm = (IRCCallbackManager) myChannel.getServer().getParser().getCallbackManager();
169
+
170
+            // Force adding this callback to the CBM.
171
+            if (cbm instanceof RelayCallbackManager) {
172
+                ((RelayCallbackManager) cbm).forceAddCallback(ChannelMessageListener.class, coreChannelHandler, myChannel.getName());
173
+            } else {
174
+                cbm.addCallback(ChannelMessageListener.class, coreChannelHandler, myChannel.getName());
175
+            }
176
+            unset();
177
+            updateNames();
178
+        }
179
+    }
180
+
181
+    /**
182
+     * Remove channel message handling from this ChannelHandler
183
+     */
184
+    public void unset() {
185
+        myChannel.getServer().getParser().getCallbackManager().delCallback(ChannelMessageListener.class, this);
186
+    }
187
+
188
+    /**
189
+    * Colour a client as needed.
190
+    *
191
+    * @param channelClient Client to colour
192
+    */
193
+    private void colourClient(final ChannelClientInfo channelClient) {
194
+        // Use nick colour plugin to colour the client if available.
195
+        final PluginInfo nickColour = PluginManager.getPluginManager().getPluginInfoByName("nickcolour");
196
+
197
+        final boolean fullColour = IdentityManager.getGlobalConfig().getOptionBool(myPlugin.getDomain(), "colourFullName");
198
+        final RelayClientInfo client = (RelayClientInfo)channelClient.getClient();
199
+        final boolean oldValue = client.getShowFullNickname();
200
+        client.setShowFullNickname(fullColour);
201
+
202
+        if (nickColour != null && nickColour.isLoaded()) {
203
+            try {
204
+                final Method gpcl = PluginInfo.class.getDeclaredMethod("getPluginClassLoader");
205
+                gpcl.setAccessible(true);
206
+                final PluginClassLoader pcl = (PluginClassLoader) gpcl.invoke(nickColour);
207
+
208
+                final Class<?> nc = nickColour.getPlugin().getClass();
209
+                final Method colourClient = nc.getDeclaredMethod("colourClient", new Class<?>[]{String.class, ChannelClientInfo.class});
210
+                colourClient.setAccessible(true);
211
+                colourClient.invoke(nickColour.getPlugin(), myChannel.getServer().getNetwork(), channelClient);
212
+            } catch (Throwable t) { /* If it can't colour then oh well. */ }
213
+        }
214
+        
215
+        client.setShowFullNickname(oldValue);
216
+    }
217
+
218
+
219
+    /**
220
+     * Handle the IRCParsers incoming message.
221
+     *
222
+     * @param parser Parser that sent the message
223
+     * @param channel Channel the message went to
224
+     * @param channelClient Client who send the message
225
+     * @param message Message content
226
+     * @param host Host of client
227
+     */
228
+    @Override
229
+    public void onChannelMessage(final Parser parser, final ChannelInfo channel, final ChannelClientInfo channelClient, final String message, final String host) {
230
+        final String channelName = parser.getStringConverter().toLowerCase(channel.getName());
231
+        String botName;
232
+        try {
233
+            botName = IdentityManager.getGlobalConfig().getOption(myPlugin.getDomain(), channelName);
234
+        } catch (IllegalArgumentException iae) {
235
+            botName = "";
236
+        }
237
+        final boolean isBot = parser.getStringConverter().equalsIgnoreCase(botName, channelClient.getClient().getNickname());
238
+
239
+        final boolean joinNew = IdentityManager.getGlobalConfig().getOptionBool(myPlugin.getDomain(), "joinOnDiscover");
240
+
241
+        // See if we need to modify this message
242
+        if (channelClient instanceof IRCChannelClientInfo && !botName.isEmpty() && isBot) {
243
+            final String[] bits = message.split(" ", 2);
244
+            final String initial = Styliser.stipControlCodes(bits[0]);
245
+
246
+            if (initial.charAt(0) == '+') {
247
+                // Channel Message
248
+                final IRCChannelClientInfo newChannelClient = getChannelClient(channel, bits[0].substring(2, bits[0].length() - 1), joinNew);
249
+
250
+                coreChannelHandler.onChannelMessage(parser, channel, newChannelClient, bits[1], host);
251
+                return;
252
+            } else if (initial.equalsIgnoreCase("***")) {
253
+                // Some kind of state-changing action
254
+                final String[] newBits = bits[1].split(" ");
255
+
256
+                if (newBits.length > 2) {
257
+                    final IRCChannelClientInfo newChannelClient = getChannelClient(channel, newBits[0], joinNew);
258
+
259
+                    if (newBits[2].equalsIgnoreCase("joined")) {
260
+                        // User joined a relayed channel.
261
+                        if (!joinNew) {
262
+                            // If auto join on discover isn't enabled, we will
263
+                            // need to send this join, else it will already
264
+                            // have been sent.
265
+                            coreChannelHandler.onChannelJoin(parser, channel, newChannelClient);
266
+                            // And recolour to combat the nickcolour plugin
267
+                            // changing the colour on join.
268
+                            colourClient(newChannelClient);
269
+                        }
270
+                        return;
271
+                    } else if (newBits[2].equalsIgnoreCase("left")) {
272
+                        // User left a relayed channel.
273
+                        String reason = (newBits.length > 4) ? mergeBits(newBits, 4, newBits.length - 1, " ") : "()";
274
+                        reason = reason.substring(1, reason.length() - 1);
275
+
276
+                        coreChannelHandler.onChannelPart(parser, channel, newChannelClient, reason);
277
+                        removeChannelClient(channel, newBits[0]);
278
+                        return;
279
+                    } else if (newBits[2].equalsIgnoreCase("quit")) {
280
+                        // User quit a relayed channel.
281
+                        String reason = (newBits.length > 4) ? mergeBits(newBits, 4, newBits.length - 1, " ") : "()";
282
+                        reason = reason.substring(1, reason.length() - 1);
283
+
284
+                        coreChannelHandler.onChannelQuit(parser, channel, newChannelClient, reason);
285
+                        removeChannelClient(channel, newBits[0]);
286
+                        return;
287
+                    } else if (newBits[2].equalsIgnoreCase("kicked")) {
288
+                        // User was kicked from a relayed channel.
289
+                        String reason = (newBits.length > 7) ? mergeBits(newBits, 7, newBits.length - 1, " ") : "()";
290
+                        reason = reason.substring(1, reason.length() - 1);
291
+
292
+                        final IRCChannelClientInfo kickingChannelClient = (newBits.length > 6) ? getChannelClient(channel, newBits[6], joinNew) : null;
293
+
294
+                        coreChannelHandler.onChannelKick(parser, channel, newChannelClient, kickingChannelClient, reason, "");
295
+                        removeChannelClient(channel, newBits[0]);
296
+                        return;
297
+                    } else if (newBits[2].equalsIgnoreCase("now")) {
298
+                        // User changed their nickname in a relayed channel.
299
+                        if (newBits.length > 3) {
300
+                            renameChannelClient(newChannelClient, newBits[3]);
301
+
302
+                            coreChannelHandler.onChannelNickChanged(parser, channel, newChannelClient, newBits[0]);
303
+                            return;
304
+                        }
305
+                    }
306
+                }
307
+            } else if (initial.charAt(0) == '*') {
308
+                // Channel Action
309
+                final String[] newBits = bits[1].split(" ", 2);
310
+
311
+                final IRCChannelClientInfo newChannelClient = getChannelClient(channel, newBits[0], joinNew);
312
+
313
+                coreChannelHandler.onChannelAction(parser, channel, newChannelClient, newBits[1], "");
314
+                return;
315
+            }
316
+        }
317
+
318
+        // Pass it on unchanged.
319
+        coreChannelHandler.onChannelMessage(parser, channel, channelClient, message, host);
320
+    }
321
+
322
+    /**
323
+     * Merge the given bits.
324
+     *and
325
+     * @param bits Bits to merge
326
+     * @param start Start
327
+     * @param end end
328
+     * @param joiner What to use to join them
329
+     * @return Joined bits.
330
+     */
331
+    private String mergeBits(final String[] bits, final int start, final int end, final String joiner) {
332
+        final StringBuilder builder = new StringBuilder();
333
+        for (int i = start; i <= end; i++) {
334
+            if (bits.length < i) { break; }
335
+            if (i != start) { builder.append(joiner); }
336
+            builder.append(bits[i]);
337
+        }
338
+
339
+        return builder.toString();
340
+    }
341
+
342
+    /**
343
+     * Send an onChannelNames() event for this channel.
344
+     * This will cause all remote clients to vanish from the nicklist.
345
+     */
346
+    public void updateNames() {
347
+        coreChannelHandler.onChannelGotNames(myChannel.getServer().getParser(), myChannel.getChannelInfo());
348
+    }
349
+}

+ 96
- 0
src/com/dmdirc/addons/relaybot/RelayClientInfo.java View File

@@ -0,0 +1,96 @@
1
+/*
2
+ * Copyright (c) 2006-2009 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.relaybot;
24
+
25
+import com.dmdirc.parser.interfaces.Parser;
26
+import com.dmdirc.parser.irc.IRCClientInfo;
27
+import com.dmdirc.parser.irc.IRCParser;
28
+
29
+/**
30
+ * This class is used to proxy an IRCClientInfo and to show the nickname
31
+ * as nick@network.
32
+ */
33
+public class RelayClientInfo extends IRCClientInfo {
34
+    /** Shoulf getNickname() return the network aswell? */
35
+    private boolean showFullNickname = true;
36
+
37
+    /**
38
+     * Create a new RelayClientInfo
39
+     *
40
+     * @param parser Parser that owns this client
41
+     * @param host Host of client.
42
+     */
43
+    public RelayClientInfo(final Parser parser, final String host) {
44
+        super((IRCParser)parser, host);
45
+        super.setFake(true);
46
+    }
47
+
48
+    /**
49
+     * Should the full nickname (nick@server) be used by getNickname?
50
+     *
51
+     * @param value
52
+     */
53
+    public void setShowFullNickname(final boolean value) {
54
+        showFullNickname = value;
55
+    }
56
+
57
+    /**
58
+     * Will the full nickname (nick@server) be used by getNickname?
59
+     *
60
+     * @return true/false
61
+     */
62
+    public boolean getShowFullNickname() {
63
+        return showFullNickname;
64
+    }
65
+
66
+    /**
67
+     * Get the nickname of this client.
68
+     *
69
+     * @return The nickname of this client.
70
+     */
71
+    @Override
72
+    public String getNickname() {
73
+        return (showFullNickname) ? super.getNickname()+"@"+super.getHostname() : super.getNickname();
74
+    }
75
+
76
+    /**
77
+     * Change the nickname of this client.
78
+     *
79
+     * @param newNick new Nickname for this client.
80
+     */
81
+    public void changeNickname(final String newNick) {
82
+        setUserBits(newNick, true);
83
+    }
84
+
85
+    /** {@inheritDoc} */
86
+    @Override
87
+    public String getHostname() {
88
+        return "";
89
+    }
90
+
91
+    /** {@inheritDoc} */
92
+    @Override
93
+    public String getUsername() {
94
+        return "";
95
+    }
96
+}

+ 28
- 0
src/com/dmdirc/addons/relaybot/plugin.config View File

@@ -0,0 +1,28 @@
1
+# This is a DMDirc configuration file.
2
+
3
+# This section indicates which sections below take key/value
4
+# pairs, rather than a simple list. It should be placed above
5
+# any sections that take key/values.
6
+keysections:
7
+  metadata
8
+  version
9
+
10
+metadata:
11
+  author=Shane <shane@dmdirc.com>
12
+  mainclass=com.dmdirc.addon.relaybot.RelayBotPlugin
13
+  description=DMDirc support for relay bots.
14
+  name=relaybot
15
+  nicename=RelayBot Support
16
+
17
+updates:
18
+  id=50
19
+
20
+defaults:
21
+  colourFullName=true
22
+  joinOnDiscover=false
23
+  \#ircreport=IRelayBot
24
+  \#dmdirc=DMDirc
25
+
26
+version:
27
+  number=5
28
+  friendly=0.5

Loading…
Cancel
Save