My friend and I are writing an IRC C# bot, and we are looking for a way to include a module system so that users can write custom modules to extend function.
The bot uses Regex to split up all raw data from the server, and then triggers the appropriate event with the data. For example, a typical event handler might look like:
OnChannelMessage(object sender, ChannelMessageEventArgs e) { }
In ChannelMessageEventArgs
would be the channel name, the nick of the sender, message etc...
I'd like to have a plugin system so that people can build modules and load / unload them at will, when the bot loads, or while its running.
Ideally I'd like to be able to compile the .cs
files on the fly, and in the plugin.cs file, would be a few things:
Thanks for any ideas on where to start for someone who is relatively new at programming.
Modular programming consists of separating implementation from interface and hiding information in the implementation. In C this is achieved by placing the interface definition in a header file and the implementation in a source file. Disciplined use of static is used to hide implementation details.
Modularity is closely tied with encapsulation; think of modularity as a way of mapping encapsulated abstractions into real, physical modules. The C/C++ convention is to create two files for each class: a header file (. h suffix) for the class interface, and an implementation file (.
The technique of breaking down one big solution into smaller modules for ease of development, implementation, modification and maintenance is called modular technique of programming or software development.
I've used stuff like this on projects before, but it's been a while. There are probably frameworks out there that do this kind of stuff for you.
To write your own plug-in architecture, basically you want to define an interface for all the modules to implement, and put this in an assembly shared by both your program and the modules:
public interface IModule { //functions/properties/events of a module }
Then your implementors will code their modules to this assembly, preferably with a default constructor.
public class SomeModule : IModule {} ///stuff
In your program (supposing your modules will be compiled into their own assemblies) you load a reference an assembly containing a module, find the types that implement the module interface, and instantiate them:
var moduleAssembly = System.Reflection.Assembly.LoadFrom("assembly file"); var moduleTypes = moduleAssembly.GetTypes().Where(t => t.GetInterfaces().Contains(typeof(IModule))); var modules = moduleTypes.Select( type => { return (IModule) Activator.CreateInstance(type); });
If you want to compile the code on the fly: you create a compiler object, tell it what assemblies to reference (System and the one that contains IModule, plus any other references needed), tell it to compile the source file into an assembly. From that, you get the exported types, filter keeping those which implement the IModule, and instantiate them.
//I made up an IModule in namespace IMod, with string property S string dynamicCS = @"using System; namespace DYN { public class Mod : IMod.IModule { public string S { get { return \"Im a module\"; } } } }"; var compiler = new Microsoft.CSharp.CSharpCodeProvider().CreateCompiler(); var options = new System.CodeDom.Compiler.CompilerParameters( new string[]{"System.dll", "IMod.dll"}); var dynamicAssembly= compiler.CompileAssemblyFromSource(options, dynamicCS); //you need to check it for errors here var dynamicModuleTypes = dynamicAssembly.CompiledAssembly.GetExportedTypes() .Where(t => t.GetInterfaces().Contains(typeof(IMod.IModule))); var dynamicModules = dynModType.Select(t => (IMod.IModule)Activator.CreateInstance(t));
Look up tutorials on plug-in architectures and loading dynamic assmeblies to get a better feel for doing this kind of stuff. This is just the start. Once you get the basics down, you can start doing some really cool things.
As for handling the metadata (module X is named YYY and should handle events A,B and C): Try to work this into your type system. You could make up different interfaces for the different functions/events, or you could put all the functions on one interface, and put attributes (you'd declare these in the shared assembly) on the module classes, using the attributes to declare which events the module should subscribe to. Basically you want to make it as simple as possible for people to write modules for your system.
enum ModuleTypes { ChannelMessage, AnotherEvent, .... } [Shared.Handles(ModuleTypes.ChannelMessage)] [Shared.Handles(ModuleTypes.AnotherEvent)] class SomeModule : IModule { ... }
or
//this is a finer-grained way of doing it class ChannelMessageLogger : IChannelMessage {} class PrivateMessageAutoReply : IPrivateMessage {}
have fun!
The Managed Extensibility Framework (MEF) provides exactly what you are looking for except they call it a Plugin architecture. You could provide a common interface which you users could build on and then MEF could scan a directory for dlls and load any Exports dynamically.
For example your plugin authors could create something like
[Export(typeof(IModule))] public class MyModule : IModule { void HandleMessage(ChannelEventArgs e) {} }
Then your code could look something like this:
void OnChannelMessage(ChannelEventArgs e) { foreach(IModule module in Modules) { module.HandleMessage(e); } } [Import] public IEnumerable<IModule> Modules { get; set; }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With