30 using System.Collections.Generic;
32 using System.Net.Sockets;
33 using System.Reflection;
34 using System.Text.RegularExpressions;
35 using System.Threading;
39 using OpenSim.Framework;
40 using OpenSim.Framework.Monitoring;
41 using OpenSim.Region.Framework.Interfaces;
42 using OpenSim.Region.Framework.Scenes;
49 #region Global (static) state
51 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
58 private static readonly
char[] CS_SPACE = {
' ' };
60 private const int WD_INTERVAL = 1000;
61 private static int PING_PERIOD = 15;
62 private static int ICCD_PERIOD = 10;
63 private static int L_TIMEOUT = 25;
65 private static int _idk_ = 0;
66 private static int _pdk_ = 0;
67 private static int _icc_ = ICCD_PERIOD;
71 private static List<IRCConnector> m_connectors =
new List<IRCConnector>();
75 private static System.Timers.Timer m_watchdog = null;
82 m_log.DebugFormat(
"[IRC-Connector]: Static initialization started");
83 m_watchdog =
new System.Timers.Timer(WD_INTERVAL);
84 m_watchdog.Elapsed +=
new ElapsedEventHandler(WatchdogHandler);
85 m_watchdog.AutoReset =
true;
87 m_log.DebugFormat(
"[IRC-Connector]: Static initialization complete");
92 #region Instance state
96 internal int idn = _idk_++;
103 internal int depends = 0;
110 internal int m_resetk = 0;
114 internal bool m_randomizeNick =
true;
115 internal string m_baseNick = null;
116 internal string m_nick = null;
120 get {
return m_nick; }
121 set { m_nick = value; }
124 private bool m_enabled =
false;
127 get {
return m_enabled; }
130 private bool m_connected =
false;
131 private bool m_pending =
false;
132 private int m_timeout = L_TIMEOUT;
133 public bool Connected
135 get {
return m_connected; }
138 private string m_ircChannel;
139 public string IrcChannel
141 get {
return m_ircChannel; }
142 set { m_ircChannel = value; }
145 private uint m_port = 6667;
148 get {
return m_port; }
149 set { m_port = value; }
152 private string m_server = null;
155 get {
return m_server; }
156 set { m_server = value; }
158 private string m_password = null;
159 public string Password
161 get {
return m_password; }
162 set { m_password = value; }
165 private string m_user =
"USER OpenSimBot 8 * :I'm an OpenSim to IRC bot";
168 get {
return m_user; }
173 private TcpClient m_tcp;
174 private NetworkStream m_stream = null;
175 private StreamReader m_reader;
176 private StreamWriter m_writer;
180 internal string usermod = String.Empty;
181 internal string chanmod = String.Empty;
182 internal string version = String.Empty;
183 internal bool motd =
false;
187 #region connector instance management
200 m_server = cs.Server;
201 m_password = cs.Password;
202 m_baseNick = cs.BaseNickname;
203 m_randomizeNick = cs.RandomizeNickname;
204 m_ircChannel = cs.IrcChannel;
208 if (m_watchdog == null)
212 ICCD_PERIOD = cs.ConnectDelay;
213 PING_PERIOD = cs.PingDelay;
229 if (m_server == null || m_baseNick == null || m_ircChannel == null || m_user == null)
230 throw new Exception(
"Invalid connector configuration");
235 m_nick = m_baseNick + Util.RandomClass.Next(1, 99);
239 m_log.InfoFormat(
"[IRC-Connector-{0}]: Initialization complete", idn);
262 m_connectors.Add(
this);
274 m_log.InfoFormat(
"[IRC-Connector-{0}] Closing", idn);
279 if ((depends == 0) && Enabled)
286 m_log.DebugFormat(
"[IRC-Connector-{0}] Closing interface", idn);
292 m_writer.WriteLine(String.Format(
"QUIT :{0} to {1} wormhole to {2} closing",
293 m_nick, m_ircChannel, m_server));
296 catch (Exception) { }
301 try { m_writer.Close(); }
302 catch (Exception) { }
303 try { m_reader.Close(); }
304 catch (Exception) { }
305 try { m_stream.Close(); }
306 catch (Exception) { }
307 try { m_tcp.Close(); }
308 catch (Exception) { }
313 m_connectors.Remove(
this);
318 m_log.InfoFormat(
"[IRC-Connector-{0}] Closed", idn);
324 #region session management
336 while (_icc_ < ICCD_PERIOD)
339 m_log.DebugFormat(
"[IRC-Connector-{0}]: Connection request for {1} on {2}:{3}", idn, m_nick, m_server, m_ircChannel);
349 if (m_connected)
return;
353 m_timeout = L_TIMEOUT;
355 m_tcp =
new TcpClient(m_server, (
int)m_port);
356 m_stream = m_tcp.GetStream();
357 m_reader =
new StreamReader(m_stream);
358 m_writer =
new StreamWriter(m_stream);
360 m_log.InfoFormat(
"[IRC-Connector-{0}]: Connected to {1}:{2}", idn, m_server, m_port);
362 WorkManager.StartThread(ListenerRun,
"IRCConnectionListenerThread", ThreadPriority.Normal,
true,
false);
365 if (m_password != null)
366 m_writer.WriteLine(String.Format(
"PASS {0}", m_password));
367 m_writer.WriteLine(String.Format(
"NICK {0}", m_nick));
369 m_writer.WriteLine(m_user);
374 m_log.ErrorFormat(
"[IRC-Connector-{0}] cannot connect {1} to {2}:{3}: {4}",
375 idn, m_nick, m_server, m_port, e.Message);
394 m_log.DebugFormat(
"[IRC-Connector-{0}]: Reconnect request for {1} on {2}:{3}", idn, m_nick, m_server, m_ircChannel);
404 m_log.InfoFormat(
"[IRC-Connector-{0}] Resetting connector", idn);
415 try { m_writer.Close(); }
416 catch (Exception) { }
417 try { m_reader.Close(); }
418 catch (Exception) { }
419 try { m_tcp.Close(); }
420 catch (Exception) { }
436 #region Outbound (to-IRC) message handlers
438 public void PrivMsg(
string pattern,
string from,
string region,
string msg)
448 m_writer.WriteLine(pattern, m_ircChannel, from, region, msg);
454 m_log.ErrorFormat(
"[IRC-Connector-{0}]: PrivMsg I/O Error: disconnected from IRC server", idn);
459 m_log.ErrorFormat(
"[IRC-Connector-{0}]: PrivMsg exception : {1}", idn, ex.Message);
472 m_writer.WriteLine(msg);
478 m_log.ErrorFormat(
"[IRC-Connector-{0}] Disconnected from IRC server.(Send)", idn);
483 m_log.ErrorFormat(
"[IRC-Connector-{0}] Send exception trap: {0}", idn, ex.Message);
495 int resetk = m_resetk;
499 while (m_enabled && m_connected)
501 if ((inputLine = m_reader.ReadLine()) == null)
502 throw new Exception(
"Listener input socket closed");
504 Watchdog.UpdateThread();
508 if (inputLine.Contains(
"PRIVMSG"))
510 Dictionary<string, string> data = ExtractMsg(inputLine);
516 c.Message = data[
"msg"];
517 c.Type = ChatTypeEnum.Region;
518 c.Position = CenterOfRegion;
519 c.From = data[
"nick"] +
"@IRC";
521 c.SenderUUID = UUID.Zero;
526 if ((1 == c.
Message[0]) && c.
Message.Substring(1).StartsWith(
"ACTION"))
527 c.Message = String.Format(
"/me {0}", c.Message.Substring(8, c.Message.Length - 9));
529 ChannelState.OSChat(
this, c,
false);
534 ProcessIRCCommand(inputLine);
548 if (m_enabled && (m_resetk == resetk))
551 Watchdog.RemoveThread();
554 private Regex RE =
new Regex(
@":(?<nick>[\w-]*)!(?<user>\S*) PRIVMSG (?<channel>\S+) :(?<msg>.*)",
555 RegexOptions.Multiline);
557 private Dictionary<string, string> ExtractMsg(
string input)
564 Dictionary<string, string> result = null;
565 MatchCollection matches = RE.Matches(input);
568 if ((matches.Count == 0) || (matches.Count != 1) || (matches[0].Groups.Count != 5))
578 result =
new Dictionary<string, string>();
579 result.Add(
"nick", matches[0].Groups[1].Value);
580 result.Add(
"user", matches[0].Groups[2].Value);
581 result.Add(
"channel", matches[0].Groups[3].Value);
582 result.Add(
"msg", matches[0].Groups[4].Value);
593 c.Message = String.Format(
format, args);
594 c.Type = ChatTypeEnum.Region;
595 c.Position = CenterOfRegion;
597 c.SenderUUID = UUID.Zero;
599 ChannelState.OSChat(
this, c,
true);
604 m_log.ErrorFormat(
"[IRC-Connector-{0}]: BroadcastSim Exception Trap: {1}\n{2}", idn, ex.Message, ex.StackTrace);
608 #region IRC Command Handlers
614 string c_server = m_server;
616 string pfx = String.Empty;
617 string cmd = String.Empty;
618 string parms = String.Empty;
626 commArgs = command.Split(CS_SPACE, 2);
628 if (commArgs[0].StartsWith(
":"))
630 pfx = commArgs[0].Substring(1);
631 commArgs = commArgs[1].Split(CS_SPACE, 2);
650 m_log.DebugFormat(
"[IRC-Connector-{0}] [{1}] parms = <{2}>", idn, cmd, parms);
651 commArgs = parms.Split(CS_SPACE);
652 c_server = commArgs[1];
654 version = commArgs[2];
655 usermod = commArgs[3];
656 chanmod = commArgs[4];
658 m_writer.WriteLine(String.Format(
"JOIN {0}", m_ircChannel));
660 m_log.InfoFormat(
"[IRC-Connector-{0}]: sent request to join {1} ", idn, m_ircChannel);
689 m_nick = m_baseNick + Util.RandomClass.Next(1, 99);
690 m_log.ErrorFormat(
"[IRC-Connector-{0}]: [{1}] IRC SERVER reports NicknameInUse, trying {2}", idn, cmd, m_nick);
692 m_writer.WriteLine(String.Format(
"NICK {0}", m_nick));
694 m_writer.WriteLine(m_user);
696 m_writer.WriteLine(String.Format(
"JOIN {0}", m_ircChannel));
700 m_log.ErrorFormat(
"[IRC-Connector-{0}] [{1}] {2}", idn, cmd, parms.Split(CS_SPACE, 2)[1]);
701 m_log.ErrorFormat(
"[IRC-Connector-{0}] [{1}] Connector disabled", idn, cmd);
710 m_log.ErrorFormat(
"[IRC-Connector-{0}] [{1}] {2}", idn, cmd, parms.Split(CS_SPACE, 2)[1]);
711 if (parms.Contains(
"reconnect too fast"))
717 m_log.DebugFormat(
"[IRC-Connector-{0}] [{1}] parms = <{2}>", idn, cmd, parms);
718 m_writer.WriteLine(String.Format(
"PONG {0}", parms));
725 m_log.DebugFormat(
"[IRC-Connector-{0}] [{1}] parms = <{2}>", idn, cmd, parms);
726 eventIrcJoin(pfx, cmd, parms);
729 m_log.DebugFormat(
"[IRC-Connector-{0}] [{1}] parms = <{2}>", idn, cmd, parms);
730 eventIrcPart(pfx, cmd, parms);
733 m_log.DebugFormat(
"[IRC-Connector-{0}] [{1}] parms = <{2}>", idn, cmd, parms);
734 eventIrcMode(pfx, cmd, parms);
737 m_log.DebugFormat(
"[IRC-Connector-{0}] [{1}] parms = <{2}>", idn, cmd, parms);
738 eventIrcNickChange(pfx, cmd, parms);
741 m_log.DebugFormat(
"[IRC-Connector-{0}] [{1}] parms = <{2}>", idn, cmd, parms);
742 eventIrcKick(pfx, cmd, parms);
745 m_log.DebugFormat(
"[IRC-Connector-{0}] [{1}] parms = <{2}>", idn, cmd, parms);
746 eventIrcQuit(pfx, cmd, parms);
749 m_log.DebugFormat(
"[IRC-Connector-{0}] Command '{1}' ignored, parms = {2}", idn, cmd, parms);
759 string[] args = parms.Split(CS_SPACE, 2);
760 string IrcUser = prefix.Split(
'!')[0];
761 string IrcChannel = args[0];
763 if (IrcChannel.StartsWith(
":"))
764 IrcChannel = IrcChannel.Substring(1);
766 if(IrcChannel == m_ircChannel)
768 m_log.InfoFormat(
"[IRC-Connector-{0}] Joined requested channel {1} at {2}", idn, IrcChannel,m_server);
772 m_log.InfoFormat(
"[IRC-Connector-{0}] Joined unknown channel {1} at {2}", idn, IrcChannel,m_server);
773 BroadcastSim(IrcUser,
"/me joins {0}", IrcChannel);
778 string[] args = parms.Split(CS_SPACE, 2);
779 string IrcUser = prefix.Split(
'!')[0];
780 string IrcChannel = args[0];
782 m_log.DebugFormat(
"[IRC-Connector-{0}] Event: IRCPart {1}:{2}", idn, m_server, m_ircChannel);
783 BroadcastSim(IrcUser,
"/me parts {0}", IrcChannel);
788 string[] args = parms.Split(CS_SPACE, 2);
791 m_log.DebugFormat(
"[IRC-Connector-{0}] Event: IRCMode {1}:{2}", idn, m_server, m_ircChannel);
792 if (UserMode.Substring(0, 1) ==
":")
794 UserMode = UserMode.Remove(0, 1);
800 string[] args = parms.Split(CS_SPACE, 2);
801 string UserOldNick = prefix.Split(
'!')[0];
802 string UserNewNick = args[0].Remove(0, 1);
804 m_log.DebugFormat(
"[IRC-Connector-{0}] Event: IRCNickChange {1}:{2}", idn, m_server, m_ircChannel);
805 BroadcastSim(UserOldNick,
"/me is now known as {0}", UserNewNick);
810 string[] args = parms.Split(CS_SPACE, 3);
811 string UserKicker = prefix.Split(
'!')[0];
812 string IrcChannel = args[0];
813 string UserKicked = args[1];
814 string KickMessage = args[2];
816 m_log.DebugFormat(
"[IRC-Connector-{0}] Event: IRCKick {1}:{2}", idn, m_server, m_ircChannel);
817 BroadcastSim(UserKicker,
"/me kicks kicks {0} off {1} saying \"{2}\"", UserKicked, IrcChannel, KickMessage);
819 if (UserKicked == m_nick)
821 BroadcastSim(m_nick,
"Hey, that was me!!!");
828 string IrcUser = prefix.Split(
'!')[0];
829 string QuitMessage = parms;
831 m_log.DebugFormat(
"[IRC-Connector-{0}] Event: IRCQuit {1}:{2}", idn, m_server, m_ircChannel);
832 BroadcastSim(IrcUser,
"/me quits saying \"{0}\"", QuitMessage);
837 #region Connector Watch Dog
848 _pdk_ = (_pdk_ + 1) % PING_PERIOD;
868 m_log.ErrorFormat(
"[IRC-Watchdog] Exception on connector {0}: {1} ", connector.idn, e.Message);
874 if (connector.m_pending)
876 if (connector.m_timeout == 0)
878 m_log.ErrorFormat(
"[IRC-Watchdog] Login timed-out for connector {0}, reconnecting", connector.idn);
879 connector.Reconnect();
882 connector.m_timeout--;
893 connector.m_writer.WriteLine(String.Format(
"PING :{0}", connector.m_server));
894 connector.m_writer.Flush();
898 m_log.ErrorFormat(
"[IRC-PingRun] Exception on connector {0}: {1} ", connector.idn, e.Message);
900 connector.Reconnect();
void PrivMsg(string pattern, string from, string region, string msg)
void eventIrcKick(string prefix, string command, string parms)
static void WatchdogHandler(Object source, ElapsedEventArgs args)
void eventIrcQuit(string prefix, string command, string parms)
string Message
The message sent by the user
void eventIrcJoin(string prefix, string command, string parms)
void eventIrcMode(string prefix, string command, string parms)
void eventIrcPart(string prefix, string command, string parms)
void BroadcastSim(string sender, string format, params string[] args)
void ProcessIRCCommand(string command)
void eventIrcNickChange(string prefix, string command, string parms)