-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathirc.cs
More file actions
676 lines (642 loc) · 27.1 KB
/
Copy pathirc.cs
File metadata and controls
676 lines (642 loc) · 27.1 KB
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
using System;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Threading;
using System.Collections.Generic;
// Irc class handles all TCP communications with the IRC server
namespace NaN0IRC
{
public delegate void StuffHappened(string stuff);
public delegate void TopicChanged(string stuff);
public delegate void Restart();
public delegate void ListNames(string thisChannel, string[] channelUsers);
public delegate void NewChannel(string channelName);
class Irc
{
public event StuffHappened stuffHappened;
public event TopicChanged topicChanged;
public event Restart restart;
public event ListNames channelUsers;
public event NewChannel joinNewChannel;
private string server;
private int port;
private string channel;
private string nick; // The NaN0 user's name
private string name;
private TcpClient connection;
private NetworkStream stream;
private StreamWriter writer;
private StreamReader reader;
private bool quit = false;
private List<string> boss;
private List<string> channelNames;
private List<Channel> channels;
private bool timestamp = true;
private bool hidePing = true;
private RPSLS rpslsGame;
private bool log = true;
private bool getResponse = false;
private DateTime lastPing;
private string currentChannel;
public void connect()
{
connection = null;
reader = null;
stream = null;
reader = null;
writer = null;
cWrite(String.Format("{0} is connecting to {1} on {2}", nick, channel, server));
try
{
connection = new TcpClient(server, port);
}
catch (SocketException) { restart(); }
try
{
stream = connection.GetStream();
reader = new StreamReader(stream);
writer = new StreamWriter(stream);
authenticate();
if (channel.Contains(";")) {
String[] allChannels = channel.Split(';');
foreach(String s in allChannels) {
join(s.Trim());
}
} else if (channel.Contains(",")) {
String[] allChannels = channel.Split(',');
foreach (String s in allChannels)
{
join(s.Trim());
}
} else {
join(channel);
}
while (!quit)
{
/* Following code is nifty, but listening is based on events
cWrite("!LISTENING");
DateTime now = DateTime.Now;
if (now.CompareTo(lastPing.AddMinutes(1)) > 0)
ping();
if (now.CompareTo(lastPing.AddSeconds(2)) > 0)
{
cWrite("PING Timeout. Connection to server lost.");
//restart();
}*/
listen();
}
writer.Close();
reader.Close();
connection.Close();
}
catch (NullReferenceException) { cWrite("NaN0IRC could not initialise connection."); restart(); }
}
private void listen()
{
string input;
// ObjectDisposedException
#region Disconnect handling
try
{
input = reader.ReadLine();
}
catch (ObjectDisposedException)
{
cWrite("NaN0IRC disconnected. Probably due to stream closing.");
quit = true;
restart();
return;
}
catch (IOException)
{
cWrite("NaN0IRC disconnected. Could not read stream, check internet connection.");
quit = true;
restart();
return;
}
#endregion
if (input != null)
{
lastPing = DateTime.Now;
cWrite("!"+input); // Checking stuff during testing
input = parseInput(input);
if (input.Substring(0, 1) == ":")
input = input.Substring(1);
string[] parts = new string[input.Split(' ').Length];
parts = input.Split(' ');
if (!serverMessage(parts, input))
{
if (parts[0] == "PING") // Check if it's a server message, and act accordingly
// Server PING, send PONG back
this.pong(parts);
else
{
switch (parts[1])
{
case "JOIN":
joined(parts); break;
case "PART":
left(parts); break;
case "MODE":
changeMode(parts);
break;
case "NICK":
changeNick(parts);
break;
case "KICK":
kicked(parts);
break;
case "QUIT":
userQuit(parts);
break;
case "PRIVMSG":
String sender = parts[0].Substring(0, input.IndexOf("!"));
String recipient = parts[2];
String message = input.Substring(input.IndexOf(":") + 1);
if (channelNames.Contains(recipient.ToLower()))
{ // Message is in a channel
channelMsg(sender, recipient, message);
}
else if (recipient.ToLower() == nick.ToLower())
{ // Message is private to user
privateMsg(sender, message);
}
break;
case "NOTICE": // Testing
sender = parts[0].Substring(0, input.IndexOf("!"));
recipient = parts[2];
message = input.Substring(input.IndexOf(":") + 1);
if (channelNames.Contains(recipient.ToLower()))
{ // Message is in a channel
channelMsg(sender, recipient, message);
}
else if (recipient.ToLower() == nick.ToLower())
{ // Message is private to user
privateMsg(sender, message);
}
break;
case "TOPIC":
changeTopic(parts);
break;
default:
cWrite("NaN0IRC did not understand server input: " + input);
break;
}
}
}
}
}
// Write to IRC stream
public void write(string output)
{
if (output.Substring(0, 1) == "!") // Command should be copied to console without timestamp
{
if (output.Length > 8)
{
if (output.Substring(1, 7).ToUpper() == "PRIVMSG")
{
string message = output.Substring(output.IndexOf(' ') + 1);
string recipient = message.Substring(0, message.IndexOf(' '));
message = message.Substring(message.IndexOf(' ') + 2);
if (message.Substring(0,1) == "\u0001") // PRIVMSG CHANNEL :\u0001ACTION {message goes here}\u0001
cWrite(String.Format("{0}: *{1} {2}*", recipient, nick, message.Substring(8, message.Length - 9)));
else
cWrite(String.Format("{0}: <{1}> {2}", recipient, nick, message));
}
else
{
cWrite(output);
}
}
else
{
cWrite(output);
}
output = output.Substring(1);
}
try
{
writer.WriteLine(output);
writer.Flush();
}
catch (IOException) { restart(); }
catch (ObjectDisposedException) { restart(); }
catch (NullReferenceException) { restart(); }
}
// Write to console or GUI
public void cWrite(string output)
{
DateTime now = DateTime.Now;
if (output.Substring(0, 1) == "!") // Input command written to console, don't timestamp
{
this.stuffHappened(output.Substring(1));
channels[0].changeContents(output);
}
else
{
this.logging(output); // Write to Logs
string cName = output.Substring(0, output.IndexOf(' '));
if (cName.Substring(cName.Length - 1) == ":")
cName = cName.Substring(0, cName.Length - 1);
bool existingChannel = false;
foreach (Channel thing in channels)
if (thing.Name == cName)
{
existingChannel = true;
if (timestamp)
{
thing.changeContents(String.Format("{0:yyyy/MM/dd HH:mm} {1}", now, output));
this.stuffHappened(String.Format("{0:yyyy/MM/dd HH:mm} {1}", now, output));
}
else
{
thing.changeContents(output);
this.stuffHappened(output);
}
}
if (!existingChannel) // Sender not on known channel list, probably a private user
{
channelNames.Add(cName);
channels.Add(new Channel(cName));
channels[channels.Count - 1].addUser(cName);
channels[channels.Count - 1].addUser(nick);
this.joinNewChannel(cName);
currentChannel = cName;
if (timestamp)
{
channels[channels.Count-1].changeContents(String.Format("{0:yyyy/MM/dd HH:mm} {1}", now, output));
this.stuffHappened(String.Format("{0:yyyy/MM/dd HH:mm} {1}", now, output));
}
else
{
channels[channels.Count - 1].changeContents(output);
this.stuffHappened(output);
}
}
}
Thread.Sleep(100);
}
private void logging(string logText)
{
if (log)
{
string logID = logText.Substring(0, logText.IndexOf(' '));
if (logID.Substring(logID.Length - 1) == ":")
logID = logID.Substring(0, logID.Length - 1);
DateTime now = DateTime.Now;
// Create a folder for the logs
string path = "Logs";
System.IO.Directory.CreateDirectory(path); // Does nothing if Directory exists
// Create a folder for the user
path = @"Logs/" + nick;
System.IO.Directory.CreateDirectory(path);
// Create a folder for the particular logID
path += @"/" + logID;
System.IO.Directory.CreateDirectory(path);
// Create the text file, using the Year Month timestamp
StreamWriter stream = new StreamWriter(String.Format("{0}\\{1:yyyy MMM}.txt", path, now), true);
stream.WriteLine(String.Format("{0:yyyy-dd-MM HH:mm} {1}", now, logText));
stream.Close();
}
}
// PING server regularly
public void ping()
{
Random random = new Random();
write("PING "+Convert.ToString(random.Next(100000,1000000)));
}
#region Standard IRC events
// User joined channel
private void joined(string[] parts) //OK
{
if (parts[2].Substring(0, 1) == ":")
parts[2] = parts[2].Substring(1);
string channelJoined = parts[2];
string user = parts[0].Split('!')[0];
if (parts[0].Split('!')[0] == nick) // A simple way to work out if the user successfully joined a new channel
{
channelNames.Add(channelJoined);
channels.Add(new Channel(channelJoined));
this.joinNewChannel(channelJoined);
currentChannel = channelJoined;
}
else
{
foreach (Channel thing in channels)
{
if (thing.Name == channelJoined)
thing.addUser(user);
this.channelUsers(thing.Name, thing.getUsers()); // Event that changes the GUI TextBox
}
}
cWrite(String.Format("{0}: {1} joined {0}", channelJoined, user));
}
// User left channel
private void left(string[] parts)
{
string user = parts[2];
cWrite(String.Format("{0}: {1} left {0}", user, parts[0].Split('!')[0]));
foreach (Channel thing in channels)
if (thing.containsUser(user))
{
thing.removeUser(user);
this.channelUsers(thing.Name, thing.getUsers()); // Event that changes the GUI TextBox
}
}
// User changed mode
private void changeMode(string[] parts)
{
// Channel = parts[2]
// User = parts[0].Split('!')[0]
string mode = "";
for (int i = 3; i < parts.Length; i++)
{
mode += parts[i] + " ";
}
if (mode.Substring(0, 1) == ":")
mode = mode.Substring(1);
if (channelNames.Contains(parts[2]))
{
cWrite(String.Format("{0}: {1} sets {2}", parts[2],
parts[0].Split('!')[0], mode));
}
} // OK
// User changed nick
private void changeNick(string[] parts)
{
string oldNick = parts[0].Split('!')[0];
string newNick = parts[2].Trim();
if (oldNick == nick)
nick = newNick;
if (boss.Contains(oldNick))
{
boss.Remove(oldNick);
boss.Add(newNick);
}
// cWrite(String.Format("{0} is now known as {1}", oldNick, newNick));
foreach (Channel thing in channels)
if (thing.containsUser(oldNick))
{
thing.changeUser(oldNick, newNick);
cWrite(String.Format("{0}: {1} is now known as {2}", thing.Name, oldNick, newNick));
}
}
private void kicked(string[] parts)
{
string kicker = parts[0].Split('!')[0];
string kicked = parts[3];
string where = parts[2];
string kickMessage = "";
for (int i = 4; i < parts.Length; i++)
{
kickMessage += parts[i] + " ";
}
cWrite(String.Format("{0}: {1} kicks {2} ({3})", where, kicker, kicked, kickMessage.Substring(1).Trim()));
foreach (Channel thing in channels)
if (thing.containsUser(kicked))
thing.removeUser(kicked);
List<string> currentChannels = new List<string>(channelNames);
if (kicked.ToLower() == nick.ToLower())
{
join(where);
}
}
private void userQuit(string[] parts)
{
string quitter = parts[0].Split('!')[0];
string quitMessage = "";
for (int i = 2; i < parts.Length; i++)
{
quitMessage += parts[i] + " ";
}
foreach (Channel thing in channels)
if (thing.containsUser(quitter))
{
thing.removeUser(quitter);
cWrite(String.Format("{0}: {1} has quit ({2})", thing.Name, quitter,
quitMessage.Substring(1).Trim()));
this.channelUsers(thing.Name, thing.getUsers()); // Event that changes the GUI TextBox. Not the best idea to put it here though.
}
}
#endregion Standard IRC events
private void channelMsg(string sender, string recipient, string message)
{ // Someone said something in the Channel
if (message.Contains("\u0001"))
{
message = message.Substring(message.IndexOf(' ') + 1);
cWrite(String.Format("{0}: *{1} {2}*", recipient, sender, message.Substring(0, message.Length - 1)));
}
else if (message.ToLower().Contains(nick.ToLower() + ":"))
cWrite(String.Format("{0}: <{1}> {2}", recipient, sender, message));
else
cWrite(String.Format("{0}: <{1}> {2}", recipient, sender, message));
}
public string[] changeChannel(string channelName)
{
string[] errorString = new string[1];
errorString[0] = String.Format("{0} is not an applicable channel", channelName);
currentChannel = channelName;
foreach (Channel thing in channels)
if (thing.Name == channelName)
{
this.channelUsers(thing.Name, thing.getUsers());
return thing.Contents;
}
return errorString;
}
public string getTopic(string channelName)
{
foreach (Channel thing in channels)
{
if (thing.Name == channelName)
return thing.Topic;
}
return "";
}
public String[] getCurrentUsers()
{
// This is a terrible implementation. Ideally, channel would be called current channel and would be a channel object, with the correct reference
foreach (Channel thing in channels)
if (thing.Name == channel)
{
return thing.getUsers();
}
String[] empty = {};
return empty;
}
public void closeIRC()
{
try
{
write("!QUIT :I was using NaN0 IRC.");
}
catch (ObjectDisposedException) { }
quit = true;
}
// Private message to user
private void privateMsg(string sender, string message)
{
cWrite(String.Format("{0}: <{0}> {1}",sender, message));
}
private bool serverMessage(string[] parts, string input)
{
if (parts[0].IndexOf(".") != -1)
{
if (parts[0].IndexOf(this.server.Substring(this.server.IndexOf(".") + 1)) != -1)
{
string sender;
if (parts[0].IndexOf('!') != -1)
sender = parts[0].Substring(0, input.IndexOf("!"));
else
sender = parts[0];
string recipient = parts[2];
string message = input.Substring(input.IndexOf(":") + 1);
// Server message
switch (parts[1])
{
case "332": topic(parts); break;
case "333": topicOwner(parts); break;
case "353": listNames(parts, input); break;
case "366": /*This is the end of the names list*/ break;
case "372": /*This is the start of the MOTD*/ break;
case "376": /* End of MOTD*/ join(channel); break;
case "433": /* Nickname is already in use restart();*/ break;
case "PONG": /*Response to us PINGing the server, but all we care about is a response of any kind*/break;
case "NOTICE":
cWrite(String.Format("{0}: {1}", sender, message));
break;
case "MODE": //:ChanServ!judd@maxgaming.net MODE #plt2 -o DarkSentinel2
message = message.Substring(message.IndexOf(' ') + 1); //MODE #plt2 -o DarkSentinel2
message = message.Substring(message.IndexOf(' ') + 1); //#plt2 -o DarkSentinel2
cWrite(String.Format("{0}: {1} sets MODE {2}", message.Substring(0,message.IndexOf(' ')),
sender, message.Substring(message.IndexOf(' ') + 1)));
break;
/*:mgo2.maxgaming.net 366 DarkSentinel #plt1 :End of /NAMES list.
:mgo2.maxgaming.net 311 DarkSentinel Hannah ~obsidi 203-211-111-43.ue.woosh.co.nz * :Hannah
:mgo2.maxgaming.net 319 DarkSentinel Hannah :#plt1
:mgo2.maxgaming.net 312 DarkSentinel Hannah mgo2.maxgaming.net :MGOirc Server
:mgo2.maxgaming.net 317 DarkSentinel Hannah 897 1270866086 :seconds idle, signon time
:mgo2.maxgaming.net 318 DarkSentinel Hannah :End of /WHOIS list.*/
default:
break;
}
return true;
}
}
return false;
}
#region SimpleBotCommands
// Prints the topic (from server)
private void topic(string[] parts)
{
string topic = "";
string thisChannel = parts[3];
if (parts[4].Substring(0, 1) == ":")
parts[4] = parts[4].Substring(1);
for (int i = 4; i < parts.Length; i++)
topic += parts[i] + " ";
topic = topic.Trim();
cWrite(String.Format("{0}: Topic is \"{1}\"", thisChannel, topic));
this.topicChanged(String.Format("{0}: Topic is \"{1}\"", thisChannel, topic));
foreach (Channel thing in channels)
if (thing.Name == thisChannel)
thing.Topic = topic;
}
// User changes topic
private void changeTopic(string[] parts)
{ // Parts will be of the form Squirrel!~Squirrel@203-211-97-51.ue.woosh.co.nz TOPIC #plt2 :New topic. Yay Yay Yay
string topic = "";
string topicChanger = parts[0].Substring(0, parts[0].IndexOf('!'));
for (int i = 3; i < parts.Length; i++)
topic += parts[i] + " ";
if (topic.Substring(0, 1) == ":")
topic = topic.Substring(1);
string thisChannel = parts[2];
topic = topic.Trim();
cWrite(String.Format("{0}: {1} Changes topic to \"{2}\"", thisChannel, topicChanger, topic));
string time = DateTime.Now.ToString("HH:mm dd MMM yyyy (dddd)");
this.topicChanged(String.Format("{0}: Topic is \"{1}\" set by {2}, {3}", thisChannel, topic,
topicChanger, time));
foreach (Channel thing in channels)
if (thing.Name == thisChannel)
thing.Topic = topic;
}
// Prints the topic owner (from server)
private void topicOwner(string[] parts)
{
DateTime topicTime = new DateTime(1970, 1, 1, 0, 0, 0);
topicTime = topicTime.AddSeconds(double.Parse(parts[5]));
string time = topicTime.ToString("HH:mm dd MMM yyyy (dddd)");
cWrite(String.Format("{0}: Topic set by {1}, {2} (Server Time)", parts[3],
parts[4].Split('!')[0], time));
}
// Lists names of people in channel (from server)
private void listNames(string[] parts, string input)
{
string channelNames = input.Substring(input.IndexOf(":", 1) + 1);
string thisChannel = parts[4];
cWrite(String.Format("{0}: Users: {1}", thisChannel, channelNames));
string[] names = new string[channelNames.Split(' ').Length];
names = channelNames.Split(' ');
foreach (Channel thing in channels)
if (thing.Name == thisChannel)
{
foreach (string userName in names)
thing.addUser(userName);
this.channelUsers(thing.Name, thing.getUsers()); // Event that changes the GUI TextBox
}
}
// Responds to PING with a PONG
private void pong(string[] ping)
{
string pong = "";
for (int i = 1; i < ping.Length; i++)
{
pong += ping[i] + " ";
}
write("PONG " + pong);
if (!hidePing)
cWrite("PONG " + pong);
}
// Authenticate to server on connect
private void authenticate()
{
write(String.Format("USER {0} 0 * :{1}", nick, name));
write("NICK " + nick);
}
// Join a channel and adds it to the Channels list
private void join(String joinChannel)
{
write("JOIN " + joinChannel);
if (channelNames.Contains(joinChannel.ToLower()))
channelNames.Remove(joinChannel.ToLower());
channelNames.Add(joinChannel.ToLower());
}
#endregion SimpleBotCommands
// Messes with the server's input
private string parseInput(string input)
{
if (input.Substring(0, 1) == ":")
input = input.Substring(1);
if (getResponse)
cWrite("!" + input);
return input;
}
#region constructor
public Irc(string ircServer, int ircPort, string ircChannel, string ircNick, string ircName)
{
this.server = ircServer;
this.port = ircPort;
this.channel = ircChannel;
this.currentChannel = ircChannel;
this.nick = ircNick;
this.name = ircName;
this.boss = new List<string>();
this.channelNames = new List<string>();
this.channels = new List<Channel>();
this.lastPing = DateTime.Now;
}
#endregion
}
}