add support to create wiki files while running in debug

This commit is contained in:
Madpeter 2024-06-27 23:09:44 +01:00
parent e64a0e67b9
commit eb1f45f330
24 changed files with 465 additions and 18 deletions

View file

@ -3,8 +3,11 @@ using SecondBotEvents.Services;
namespace SecondBotEvents.Commands
{
[ClassInfo("Animations and Gestures")]
internal class AnimationCommands : CommandsAPI
{
public AnimationCommands(EventsSecondBot setmaster) : base(setmaster)
{
}

View file

@ -6,6 +6,7 @@ using SecondBotEvents.Services;
namespace SecondBotEvents.Commands
{
[ClassInfo("Avatars near me and Key2Name and Name2Key lookups")]
public class Avatars : CommandsAPI
{
public Avatars(EventsSecondBot setmaster) : base(setmaster)

View file

@ -4,6 +4,7 @@ using SecondBotEvents.Services;
namespace SecondBotEvents.Commands
{
[ClassInfo("Text based interaction,message status and history")]
public class Chat : CommandsAPI
{
public Chat(EventsSecondBot setmaster) : base(setmaster)

View file

@ -2,6 +2,7 @@
namespace SecondBotEvents.Commands
{
[ClassInfo("Used to test interaction with the bot... hello world for avartars")]
public class Core : CommandsAPI
{
public Core(EventsSecondBot setmaster) : base(setmaster)

View file

@ -4,6 +4,7 @@ using SecondBotEvents.Services;
namespace SecondBotEvents.Commands
{
[ClassInfo("Script dialog box interaction / events when displayed")]
public class Dialogs : CommandsAPI
{
public Dialogs(EventsSecondBot setmaster) : base(setmaster)

View file

@ -5,6 +5,7 @@ using System.Linq;
namespace SecondBotEvents.Commands
{
[ClassInfo("Control discord from the grid")]
public class DiscordCommands : CommandsAPI
{
public DiscordCommands(EventsSecondBot setmaster) : base(setmaster)

View file

@ -5,6 +5,7 @@ using System.Collections.Generic;
namespace SecondBotEvents.Commands
{
[ClassInfo("Look after a sim as the estate manager")]
public class Estate : CommandsAPI
{
public Estate(EventsSecondBot setmaster) : base(setmaster)
@ -13,7 +14,7 @@ namespace SecondBotEvents.Commands
[About("Sends the message to the current sim")]
[ReturnHints("restarting")]
[ReturnHintsFailure("Not an estate manager here")]
[ReturnHintsFailure("canceled")]
[ReturnHints("canceled")]
[ArgHints("delay", "How long to delay the restart for (30 to 240 secs) - defaults to 240 if out of bounds \n" +
"set to 0 if your canceling!")]
[ArgHints("mode", "true to start a restart, false to cancel")]
@ -44,7 +45,7 @@ namespace SecondBotEvents.Commands
[ReturnHintsFailure("Message empty")]
[ReturnHints("ok")]
[ArgHints("message", "What the message is")]
public object SimMessage(string message, string token)
public object SimMessage(string message)
{
if (SecondbotHelpers.notempty(message) == false)
{

View file

@ -5,6 +5,7 @@ using System.Collections.Generic;
namespace SecondBotEvents.Commands
{
[ClassInfo("The power of friendship")]
public class Friends : CommandsAPI
{
public Friends(EventsSecondBot setmaster) : base(setmaster)

View file

@ -4,6 +4,7 @@ using SecondBotEvents.Services;
namespace SecondBotEvents.Commands
{
[ClassInfo("Money makes the world go round")]
public class Funds : CommandsAPI
{
public Funds(EventsSecondBot setmaster) : base(setmaster)
@ -12,7 +13,7 @@ namespace SecondBotEvents.Commands
[About("Requests the current balance and requests the balance to update.")]
[ReturnHints("Current fund level")]
[ReturnHintsFailure("Funds commands are disabled")]
public object Balance(string token)
public object Balance()
{
if (master.CommandsService.myConfig.GetAllowFundsCommands() == false)
{

View file

@ -5,6 +5,7 @@ using System.Collections.Generic;
namespace SecondBotEvents.Commands
{
[ClassInfo("Control groups with ease")]
public class GroupCommands : CommandsAPI
{
public GroupCommands(EventsSecondBot setmaster) : base(setmaster)

View file

@ -6,6 +6,7 @@ using System.Collections.Generic;
namespace SecondBotEvents.Commands
{
[ClassInfo("What is going on with the bot")]
public class Info : CommandsAPI
{
public Info(EventsSecondBot setmaster) : base(setmaster)

View file

@ -11,6 +11,7 @@ using System.Linq;
namespace SecondBotEvents.Commands
{
[ClassInfo("Inventory controls for sending items and so on")]
public class InventoryCommands : CommandsAPI
{
public InventoryCommands(EventsSecondBot setmaster) : base(setmaster)

View file

@ -7,6 +7,7 @@ using System.Threading;
namespace SecondBotEvents.Commands
{
[ClassInfo("Dont like this place lets try somewhere else")]
public class Movement : CommandsAPI
{
public Movement(EventsSecondBot setmaster) : base(setmaster)

View file

@ -4,6 +4,7 @@ using SecondBotEvents.Services;
namespace SecondBotEvents.Commands
{
[ClassInfo("Why this is not a built in script command I will never know, but its due to needing this that the bot was made")]
public class Notecard : CommandsAPI
{
public Notecard(EventsSecondBot setmaster) : base(setmaster)

View file

@ -9,6 +9,7 @@ using static OpenMetaverse.ParcelManager;
namespace SecondBotEvents.Commands
{
[ClassInfo("Control the land under our feet")]
public partial class ParcelCommands : CommandsAPI
{
public ParcelCommands(EventsSecondBot setmaster) : base(setmaster)

View file

@ -6,6 +6,7 @@ using System.Linq;
namespace SecondBotEvents.Commands
{
[ClassInfo("The more we know about our self the better we are")]
public class Self : CommandsAPI
{
public Self(EventsSecondBot setmaster) : base(setmaster)
@ -150,7 +151,7 @@ namespace SecondBotEvents.Commands
[About("Gets the last 5 commands issued to the bot")]
[ReturnHints("list of commands")]
public object GetLastCommands(string token)
public object GetLastCommands()
{
List<string> reply = new List<string>();
foreach(CommandHistory A in master.DataStoreService.GetCommandHistory())

View file

@ -6,6 +6,7 @@ using System;
namespace SecondBotEvents.Commands
{
[ClassInfo("Interface for streamadmin to create notecards, you will prob never need this")]
public class StreamAdmin : CommandsAPI
{
public StreamAdmin(EventsSecondBot setmaster) : base(setmaster)

View file

@ -11,20 +11,23 @@ namespace SecondBotEvents
{
static void Main(string[] args)
{
LogFormater.Info("Welcome to Secondbot [Events build] version: " + AssemblyInfo.GetGitHash());
EventsSecondBot worker = new EventsSecondBot(args);
#if (DEBUG)
new WikiMake();
#endif
LogFormater.Info("Welcome to Secondbot [Events build] version: " + AssemblyInfo.GetGitHash());
EventsSecondBot worker = new EventsSecondBot(args);
while(worker.Exit() == false)
{
if(worker.Ready == true)
while (worker.Exit() == false)
{
worker.Status();
if (worker.Ready == true)
{
worker.Status();
}
Thread.Sleep(1000);
}
Thread.Sleep(1000);
LogFormater.Warn("Shutdown in 5 secs");
Thread.Sleep(5000);
}
LogFormater.Warn("Shutdown in 5 secs");
Thread.Sleep(5000);
}
}
public class EventsSecondBot

View file

@ -0,0 +1,63 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace SecondBotEvents.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SecondBotEvents.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
}
}

View file

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View file

@ -9,6 +9,9 @@
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<DockerfileRunEnvironmentFiles>settings.env</DockerfileRunEnvironmentFiles>
</PropertyGroup>
<ItemGroup>
<None Remove="Services\template.html" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Discord.Net" Version="3.15.2" />
<PackageReference Include="Discord.Net.WebSocket" Version="3.15.2" />
@ -25,4 +28,22 @@
<ItemGroup>
<ProjectReference Include="..\LibreMetaverse\LibreMetaverse.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<None Update="Static\template.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View file

@ -231,6 +231,16 @@ namespace SecondBotEvents.Services
}
[AttributeUsage(AttributeTargets.Class)]
public class ClassInfo : Attribute
{
public string classinfo = "";
public ClassInfo(string classinfo)
{
this.classinfo = classinfo;
}
}
[AttributeUsage(AttributeTargets.Method)]
public class About : Attribute
@ -246,7 +256,6 @@ namespace SecondBotEvents.Services
public class ReturnHints : Attribute
{
public string hint = "";
public bool good = true;
public ReturnHints(string hint)
{
this.hint = hint;
@ -254,11 +263,12 @@ namespace SecondBotEvents.Services
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class ReturnHintsFailure : ReturnHints
public class ReturnHintsFailure : Attribute
{
public ReturnHintsFailure(string hint) : base(hint)
public string hint = "";
public ReturnHintsFailure(string hint)
{
this.good = false;
this.hint = hint;
}
}

View file

@ -0,0 +1,54 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>[[PAGETITLE]]</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<style>
.card-deck {
display: flex;
flex-wrap: wrap;
align-items: stretch;
}
.card {
flex: 1 0 auto;
}
</style>
</head>
<body>
<div class="container-fluid px-2">
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container-fluid">
<a class="navbar-brand" href="index.html">Secondbot [Events]</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link" href="https://github.com/Madpeterz/SecondBot/wiki">Help docs</a>
</li>
<li class="nav-item">
<a class="nav-link" href="https://github.com/Madpeterz/SecondBot">View source</a>
</li>
<li class="nav-item">
<a class="nav-link" href="https://webui.blackatom.win/">WebUI</a>
</li>
<li class="nav-item">
<a class="nav-link" href="secondlife:///app/group/cbda0f35-1c0b-2a45-e693-62a63a8f9d88/about">Join SL group</a>
</li>
</ul>
</div>
<a class="navbar-brand" href="#">Build:[[BUILDVERSION]] [[BUILDDATE]]</a>
</div>
</nav>
<div class="container-fluid">
[[CONTENT]]
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
</body>
</html>

View file

@ -0,0 +1,157 @@
using OpenMetaverse.ImportExport.Collada14;
using RestSharp.Extensions;
using SecondBotEvents.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Resources;
using System.Security.Policy;
using System.Text;
using System.Threading.Tasks;
using static log4net.Appender.RollingFileAppender;
namespace SecondBotEvents
{
public class WikiMake
{
protected SimpleIO IO = new SimpleIO();
protected string version = "";
protected string htmltemplate = "";
public WikiMake()
{
IO.ChangeRoot("Static");
htmltemplate = IO.ReadFile("template.html");
version = AssemblyInfo.GetGitHash();
LogFormater.Info("Creating wiki files");
IO.ChangeRoot("wiki");
makehome();
}
protected void makehome()
{
Dictionary<string, Type> commandmodules = http_commands_helper.getCommandModules();
string content = "<div class=\"card-deck\">";
foreach (KeyValuePair<string, Type> entry in commandmodules)
{
Type endpointtype = entry.Value;
string namespaceworker = entry.Key.ToLower();
string url = namespaceworker + ".html";
int commandcount = 0;
foreach (MethodInfo M in endpointtype.GetMethods())
{
foreach (CustomAttributeData At in M.CustomAttributes)
{
if (At.AttributeType.Name == "About")
{
commandcount++;
break;
}
}
}
string about = entry.Value.GetAttribute<ClassInfo>().classinfo;
string card = "<div class=\"card mt-2 mx-2\" style=\"width: 18rem;\">"
+ "<div class=\"card-body\">"
+ "<h5 class=\"card-title\">" + namespaceworker.FirstCharToUpper() + "</h5>"
+ "<p class=\"card-text\">" + about + "</p>"
+ "<a href = \"" + url + "\" class=\"btn btn-primary\">View " + commandcount.ToString() + " commands</a>"
+ "</div>"
+ "</div>";
content = content + card;
makecommandlist(endpointtype, namespaceworker, about);
}
makefile("index", content, "Index");
}
protected void makecommandlist(Type endpoint, string namespaceworker, string aboutnamespace)
{
string content = "<br/><a href=\"index.html\"><- Back to command sections</a><br/> " +
"<h4>"+namespaceworker.FirstCharToUpper()+"</h4><p>"+ aboutnamespace + "</p>"
+"<hr/><div class=\"table-responsive\"><table class=\"table table-hover table-bordered table-striped\">";
content = content + "<thead><tr><th>Command</th>";
content = content + "<th>About</th></tr></thead><tbody>";
foreach (MethodInfo M in endpoint.GetMethods())
{
bool isCallable = false;
string about = "";
foreach (CustomAttributeData At in M.CustomAttributes)
{
if (At.AttributeType.Name == "About")
{
about = M.GetAttribute<About>().about;
isCallable = true;
break;
}
}
if(isCallable == false)
{
continue;
}
string url = "command" + M.Name.ToLower() + ".html";
content = content + "<tr>";
content = content + "<td><a href=\""+ url+"\">" + M.Name + "</a></td>";
content = content + "<td>" + about + "</td></tr>";
makecommandfile(namespaceworker, M, about);
}
content = content + "</tbody></table></div>";
makefile(namespaceworker, content, "Command list for "+ namespaceworker.FirstCharToUpper());
}
protected void makecommandfile(string namespaceworker, MethodInfo method, string methodabout)
{
string content = "<br/><a href=\"" + namespaceworker + ".html\"><- Back to "+namespaceworker.FirstCharToUpper()+"</a><br/> " +
"<h4>" + method.Name + "</h4></a><p>" + methodabout + "</p>"
+ "<hr/>";
Dictionary<string,string> hints = new Dictionary<string,string>();
foreach (ArgHints At in method.GetCustomAttributes<ArgHints>())
{
hints.Add(At.name, At.about);
}
if (method.GetParameters().Length > 0)
{
content = content + "<h5>Args</h5><div class=\"table-responsive\"><table class=\"table table-hover table-bordered table-striped\">";
content = content + "<thead><tr><th>Name</th>";
content = content + "<th>Hint</th></tr></thead><tbody>";
foreach (ParameterInfo pram in method.GetParameters())
{
content = content + "<tr>";
content = content + "<td>" + pram.Name + "</a></td>";
string hinttext = "";
if (hints.ContainsKey(pram.Name) == true)
{
hinttext = hints[pram.Name];
}
content = content + "<td>" + hinttext + "</td></tr>";
}
content = content + "</tbody></table></div>";
}
content = content + "<h5>Return hints</h5><ul class=\"list-group\">";
foreach (ReturnHints At in method.GetCustomAttributes<ReturnHints>())
{
content = content + "<li class=\"list-group-item text-success\">☑ "+At.hint+"</li>";
}
foreach (ReturnHintsFailure At in method.GetCustomAttributes<ReturnHintsFailure>())
{
content = content + "<li class=\"list-group-item text-danger\">❌ "+ At.hint+"</li>";
}
content = content + "</ul>";
makefile("command"+method.Name.ToLower(), content, "Command info for " + method.Name);
}
protected void makefile(string file, string content, string title)
{
string pagecontent = htmltemplate;
pagecontent = pagecontent.Replace("[[PAGETITLE]]", "Secondbot [Events] command wiki / "+title);
pagecontent = pagecontent.Replace("[[BUILDVERSION]]", version);
pagecontent = pagecontent.Replace("[[BUILDDATE]]", "Wiki created:"+DateTime.Now.ToString("dd/MM/yyyy"));
pagecontent = pagecontent.Replace("[[CONTENT]]", content);
IO.WriteFile(file+".html", pagecontent);
}
}
}