--- layout: post status: publish published: true title: Creating an IRC front/back-end from a C# web application wordpress_id: 249 wordpress_url: http://pro.grammatic.org/post-creating-an-irc-frontbackend-from-a-c-web-application-53.aspx date: !binary |- MjAwOC0wOS0xNyAxMzowMzoyMCArMDIwMA== date_gmt: !binary |- MjAwOC0wOS0xNyAxMzowMzoyMCArMDIwMA== categories: - Technology - .NET tags: - C# - eggdrop - IRC - bot comments: [] ---
This lengthy howto will show you how to hook up C# to an eggdrop IRC bot. I've taken this approach because it avoids the overhead of managing a fully fledged IRC client in C# whilst still providing 2-way command functionality between IRC and the application.
The app works by:
So, let's get started!
The first part is the main IRC class. The version implemented here comes with a log4net appender implementation so that errors can be logged directly to IRC.
IRC.cs
{% highlight csharp %} using System; using System.Collections.Generic; using System.Text; using System.Net; using System.Net.Sockets; using System.Collections.Specialized; using System.Configuration; using System.IO; using System.Threading; namespace Logging { public class IRCLogger : log4net.Appender.AppenderSkeleton { protected override void Append(log4net.Core.LoggingEvent loggingEvent) { if (loggingEvent.Level == log4net.Core.Level.Error) { if (loggingEvent.ExceptionObject != null) { IRC.SendAdminMessage(loggingEvent.MessageObject.ToString() + ": " + loggingEvent.ExceptionObject.Message); } else { IRC.SendAdminMessage(loggingEvent.MessageObject.ToString()); } DateTime gmt = DateTime.Now.AddHours(5); IRC.SetAdminTopic("Last error rececived on " + gmt.ToShortDateString() + " at " + gmt.ToShortTimeString() + " (GMT)."); } } } public class IRC { static TcpClient client = null; static NetworkStream ns = null; static StreamWriter sw = null; static StreamReader sr = null; static bool isConnecting = false; static bool isSending = false; static ListThen, put the relevant stuff into your web.config file:
{% highlight xml %}Tada - it should hook up! But what about getting commands from the bot I hear you cry? Well, first of all you'll need to hook up the following TCL to your eggdrop:
{% highlight tcl %} package require http 2.7 bind pubm - * webServiceRequest proc webServiceRequest {nick uhost hand chan text} { if {[string index $text 0] == "!"} { set token [::http::geturl "http://www.yoursite.com/webservice.asmx" \ -type "text/xml; charset=utf-8" \ -headers {"SOAPAction" "http://www.yoursite.com/IrcMessage"} \ -query " \Then, the webservice at http://www.yoursite.com/webservice.asmx should expose the following web method:
{% highlight csharp %} [WebMethod] [PrincipalPermissionAttribute(SecurityAction.Demand, Role = "Bot")] [SoapHeader("authentication")] public void IrcMessage(string user, string hostmark, string channel, string message) { IRC.ParseIrcMessage(user, hostmark, channel, message); } {% endhighlight %}With which you can do what you like! The only proviso you might require is the following setup for the authentication SOAP header:
{% highlight csharp %} ///And the following HttpModule:
{% highlight csharp %} using System; using System.Web; using System.IO; using System.Xml; using System.Xml.XPath; using System.Text; using System.Web.Services.Protocols; using System.Security.Principal; using System.Web.Security; using System.Diagnostics; using Logging; namespace YourWebService { public sealed class WebServiceAuthenticationModule : IHttpModule { public void Dispose() { } public void Init(HttpApplication app) { app.AuthenticateRequest += new EventHandler(this.OnEnter); } public string ModuleName { get { return "WebServiceAuthentication"; } } void OnEnter(Object source, EventArgs eventArgs) { HttpApplication app = (HttpApplication)source; HttpContext context = app.Context; Stream HttpStream = context.Request.InputStream; // Save the current position of stream. long posStream = HttpStream.Position; // If the request contains an HTTP_SOAPACTION // header, look at this message. if (context.Request.ServerVariables["HTTP_SOAPACTION"] == null) return; // Load the body of the HTTP message // into an XML document. XmlDocument dom = new XmlDocument(); string soapUser; string soapPassword; string soapVersion; try { dom.Load(HttpStream); // Reset the stream position. HttpStream.Position = posStream; // Bind to the Authentication header. soapUser = dom.GetElementsByTagName("User").Item(0).InnerText; soapPassword = dom.GetElementsByTagName("Password").Item(0).InnerText; soapVersion = dom.GetElementsByTagName("Version").Item(0).InnerText; if (Membership.ValidateUser(soapUser, soapPassword)) { MembershipUser user = Membership.GetUser(soapUser); GenericIdentity identity = new GenericIdentity(user.UserName); RolePrincipal rp = new RolePrincipal(identity); HttpContext.Current.User = rp; user.Comment = soapVersion; Membership.UpdateUser(user); Logger.Log.Info("Authenticated: " + soapUser + " :: " + soapVersion); } else { Logger.Log.Info("Failed to login as " + soapUser); Logging.IRC.SendAdminMessage("Attempt to login as " + soapUser + " with password " + soapPassword + " failed."); } } catch (Exception e) { Logging.IRC.SendAdminMessage("Error in SOAP parser: " + e.Message); // Reset the position of stream. HttpStream.Position = posStream; // Throw a SOAP exception. XmlQualifiedName name = new XmlQualifiedName("Load"); SoapException soapException = new SoapException( "Unable to read SOAP request", name, e); throw soapException; } return; } } } {% endhighlight %}Which, of course, you will need to load in web.config:
{% highlight xml %}