-
Notifications
You must be signed in to change notification settings - Fork 16
/
PircBot.java
3200 lines (2845 loc) · 121 KB
/
PircBot.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
Copyright Paul James Mutton, 2001-2009, http://www.jibble.org/
This file is part of PircBot.
This software is dual-licensed, allowing you to choose between the GNU
General Public License (GPL) and the www.jibble.org Commercial License.
Since the GPL may be too restrictive for use in a proprietary application,
a commercial license is also provided. Full license information can be
found at http://www.jibble.org/licenses/
Modifications from PircBot 1.5 by David Lazar.
*/
package org.jibble.pircbot;
import java.io.*;
import java.net.*;
import java.util.*;
import javax.net.*;
import javax.net.ssl.*;
/**
* PircBot is a Java framework for writing IRC bots quickly and easily.
* <p>
* It provides an event-driven architecture to handle common IRC
* events, flood protection, DCC support, ident support, and more.
* The comprehensive logfile format is suitable for use with pisg to generate
* channel statistics.
* <p>
* Methods of the PircBot class can be called to send events to the IRC server
* that it connects to. For example, calling the sendMessage method will
* send a message to a channel or user on the IRC server. Multiple servers
* can be supported using multiple instances of PircBot.
* <p>
* To perform an action when the PircBot receives a normal message from the IRC
* server, you would override the onMessage method defined in the PircBot
* class. All on<i>XYZ</i> methods in the PircBot class are automatically called
* when the event <i>XYZ</i> happens, so you would override these if you wish
* to do something when it does happen.
* <p>
* Some event methods, such as onPing, should only really perform a specific
* function (i.e. respond to a PING from the server). For your convenience, such
* methods are already correctly implemented in the PircBot and should not
* normally need to be overridden. Please read the full documentation for each
* method to see which ones are already implemented by the PircBot class.
* <p>
* Please visit the PircBot homepage at
* <a href="http://www.jibble.org/pircbot.php">http://www.jibble.org/pircbot.php</a>
* for full revision history, a beginners guide to creating your first PircBot
* and a list of some existing Java IRC bots and clients that use the PircBot
* framework.
*
* @author Paul James Mutton,
* <a href="http://www.jibble.org/">http://www.jibble.org/</a>
* @version 1.5.0 (Build time: Mon Dec 14 20:07:17 2009)
*/
public abstract class PircBot implements ReplyConstants {
/**
* The definitive version number of this release of PircBot.
* (Note: Change this before automatically building releases)
*/
public static final String VERSION = "1.5.0";
private static final int OP_ADD = 1;
private static final int OP_REMOVE = 2;
private static final int VOICE_ADD = 3;
private static final int VOICE_REMOVE = 4;
/**
* Constructs a PircBot with the default settings. Your own constructors
* in classes which extend the PircBot abstract class should be responsible
* for changing the default settings if required.
*/
public PircBot() {}
/**
* Attempt to connect to the specified IRC server.
* The onConnect method is called upon success.
*
* @param hostname The hostname of the server to connect to.
*
* @throws IOException if it was not possible to connect to the server.
* @throws IrcException if the server would not let us join it.
* @throws NickAlreadyInUseException if our nick is already in use on the server.
*/
public final synchronized void connect(String hostname) throws IOException, IrcException, NickAlreadyInUseException {
ConnectionSettings cs = new ConnectionSettings(hostname);
this.connect(cs);
}
/**
* Attempt to connect to the specified IRC server and port number.
* The onConnect method is called upon success.
*
* @param hostname The hostname of the server to connect to.
* @param port The port number to connect to on the server.
*
* @throws IOException if it was not possible to connect to the server.
* @throws IrcException if the server would not let us join it.
* @throws NickAlreadyInUseException if our nick is already in use on the server.
*/
public final synchronized void connect(String hostname, int port) throws IOException, IrcException, NickAlreadyInUseException {
ConnectionSettings cs = new ConnectionSettings(hostname);
cs.port = port;
this.connect(cs);
}
/**
* Attempt to connect to the specified IRC server using the supplied
* password.
* The onConnect method is called upon success.
*
* @param hostname The hostname of the server to connect to.
* @param port The port number to connect to on the server.
* @param password The password to use to join the server.
*
* @throws IOException if it was not possible to connect to the server.
* @throws IrcException if the server would not let us join it.
* @throws NickAlreadyInUseException if our nick is already in use on the server.
*/
public final synchronized void connect(String hostname, int port, String password) throws IOException, IrcException, NickAlreadyInUseException {
ConnectionSettings cs = new ConnectionSettings(hostname);
cs.port = port;
cs.password = password;
this.connect(cs);
}
/**
* Attempt to connect to an IRC server using the supplied
* connection settings.
* The onConnect method is called upon success.
*
* @param cs The connection settings to use.
*
* @throws IOException if it was not possible to connect to the server.
* @throws IrcException if the server would not let us join it.
* @throws NickAlreadyInUseException if our nick is already in use on the server.
*/
public final synchronized void connect(ConnectionSettings cs) throws IOException, IrcException, NickAlreadyInUseException {
ConnectionSettings _cs = cs.clone();
_connectionSettings = _cs;
if (isConnected()) {
throw new IOException("The PircBot is already connected to an IRC server. Disconnect first.");
}
// Don't clear the outqueue - there might be something important in it!
// Clear everything we may have know about channels.
this.removeAllChannels();
// Connect to the server.
Socket socket;
if (_cs.useSSL) {
try {
SocketFactory factory;
if (_cs.verifySSL) {
factory = SSLSocketFactory.getDefault();
} else {
SSLContext sc = UnverifiedSSL.getUnverifiedSSLContext();
factory = sc.getSocketFactory();
}
socket = factory.createSocket(_cs.server, _cs.port);
} catch (Exception e) {
throw new IOException("SSL failure");
}
} else {
socket = new Socket(_cs.server, _cs.port);
}
this.log("*** Connected to server.");
_inetAddress = socket.getLocalAddress();
InputStreamReader inputStreamReader = null;
OutputStreamWriter outputStreamWriter = null;
if (getEncoding() != null) {
// Assume the specified encoding is valid for this JVM.
inputStreamReader = new InputStreamReader(socket.getInputStream(), getEncoding());
outputStreamWriter = new OutputStreamWriter(socket.getOutputStream(), getEncoding());
}
else {
// Otherwise, just use the JVM's default encoding.
inputStreamReader = new InputStreamReader(socket.getInputStream());
outputStreamWriter = new OutputStreamWriter(socket.getOutputStream());
}
BufferedReader breader = new BufferedReader(inputStreamReader);
BufferedWriter bwriter = new BufferedWriter(outputStreamWriter);
// Attempt to join the server.
if (_cs.password != null && !_cs.password.equals("")) {
OutputThread.sendRawLine(this, bwriter, "PASS " + _cs.password);
}
String nick = this.getName();
OutputThread.sendRawLine(this, bwriter, "NICK " + nick);
// http://tools.ietf.org/html/rfc2812#section-3.1.3
OutputThread.sendRawLine(this, bwriter, "USER " + this.getUserName() + " 8 * :" + this.getRealName());
_inputThread = new InputThread(this, socket, breader, bwriter);
// Read stuff back from the server to see if we connected.
String line = null;
int tries = 1;
while ((line = breader.readLine()) != null) {
this.handleLine(line);
int firstSpace = line.indexOf(" ");
int secondSpace = line.indexOf(" ", firstSpace + 1);
if (secondSpace >= 0) {
String code = line.substring(firstSpace + 1, secondSpace);
if (code.equals("004")) {
// We're connected to the server.
break;
}
else if (code.equals("433")) {
if (_autoNickChange) {
tries++;
nick = getName() + tries;
OutputThread.sendRawLine(this, bwriter, "NICK " + nick);
}
else {
socket.close();
_inputThread = null;
throw new NickAlreadyInUseException(line);
}
}
else if (code.equals("439")) {
// No action required.
}
else if (code.startsWith("5") || code.startsWith("4")) {
socket.close();
_inputThread = null;
throw new IrcException("Could not log into the IRC server: " + line);
}
}
this.setNick(nick);
}
this.log("*** Logged onto server.");
// This makes the socket timeout on read operations after 5 minutes.
// Maybe in some future version I will let the user change this at runtime.
socket.setSoTimeout(5 * 60 * 1000);
// Now start the InputThread to read all other lines from the server.
_inputThread.start();
// Now start the outputThread that will be used to send all messages.
if (_outputThread == null) {
_outputThread = new OutputThread(this, _outQueue);
_outputThread.start();
}
this.onConnect();
}
/**
* Reconnects to the IRC server that we were previously connected to.
* If necessary, the appropriate port number and password will be used.
* This method will throw an IrcException if we have never connected
* to an IRC server previously.
*
* @since PircBot 0.9.9
*
* @throws IOException if it was not possible to connect to the server.
* @throws IrcException if the server would not let us join it.
* @throws NickAlreadyInUseException if our nick is already in use on the server.
*/
public final synchronized void reconnect() throws IOException, IrcException, NickAlreadyInUseException{
if (getServer() == null) {
throw new IrcException("Cannot reconnect to an IRC server because we were never connected to one previously!");
}
connect(_connectionSettings);
}
/**
* This method disconnects from the server cleanly by calling the
* quitServer() method. Providing the PircBot was connected to an
* IRC server, the onDisconnect() will be called as soon as the
* disconnection is made by the server.
*
* @see #quitServer() quitServer
* @see #quitServer(String) quitServer
*/
public final synchronized void disconnect() {
this.quitServer();
}
/**
* When you connect to a server and your nick is already in use and
* this is set to true, a new nick will be automatically chosen.
* This is done by adding numbers to the end of the nick until an
* available nick is found.
*
* @param autoNickChange Set to true if you want automatic nick changes
* during connection.
*/
public void setAutoNickChange(boolean autoNickChange) {
_autoNickChange = autoNickChange;
}
/**
* Starts an ident server (Identification Protocol Server, RFC 1413).
* <p>
* Most IRC servers attempt to contact the ident server on connecting
* hosts in order to determine the user's identity. A few IRC servers
* will not allow you to connect unless this information is provided.
* <p>
* So when a PircBot is run on a machine that does not run an ident server,
* it may be necessary to call this method to start one up.
* <p>
* Calling this method starts up an ident server which will respond with
* the login provided by calling getLogin() and then shut down immediately.
* It will also be shut down if it has not been contacted within 60 seconds
* of creation.
* <p>
* If you require an ident response, then the correct procedure is to start
* the ident server and then connect to the IRC server. The IRC server may
* then contact the ident server to get the information it needs.
* <p>
* The ident server will fail to start if there is already an ident server
* running on port 113, or if you are running as an unprivileged user who
* is unable to create a server socket on that port number.
* <p>
* If it is essential for you to use an ident server when connecting to an
* IRC server, then make sure that port 113 on your machine is visible to
* the IRC server so that it may contact the ident server.
*
* @since PircBot 0.9c
*/
public final void startIdentServer() {
new IdentServer(this, getLogin());
}
/**
* Joins a channel.
*
* @param channel The name of the channel to join (eg "#cs").
*/
public final void joinChannel(String channel) {
this.sendRawLine("JOIN " + channel);
}
/**
* Joins a channel with a key.
*
* @param channel The name of the channel to join (eg "#cs").
* @param key The key that will be used to join the channel.
*/
public final void joinChannel(String channel, String key) {
this.joinChannel(channel + " " + key);
}
/**
* Parts a channel.
*
* @param channel The name of the channel to leave.
*/
public final void partChannel(String channel) {
this.sendRawLine("PART " + channel);
}
/**
* Parts a channel, giving a reason.
*
* @param channel The name of the channel to leave.
* @param reason The reason for parting the channel.
*/
public final void partChannel(String channel, String reason) {
this.sendRawLine("PART " + channel + " :" + reason);
}
/**
* Quits from the IRC server.
* Providing we are actually connected to an IRC server, the
* onDisconnect() method will be called as soon as the IRC server
* disconnects us.
*/
public final void quitServer() {
this.quitServer("");
}
/**
* Quits from the IRC server with a reason.
* Providing we are actually connected to an IRC server, the
* onDisconnect() method will be called as soon as the IRC server
* disconnects us.
*
* @param reason The reason for quitting the server.
*/
public final void quitServer(String reason) {
this.sendRawLine("QUIT :" + reason);
}
/**
* Sends a raw line to the IRC server as soon as possible, bypassing the
* outgoing message queue.
*
* @param line The raw line to send to the IRC server.
*/
public final synchronized void sendRawLine(String line) {
if (isConnected()) {
_inputThread.sendRawLine(line);
}
}
/**
* Sends a raw line through the outgoing message queue.
*
* @param line The raw line to send to the IRC server.
*/
public final synchronized void sendRawLineViaQueue(String line) {
if (line == null) {
throw new NullPointerException("Cannot send null messages to server");
}
if (isConnected()) {
_outQueue.add(line);
}
}
/**
* Sends a message to a channel or a private message to a user. These
* messages are added to the outgoing message queue and sent at the
* earliest possible opportunity.
* <p>
* Some examples: -
* <pre> // Send the message "Hello!" to the channel #cs.
* sendMessage("#cs", "Hello!");
*
* // Send a private message to Paul that says "Hi".
* sendMessage("Paul", "Hi");</pre>
*
* You may optionally apply colours, boldness, underlining, etc to
* the message by using the <code>Colors</code> class.
*
* @param target The name of the channel or user nick to send to.
* @param message The message to send.
*
* @see Colors
*/
public final void sendMessage(String target, String message) {
_outQueue.add("PRIVMSG " + target + " :" + message);
}
/**
* Sends an action to the channel or to a user.
*
* @param target The name of the channel or user nick to send to.
* @param action The action to send.
*
* @see Colors
*/
public final void sendAction(String target, String action) {
sendCTCPCommand(target, "ACTION " + action);
}
/**
* Sends a notice to the channel or to a user.
*
* @param target The name of the channel or user nick to send to.
* @param notice The notice to send.
*/
public final void sendNotice(String target, String notice) {
_outQueue.add("NOTICE " + target + " :" + notice);
}
/**
* Sends a CTCP command to a channel or user. (Client to client protocol).
* Examples of such commands are "PING <number>", "FINGER", "VERSION", etc.
* For example, if you wish to request the version of a user called "Dave",
* then you would call <code>sendCTCPCommand("Dave", "VERSION");</code>.
* The type of response to such commands is largely dependant on the target
* client software.
*
* @since PircBot 0.9.5
*
* @param target The name of the channel or user to send the CTCP message to.
* @param command The CTCP command to send.
*/
public final void sendCTCPCommand(String target, String command) {
_outQueue.add("PRIVMSG " + target + " :\u0001" + command + "\u0001");
}
/**
* Attempt to change the current nick (nickname) of the bot when it
* is connected to an IRC server.
* After confirmation of a successful nick change, the getNick method
* will return the new nick.
*
* @param newNick The new nick to use.
*/
public final void changeNick(String newNick) {
this.sendRawLine("NICK " + newNick);
}
/**
* Identify the bot with NickServ, supplying the appropriate password.
* Some IRC Networks (such as freenode) require users to <i>register</i> and
* <i>identify</i> with NickServ before they are able to send private messages
* to other users, thus reducing the amount of spam. If you are using
* an IRC network where this kind of policy is enforced, you will need
* to make your bot <i>identify</i> itself to NickServ before you can send
* private messages. Assuming you have already registered your bot's
* nick with NickServ, this method can be used to <i>identify</i> with
* the supplied password. It usually makes sense to identify with NickServ
* immediately after connecting to a server.
* <p>
* This method issues a raw NICKSERV command to the server, and is therefore
* safer than the alternative approach of sending a private message to
* NickServ. The latter approach is considered dangerous, as it may cause
* you to inadvertently transmit your password to an untrusted party if you
* connect to a network which does not run a NickServ service and where the
* untrusted party has assumed the nick "NickServ". However, if your IRC
* network is only compatible with the private message approach, you may
* typically identify like so:
* <pre>sendMessage("NickServ", "identify PASSWORD");</pre>
*
* @param password The password which will be used to identify with NickServ.
*/
public final void identify(String password) {
this.sendRawLine("NICKSERV IDENTIFY " + password);
}
/**
* Set the mode of a channel.
* This method attempts to set the mode of a channel. This
* may require the bot to have operator status on the channel.
* For example, if the bot has operator status, we can grant
* operator status to "Dave" on the #cs channel
* by calling setMode("#cs", "+o Dave");
* An alternative way of doing this would be to use the op method.
*
* @param channel The channel on which to perform the mode change.
* @param mode The new mode to apply to the channel. This may include
* zero or more arguments if necessary.
*
* @see #op(String,String) op
*/
public final void setMode(String channel, String mode) {
this.sendRawLine("MODE " + channel + " " + mode);
}
/**
* Sends an invitation to join a channel. Some channels can be marked
* as "invite-only", so it may be useful to allow a bot to invite people
* into it.
*
* @param nick The nick of the user to invite
* @param channel The channel you are inviting the user to join.
*
*/
public final void sendInvite(String nick, String channel) {
this.sendRawLine("INVITE " + nick + " :" + channel);
}
/**
* Bans a user from a channel. An example of a valid hostmask is
* "*!*compu@*.18hp.net". This may be used in conjunction with the
* kick method to permanently remove a user from a channel.
* Successful use of this method may require the bot to have operator
* status itself.
*
* @param channel The channel to ban the user from.
* @param hostmask A hostmask representing the user we're banning.
*/
public final void ban(String channel, String hostmask) {
this.sendRawLine("MODE " + channel + " +b " + hostmask);
}
/**
* Unbans a user from a channel. An example of a valid hostmask is
* "*!*compu@*.18hp.net".
* Successful use of this method may require the bot to have operator
* status itself.
*
* @param channel The channel to unban the user from.
* @param hostmask A hostmask representing the user we're unbanning.
*/
public final void unBan(String channel, String hostmask) {
this.sendRawLine("MODE " + channel + " -b " + hostmask);
}
/**
* Grants operator privilidges to a user on a channel.
* Successful use of this method may require the bot to have operator
* status itself.
*
* @param channel The channel we're opping the user on.
* @param nick The nick of the user we are opping.
*/
public final void op(String channel, String nick) {
this.setMode(channel, "+o " + nick);
}
/**
* Removes operator privilidges from a user on a channel.
* Successful use of this method may require the bot to have operator
* status itself.
*
* @param channel The channel we're deopping the user on.
* @param nick The nick of the user we are deopping.
*/
public final void deOp(String channel, String nick) {
this.setMode(channel, "-o " + nick);
}
/**
* Grants voice privilidges to a user on a channel.
* Successful use of this method may require the bot to have operator
* status itself.
*
* @param channel The channel we're voicing the user on.
* @param nick The nick of the user we are voicing.
*/
public final void voice(String channel, String nick) {
this.setMode(channel, "+v " + nick);
}
/**
* Removes voice privilidges from a user on a channel.
* Successful use of this method may require the bot to have operator
* status itself.
*
* @param channel The channel we're devoicing the user on.
* @param nick The nick of the user we are devoicing.
*/
public final void deVoice(String channel, String nick) {
this.setMode(channel, "-v " + nick);
}
/**
* Set the topic for a channel.
* This method attempts to set the topic of a channel. This
* may require the bot to have operator status if the topic
* is protected.
*
* @param channel The channel on which to perform the mode change.
* @param topic The new topic for the channel.
*
*/
public final void setTopic(String channel, String topic) {
this.sendRawLine("TOPIC " + channel + " :" + topic);
}
/**
* Kicks a user from a channel.
* This method attempts to kick a user from a channel and
* may require the bot to have operator status in the channel.
*
* @param channel The channel to kick the user from.
* @param nick The nick of the user to kick.
*/
public final void kick(String channel, String nick) {
this.kick(channel, nick, "");
}
/**
* Kicks a user from a channel, giving a reason.
* This method attempts to kick a user from a channel and
* may require the bot to have operator status in the channel.
*
* @param channel The channel to kick the user from.
* @param nick The nick of the user to kick.
* @param reason A description of the reason for kicking a user.
*/
public final void kick(String channel, String nick, String reason) {
this.sendRawLine("KICK " + channel + " " + nick + " :" + reason);
}
/**
* Issues a request for a list of all channels on the IRC server.
* When the PircBot receives information for each channel, it will
* call the onChannelInfo method, which you will need to override
* if you want it to do anything useful.
*
* @see #onChannelInfo(String,int,String) onChannelInfo
*/
public final void listChannels() {
this.listChannels(null);
}
/**
* Issues a request for a list of all channels on the IRC server.
* When the PircBot receives information for each channel, it will
* call the onChannelInfo method, which you will need to override
* if you want it to do anything useful.
* <p>
* Some IRC servers support certain parameters for LIST requests.
* One example is a parameter of ">10" to list only those channels
* that have more than 10 users in them. Whether these parameters
* are supported or not will depend on the IRC server software.
*
* @param parameters The parameters to supply when requesting the
* list.
*
* @see #onChannelInfo(String,int,String) onChannelInfo
*/
public final void listChannels(String parameters) {
if (parameters == null) {
this.sendRawLine("LIST");
}
else {
this.sendRawLine("LIST " + parameters);
}
}
/**
* Sends a file to another user. Resuming is supported.
* The other user must be able to connect directly to your bot to be
* able to receive the file.
* <p>
* You may throttle the speed of this file transfer by calling the
* setPacketDelay method on the DccFileTransfer that is returned.
* <p>
* This method may not be overridden.
*
* @since 0.9c
*
* @param file The file to send.
* @param nick The user to whom the file is to be sent.
* @param timeout The number of milliseconds to wait for the recipient to
* acccept the file (we recommend about 120000).
*
* @return The DccFileTransfer that can be used to monitor this transfer.
*
* @see DccFileTransfer
*
*/
public final DccFileTransfer dccSendFile(File file, String nick, int timeout) {
DccFileTransfer transfer = new DccFileTransfer(this, _dccManager, file, nick, timeout);
transfer.doSend(true);
return transfer;
}
/**
* Receives a file that is being sent to us by a DCC SEND request.
* Please use the onIncomingFileTransfer method to receive files.
*
* @deprecated As of PircBot 1.2.0, use {@link #onIncomingFileTransfer(DccFileTransfer)}
*/
protected final void dccReceiveFile(File file, long address, int port, int size) {
throw new RuntimeException("dccReceiveFile is deprecated, please use sendFile");
}
/**
* Attempts to establish a DCC CHAT session with a client. This method
* issues the connection request to the client and then waits for the
* client to respond. If the connection is successfully made, then a
* DccChat object is returned by this method. If the connection is not
* made within the time limit specified by the timeout value, then null
* is returned.
* <p>
* It is <b>strongly recommended</b> that you call this method within a new
* Thread, as it may take a long time to return.
* <p>
* This method may not be overridden.
*
* @since PircBot 0.9.8
*
* @param nick The nick of the user we are trying to establish a chat with.
* @param timeout The number of milliseconds to wait for the recipient to
* accept the chat connection (we recommend about 120000).
*
* @return a DccChat object that can be used to send and recieve lines of
* text. Returns <b>null</b> if the connection could not be made.
*
* @see DccChat
*/
public final DccChat dccSendChatRequest(String nick, int timeout) {
DccChat chat = null;
try {
ServerSocket ss = null;
int[] ports = getDccPorts();
if (ports == null) {
// Use any free port.
ss = new ServerSocket(0);
}
else {
for (int i = 0; i < ports.length; i++) {
try {
ss = new ServerSocket(ports[i]);
// Found a port number we could use.
break;
}
catch (Exception e) {
// Do nothing; go round and try another port.
}
}
if (ss == null) {
// No ports could be used.
throw new IOException("All ports returned by getDccPorts() are in use.");
}
}
ss.setSoTimeout(timeout);
int port = ss.getLocalPort();
InetAddress inetAddress = getDccInetAddress();
if (inetAddress == null) {
inetAddress = getInetAddress();
}
byte[] ip = inetAddress.getAddress();
long ipNum = ipToLong(ip);
sendCTCPCommand(nick, "DCC CHAT chat " + ipNum + " " + port);
// The client may now connect to us to chat.
Socket socket = ss.accept();
// Close the server socket now that we've finished with it.
ss.close();
chat = new DccChat(this, nick, socket);
}
catch (Exception e) {
// Do nothing.
}
return chat;
}
/**
* Attempts to accept a DCC CHAT request by a client.
* Please use the onIncomingChatRequest method to receive files.
*
* @deprecated As of PircBot 1.2.0, use {@link #onIncomingChatRequest(DccChat)}
*/
protected final DccChat dccAcceptChatRequest(String sourceNick, long address, int port) {
throw new RuntimeException("dccAcceptChatRequest is deprecated, please use onIncomingChatRequest");
}
/**
* Adds a line to the log. This log is currently output to the standard
* output and is in the correct format for use by tools such as pisg, the
* Perl IRC Statistics Generator. You may override this method if you wish
* to do something else with log entries.
* Each line in the log begins with a number which
* represents the logging time (as the number of milliseconds since the
* epoch). This timestamp and the following log entry are separated by
* a single space character, " ". Outgoing messages are distinguishable
* by a log entry that has ">>>" immediately following the space character
* after the timestamp. DCC events use "+++" and warnings about unhandled
* Exceptions and Errors use "###".
* <p>
* This implementation of the method will only cause log entries to be
* output if the PircBot has had its verbose mode turned on by calling
* setVerbose(true);
*
* @param line The line to add to the log.
*/
public void log(String line) {
if (_verbose) {
System.out.println(System.currentTimeMillis() + " " + line);
}
}
/**
* This method handles events when any line of text arrives from the server,
* then calling the appropriate method in the PircBot. This method is
* protected and only called by the InputThread for this instance.
* <p>
* This method may not be overridden!
*
* @param line The raw line of text from the server.
*/
protected void handleLine(String line) {
this.log(line);
// Check for server pings.
if (line.startsWith("PING ")) {
// Respond to the ping and return immediately.
this.onServerPing(line.substring(5));
return;
}
String sourceNick = "";
String sourceLogin = "";
String sourceHostname = "";
StringTokenizer tokenizer = new StringTokenizer(line);
String senderInfo = tokenizer.nextToken();
String command = tokenizer.nextToken();
String target = null;
int exclamation = senderInfo.indexOf("!");
int at = senderInfo.indexOf("@");
if (senderInfo.startsWith(":")) {
if (exclamation > 0 && at > 0 && exclamation < at) {
sourceNick = senderInfo.substring(1, exclamation);
sourceLogin = senderInfo.substring(exclamation + 1, at);
sourceHostname = senderInfo.substring(at + 1);
}
else {
if (tokenizer.hasMoreTokens()) {
String token = command;
int code = -1;
try {
code = Integer.parseInt(token);
}
catch (NumberFormatException e) {
// Keep the existing value.
}
if (code != -1) {
String errorStr = token;
String response = line.substring(line.indexOf(errorStr, senderInfo.length()) + 4, line.length());
this.processServerResponse(code, response);
// Return from the method.
return;
}
else {
// This is not a server response.
// It must be a nick without login and hostname.
// (or maybe a NOTICE or suchlike from the server)
sourceNick = senderInfo;
target = token;
}
}
else {
// We don't know what this line means.
this.onUnknown(line);
// Return from the method;
return;
}
}
}
command = command.toUpperCase();
if (sourceNick.startsWith(":")) {
sourceNick = sourceNick.substring(1);
}
if (target == null) {
target = tokenizer.nextToken();
}
if (target.startsWith(":")) {
target = target.substring(1);
}
// Check for CTCP requests.
if (command.equals("PRIVMSG") && line.indexOf(":\u0001") > 0 && line.endsWith("\u0001")) {
String request = line.substring(line.indexOf(":\u0001") + 2, line.length() - 1);
if (request.equals("VERSION")) {
// VERSION request
this.onVersion(sourceNick, sourceLogin, sourceHostname, target);
}