HTTP and Discord updates
config values no longer used: Http_Port Http_PublicUrl new config values: DiscordFull_Keep_GroupChat
This commit is contained in:
parent
3c4158bcf4
commit
e3bb28ff8e
26 changed files with 856 additions and 1363 deletions
|
|
@ -44,9 +44,7 @@ namespace BetterSecondBotShared.Json
|
|||
|
||||
// HTTP interface
|
||||
public bool Http_Enable { get; set; }
|
||||
public int Http_Port { get; set; }
|
||||
public string Http_Host { get; set; }
|
||||
public string Http_PublicUrl { get; set; }
|
||||
|
||||
// Name2Key DB
|
||||
public bool Name2Key_Enable { get; set; }
|
||||
|
|
|
|||
|
|
@ -126,17 +126,9 @@ namespace BetterSecondBotShared.Json
|
|||
{
|
||||
return "Allow the bot to start a HTTP interface (Required for web UI and Post commands)";
|
||||
}
|
||||
else if (cmd == "Http_Port")
|
||||
{
|
||||
return "The port the HTTP interface should be running on";
|
||||
}
|
||||
else if (cmd == "Http_Host")
|
||||
{
|
||||
return "The URL the interface is on example use \"docker\" for auto or \"http://localhost:portnum\"";
|
||||
}
|
||||
else if (cmd == "Http_PublicUrl")
|
||||
{
|
||||
return "The public url used to access the web ui";
|
||||
return "The URL the interface is on example use \"docker\" for auto or \"http://*:8080\" to listen on port 8080<br/>Note: for windows you might need to accept a firewall alert and restart the app!";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
|
@ -265,15 +257,14 @@ namespace BetterSecondBotShared.Json
|
|||
else if (cmd == "DiscordFull_Enable") { return "False"; }
|
||||
else if (cmd == "DiscordFull_Token") { return ""; }
|
||||
else if (cmd == "DiscordFull_ServerID") { return ""; }
|
||||
else if (cmd == "DiscordFull_Keep_GroupChat") { return "False"; }
|
||||
return "";
|
||||
}
|
||||
protected string GetValueHint_HTTP(string cmd)
|
||||
{
|
||||
// HTTP
|
||||
if (cmd == "Http_Enable") { return "False"; }
|
||||
else if (cmd == "Http_Port") { return "80"; }
|
||||
else if (cmd == "Http_Host") { return "http://localhost:80"; }
|
||||
else if (cmd == "Http_PublicUrl") { return "http://localhost/"; }
|
||||
else if (cmd == "Http_Host") { return "http://*:8080"; }
|
||||
return "";
|
||||
}
|
||||
protected string GetValueHint_DiscordTTS(string cmd)
|
||||
|
|
@ -409,13 +400,13 @@ namespace BetterSecondBotShared.Json
|
|||
{
|
||||
LogFormater.Warn("Http disabled: Security_WebUIKey length must be 12 or more");
|
||||
}
|
||||
else if (jsonConfig.Http_Port < 80)
|
||||
{
|
||||
LogFormater.Warn("Http disabled: Httpport range below protected range - Given: (" + jsonConfig.Http_Port + ")");
|
||||
}
|
||||
else if ((jsonConfig.Http_Host != "docker") && (jsonConfig.Http_Host.StartsWith("http") == false))
|
||||
{
|
||||
LogFormater.Warn("Http disabled: Http_Host must be vaild: http://XXXXXX or \"docker\" - Given: (" + jsonConfig.Http_Host + ")");
|
||||
LogFormater.Warn("Http disabled: Http_Host must be vaild or \"docker\" - Given: (" + jsonConfig.Http_Host + ")");
|
||||
}
|
||||
else if (jsonConfig.Http_Host == "docker")
|
||||
{
|
||||
jsonConfig.Http_Host = "http://*:8080";
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -26,35 +26,18 @@
|
|||
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="HttpWebUi\theme\images\favicon.png" />
|
||||
<None Remove="HttpWebUi\theme\images\logo.png" />
|
||||
<None Remove="HttpWebUi\theme\layout\cdn.css.layout" />
|
||||
<None Remove="HttpWebUi\theme\layout\cdn.js.layout" />
|
||||
<None Remove="HttpWebUi\theme\layout\full.layout" />
|
||||
<None Remove="HttpWebUi\theme\layout\login.block" />
|
||||
<None Remove="HttpWebUi\theme\layout\sidemenu.layout" />
|
||||
<None Remove="HttpWebUi\theme\site.css" />
|
||||
<None Remove="HttpWebUi\theme\site.js" />
|
||||
<None Remove="WikiMake\wiki_footer.txt" />
|
||||
<None Remove="WikiMake\wiki_header.txt" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="EmbedIO" Version="3.4.3" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.10.8" />
|
||||
<PackageReference Include="MSBuildGitHash" Version="2.0.1">
|
||||
<PackageReference Include="MSBuildGitHash" Version="2.0.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="HttpWebUi\theme\images\favicon.png" />
|
||||
<EmbeddedResource Include="HttpWebUi\theme\images\logo.png" />
|
||||
<EmbeddedResource Include="HttpWebUi\theme\layout\cdn.css.layout" />
|
||||
<EmbeddedResource Include="HttpWebUi\theme\layout\cdn.js.layout" />
|
||||
<EmbeddedResource Include="HttpWebUi\theme\layout\full.layout" />
|
||||
<EmbeddedResource Include="HttpWebUi\theme\layout\login.block" />
|
||||
<EmbeddedResource Include="HttpWebUi\theme\layout\sidemenu.layout" />
|
||||
<EmbeddedResource Include="HttpWebUi\theme\css\site.css" />
|
||||
<EmbeddedResource Include="HttpWebUi\theme\js\site.js" />
|
||||
<EmbeddedResource Include="WikiMake\wiki_footer.txt" />
|
||||
<EmbeddedResource Include="WikiMake\wiki_header.txt" />
|
||||
</ItemGroup>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,8 @@
|
|||
using BetterSecondBot.HttpServer;
|
||||
using BetterSecondBotShared.IO;
|
||||
using BetterSecondBot.HttpService;
|
||||
using BetterSecondBotShared.Json;
|
||||
using BetterSecondBotShared.logs;
|
||||
using BetterSecondBotShared.Static;
|
||||
using BSB.bottypes;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using static BetterSecondBot.Program;
|
||||
|
||||
|
|
@ -51,8 +47,11 @@ namespace BetterSecondBot
|
|||
Bot.Start();
|
||||
if (Config.Http_Enable == true)
|
||||
{
|
||||
/*
|
||||
SecondBotHttpServer my_http_server = new SecondBotHttpServer();
|
||||
my_http_server.StartHttpServer(Bot, Config);
|
||||
*/
|
||||
new HttpAsService(Bot, Config);
|
||||
}
|
||||
if(use_self_keep_alive == true)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ namespace BetterSecondBot.DiscordSupervisor
|
|||
}
|
||||
}
|
||||
}
|
||||
string DiscordBotStatus_laststatus = "";
|
||||
|
||||
public string GetStatus()
|
||||
{
|
||||
string reply = "";
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ namespace BetterSecondBot.DiscordSupervisor
|
|||
else
|
||||
{
|
||||
controler.Bot.Debug("DiscordBotChatControler - Localchat:" + sender_name + ":" + message);
|
||||
await SendMessageToChannelAsync("localchat", "" + sender_name + ":" + message, "bot", UUID.Zero, "bot").ConfigureAwait(false);
|
||||
await SendMessageToChannelAsync("localchat", "" + sender_name + ": " + message, "bot", UUID.Zero, "bot").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -1,277 +0,0 @@
|
|||
using BetterSecondBot.HttpWebUi;
|
||||
using BetterSecondBotShared.IO;
|
||||
using BetterSecondBotShared.Json;
|
||||
using BetterSecondBotShared.logs;
|
||||
using BetterSecondBotShared.Static;
|
||||
using BSB;
|
||||
using BSB.bottypes;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
|
||||
namespace BetterSecondBot.HttpServer
|
||||
{
|
||||
public class Json_http_reply
|
||||
{
|
||||
public bool status;
|
||||
public string message;
|
||||
public string redirect;
|
||||
}
|
||||
public class SecondBotHttpServer
|
||||
{
|
||||
protected HttpListener listener;
|
||||
protected SecondBot Bot;
|
||||
public SecondBot GetBot { get { return Bot; } }
|
||||
protected JsonConfig Config;
|
||||
public JsonConfig GetConfig { get { return Config; } }
|
||||
protected HTTPCommandsInterface post_controler;
|
||||
|
||||
protected string url = "";
|
||||
public bool ShutdownHTTP { get; set; }
|
||||
public bool HTTPCnCmode { get; set; }
|
||||
|
||||
public string GetStatus()
|
||||
{
|
||||
string reply = "";
|
||||
if(Bot == null)
|
||||
{
|
||||
reply += "- No bot";
|
||||
}
|
||||
else
|
||||
{
|
||||
Bot.LastStatusMessage = Bot.GetStatus();
|
||||
reply += Bot.LastStatusMessage;
|
||||
}
|
||||
if(listener == null)
|
||||
{
|
||||
reply += " - No HTTP listener";
|
||||
}
|
||||
return reply;
|
||||
}
|
||||
public void NewBot()
|
||||
{
|
||||
Bot = new SecondBot();
|
||||
}
|
||||
public void KillBot()
|
||||
{
|
||||
Bot.GetClient.Network.Logout();
|
||||
Bot.GetClient.Network.Shutdown(OpenMetaverse.NetworkManager.DisconnectType.ClientInitiated);
|
||||
Bot = null;
|
||||
}
|
||||
public void StartHttpServer(JsonConfig setConfig)
|
||||
{
|
||||
StartHttpServer(setConfig, null);
|
||||
}
|
||||
public void StartHttpServer(SecondBot LinkBot, JsonConfig setConfig)
|
||||
{
|
||||
StartHttpServer(setConfig, LinkBot);
|
||||
}
|
||||
protected void StartHttpServer(JsonConfig setConfig, SecondBot LinkBot)
|
||||
{
|
||||
Config = setConfig;
|
||||
Bot = LinkBot;
|
||||
int port = Math.Abs(Config.Http_Port);
|
||||
url = Config.Http_Host;
|
||||
if (Config.Http_Host == "docker")
|
||||
{
|
||||
url = "http://*:" + port.ToString() + "/";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (url.StartsWith("http://") == false)
|
||||
{
|
||||
url = "http://localhost:" + port.ToString() + "";
|
||||
}
|
||||
if (url.EndsWith("/") == false)
|
||||
{
|
||||
url = "" + url + "/";
|
||||
}
|
||||
}
|
||||
OpenHttpService();
|
||||
}
|
||||
|
||||
protected async Task HandleIncomingConnections()
|
||||
{
|
||||
Webui myUI = new Webui(Config,Bot);
|
||||
while (Bot.KillMe == false)
|
||||
{
|
||||
HttpListenerContext ctx = await listener.GetContextAsync().ConfigureAwait(true);
|
||||
HttpListenerRequest req = ctx.Request;
|
||||
HttpListenerResponse resp = ctx.Response;
|
||||
resp.Cookies = req.Cookies;
|
||||
string test = req.Url.AbsoluteUri;
|
||||
if (helpers.notempty(Config.Http_PublicUrl) == true)
|
||||
{
|
||||
// normal mapped
|
||||
test = test.Replace(Config.Http_PublicUrl, "");
|
||||
|
||||
//rproxy weirdness
|
||||
test = test.Replace(Config.Http_PublicUrl, "");
|
||||
Config.Http_PublicUrl = Config.Http_PublicUrl.Replace("http://", "");
|
||||
Config.Http_PublicUrl = Config.Http_PublicUrl.Replace("https://", "");
|
||||
test = test.Replace(Config.Http_PublicUrl, "");
|
||||
test = test.Replace(":" + Config.Http_Port.ToString(), "");
|
||||
}
|
||||
Json_http_reply reply = new Json_http_reply();
|
||||
List<string> http_args = test.Split('/', StringSplitOptions.RemoveEmptyEntries).ToList();
|
||||
byte[] data;
|
||||
resp.StatusCode = 200;
|
||||
resp.ContentType = "text/html";
|
||||
if (req.HttpMethod == "GET")
|
||||
{
|
||||
KeyValuePair<string, byte[]> replydata = myUI.Get_Process(resp, http_args);
|
||||
if (replydata.Value != null)
|
||||
{
|
||||
resp.ContentType = replydata.Key;
|
||||
data = replydata.Value;
|
||||
resp.ContentLength64 = data.LongLength;
|
||||
await resp.OutputStream.WriteAsync(data, 0, data.Length).ConfigureAwait(true);
|
||||
resp.Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
data = Encoding.UTF8.GetBytes("error with service");
|
||||
resp.ContentLength64 = data.LongLength;
|
||||
resp.ContentType = "text/html";
|
||||
await resp.OutputStream.WriteAsync(data, 0, data.Length).ConfigureAwait(true);
|
||||
resp.Close();
|
||||
}
|
||||
}
|
||||
else if (req.HttpMethod == "POST")
|
||||
{
|
||||
string text;
|
||||
using (var reader = new StreamReader(req.InputStream, req.ContentEncoding))
|
||||
{
|
||||
text = reader.ReadToEnd();
|
||||
}
|
||||
//"command = logout & args = &passkey = Kp83VZaEhcy"
|
||||
string[] pairs = text.Split('&', StringSplitOptions.RemoveEmptyEntries);
|
||||
Dictionary<string, string> post_args = new Dictionary<string, string>();
|
||||
foreach (string B in pairs)
|
||||
{
|
||||
string[] bits = B.Split('=', StringSplitOptions.RemoveEmptyEntries);
|
||||
if (bits.Length == 2)
|
||||
{
|
||||
post_args.Add(bits[0].Trim(), HttpUtility.UrlDecode(bits[1].Trim()));
|
||||
}
|
||||
}
|
||||
resp.StatusCode = 200;
|
||||
if (myUI.Post_process(reply, resp, http_args, post_args) == true)
|
||||
{
|
||||
bool accept_request = false;
|
||||
string why_request_failed = "Bad signing request";
|
||||
|
||||
string command = "";
|
||||
string arg = "";
|
||||
string signing_code;
|
||||
|
||||
if (post_args.ContainsKey("command") == true)
|
||||
{
|
||||
command = post_args["command"];
|
||||
if (post_args.ContainsKey("args") == true)
|
||||
{
|
||||
arg = post_args["args"];
|
||||
}
|
||||
if (post_args.ContainsKey("code") == true)
|
||||
{
|
||||
signing_code = post_args["code"];
|
||||
string raw = "" + command + "" + arg + "" + Config.Security_SignedCommandkey + "";
|
||||
string hashcheck = helpers.GetSHA1(raw);
|
||||
if (hashcheck == signing_code)
|
||||
{
|
||||
accept_request = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
why_request_failed = "Post missing arg value";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
why_request_failed = "Post missing command value";
|
||||
}
|
||||
reply.message = why_request_failed;
|
||||
reply.status = accept_request;
|
||||
if (accept_request == true)
|
||||
{
|
||||
reply.message = String.Join("###", post_controler.Call(command, arg));
|
||||
}
|
||||
}
|
||||
if (reply.redirect != "")
|
||||
{
|
||||
if (reply.redirect != null)
|
||||
{
|
||||
if (reply.redirect == "/")
|
||||
{
|
||||
reply.redirect = Config.Http_PublicUrl;
|
||||
}
|
||||
else
|
||||
{
|
||||
reply.redirect = Config.Http_PublicUrl + reply.redirect;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
data = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(reply));
|
||||
resp.ContentLength64 = data.LongLength;
|
||||
await resp.OutputStream.WriteAsync(data, 0, data.Length).ConfigureAwait(true);
|
||||
resp.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
protected void OpenHttpService()
|
||||
{
|
||||
post_controler = new HTTPCommandsInterface(Bot,this);
|
||||
Thread t = new Thread(delegate (object _)
|
||||
{
|
||||
bool ok = true;
|
||||
try
|
||||
{
|
||||
listener = new HttpListener();
|
||||
listener.Prefixes.Add(url);
|
||||
listener.Start();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ok = false;
|
||||
LogFormater.Crit("Unable to setup http service: "+e.Message+"");
|
||||
LogFormater.Crit("if running on windows please check: https://docs.microsoft.com/en-us/dotnet/framework/wcf/feature-details/configuring-http-and-https?redirectedfrom=MSDN");
|
||||
}
|
||||
if (ok == true)
|
||||
{
|
||||
bool clean_exit = false;
|
||||
LogFormater.Status("Listening for connections on " + url + "");
|
||||
while (clean_exit == false)
|
||||
{
|
||||
try
|
||||
{
|
||||
LogFormater.Status("HTTP ready");
|
||||
Task listenTask = HandleIncomingConnections();
|
||||
listenTask.GetAwaiter().GetResult();
|
||||
}
|
||||
catch
|
||||
{
|
||||
LogFormater.Status("HTTP died");
|
||||
//Status("Http interface killed itself: " + e.ToString() + "");
|
||||
}
|
||||
}
|
||||
}
|
||||
listener.Close();
|
||||
})
|
||||
{
|
||||
Name = "http thead",
|
||||
IsBackground = true
|
||||
};
|
||||
t.Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BetterSecondBot.HttpServer.Control.Actions
|
||||
{
|
||||
public class Logout : HTTP_commands
|
||||
{
|
||||
public override string Helpfile { get { return "Makes the bot logout<br/> Returns OK,I dont want to go if accepted"; } }
|
||||
|
||||
public override string[] CallFunction(string[] args)
|
||||
{
|
||||
if (bot != null)
|
||||
{
|
||||
if (httpserver.HTTPCnCmode == true)
|
||||
{
|
||||
httpserver.KillBot();
|
||||
return new [] { "OK", "Bot Shutting down" };
|
||||
}
|
||||
else
|
||||
{
|
||||
bot.KillMePlease();
|
||||
return new [] { "OK", "I dont want to go" };
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return Failed("No bot logged in");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
using OpenMetaverse;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BetterSecondBot.HttpServer.Control.Interface
|
||||
{
|
||||
public class CoreCMD : HTTP_commands
|
||||
{
|
||||
public override string[] ArgTypes { get { return new [] { "Text","Text" }; } }
|
||||
public override string[] ArgHints { get { return new [] { "Command","Args (~#~ split)" }; } }
|
||||
public override string Helpfile { get { return "Calls the Commands interface via HTTP (returns OK if not rejected)"; } }
|
||||
|
||||
public override int Min_Required_args { get { return 2; } }
|
||||
|
||||
public override string[] CallFunction(string[] args)
|
||||
{
|
||||
if (bot != null)
|
||||
{
|
||||
if (ArgsCheck(args) == true)
|
||||
{
|
||||
if (bot.GetCommandsInterface.Call(args[0], args[1], UUID.Zero) == true)
|
||||
{
|
||||
return new [] { "OK" };
|
||||
}
|
||||
else
|
||||
{
|
||||
return Failed("Something went wrong with the call");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return Failed("Incorrect number of args");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return Failed("No bot");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
using OpenMetaverse;
|
||||
using BSB;
|
||||
using BSB.bottypes;
|
||||
using BetterSecondBotShared.API;
|
||||
using BetterSecondBotShared.Json;
|
||||
using BetterSecondBotShared.logs;
|
||||
|
||||
namespace BetterSecondBot.HttpServer
|
||||
{
|
||||
public class HTTPCommandsInterface : API_supported_interface
|
||||
{
|
||||
protected SecondBot bot;
|
||||
protected SecondBotHttpServer httpserver;
|
||||
|
||||
public HTTPCommandsInterface(SecondBot linktobot, SecondBotHttpServer sethttpserver)
|
||||
{
|
||||
bot = linktobot;
|
||||
httpserver = sethttpserver;
|
||||
API_type = typeof(HTTP_commands);
|
||||
LoadCommandsList();
|
||||
}
|
||||
|
||||
public string[] Call(string command, string arg)
|
||||
{
|
||||
command = command.ToLowerInvariant();
|
||||
HTTP_commands cmd = (HTTP_commands)GetCommand(command);
|
||||
if (cmd != null)
|
||||
{
|
||||
cmd.Setup(bot,httpserver);
|
||||
return cmd.CallFunction(arg.Split(new[] { "|||" }, StringSplitOptions.None));
|
||||
}
|
||||
else
|
||||
{
|
||||
LogFormater.Debug("" + command + " [Failed]: I have no fucking idea what you are talking about");
|
||||
return new[] { "Failed", "I have no fucking idea what you are talking about" };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class HTTP_commands : API_interface
|
||||
{
|
||||
protected SecondBot bot;
|
||||
protected SecondBotHttpServer httpserver;
|
||||
public void Setup(SecondBot setBot,SecondBotHttpServer Sethttpserver)
|
||||
{
|
||||
bot = setBot;
|
||||
httpserver = Sethttpserver;
|
||||
}
|
||||
public virtual string[] CallFunction(string[] args)
|
||||
{
|
||||
if (ArgsCheck(args) == true)
|
||||
{
|
||||
return new[] { "OK", "No override" };
|
||||
}
|
||||
return Failed("Incorrect number of args");
|
||||
}
|
||||
protected bool ArgsCheck(string[] args)
|
||||
{
|
||||
if (args.Length >= Min_Required_args)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
protected string[] Failed(string why_failed)
|
||||
{
|
||||
LogFormater.Warn("HTTP: " + CommandName + " [Failed]: " + why_failed);
|
||||
return new [] {"Failed",why_failed};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
164
CLI/HttpService/HttpAsService.cs
Normal file
164
CLI/HttpService/HttpAsService.cs
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
using BetterSecondBotShared.Json;
|
||||
using BSB.bottypes;
|
||||
using System;
|
||||
using EmbedIO;
|
||||
using EmbedIO.Actions;
|
||||
using EmbedIO.WebApi;
|
||||
using System.Threading;
|
||||
using Swan.Logging;
|
||||
using System.Collections.Generic;
|
||||
using EmbedIO.Security;
|
||||
using BetterSecondBotShared.Static;
|
||||
using System.Net;
|
||||
using EmbedIO.Routing;
|
||||
|
||||
namespace BetterSecondBot.HttpService
|
||||
{
|
||||
public class HttpAsService
|
||||
{
|
||||
protected JsonConfig Config;
|
||||
protected SecondBot Bot;
|
||||
protected TokenStorage Tokens;
|
||||
public HttpAsService(SecondBot MainBot, JsonConfig BotConfig)
|
||||
{
|
||||
Bot = MainBot;
|
||||
Config = BotConfig;
|
||||
|
||||
// Our web server is disposable
|
||||
Logger.UnregisterLogger<ConsoleLogger>();
|
||||
using (var server = CreateWebServer(Config.Http_Host))
|
||||
{
|
||||
// Once we've registered our modules and configured them, we call the RunAsync() method.
|
||||
server.RunAsync();
|
||||
while(Bot.KillMe == false)
|
||||
{
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private WebServer CreateWebServer(string url)
|
||||
{
|
||||
Tokens = new TokenStorage();
|
||||
var server = new WebServer(o => o
|
||||
.WithUrlPrefix(url)
|
||||
.WithMode(HttpListenerMode.EmbedIO))
|
||||
.WithIPBanning(o => o
|
||||
.WithMaxRequestsPerSecond(10)
|
||||
.WithRegexRules("HTTP exception 404")
|
||||
)
|
||||
.WithWebApi("/inventory", m => m
|
||||
.WithController(() => new SecondbotInventory(Bot, Tokens)))
|
||||
.WithWebApi("/core", m => m
|
||||
.WithController(() => new SecondbotCoreWebAPi(Config, Bot, Tokens)))
|
||||
.WithModule(new ActionModule("/", HttpVerbs.Any, ctx => ctx.SendDataAsync(new { Message = "Error" })));
|
||||
return server;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class TokenStorage
|
||||
{
|
||||
protected Dictionary<string, tokenInfo> tokens = new Dictionary<string, tokenInfo>();
|
||||
|
||||
public void PurgeExpired()
|
||||
{
|
||||
List<string> oldTokens = new List<string>();
|
||||
long now = helpers.UnixTimeNow();
|
||||
foreach (KeyValuePair<string, tokenInfo> entry in tokens)
|
||||
{
|
||||
if(entry.Value.Expires <= now)
|
||||
{
|
||||
oldTokens.Add(entry.Key);
|
||||
}
|
||||
}
|
||||
foreach(string A in oldTokens)
|
||||
{
|
||||
tokens.Remove(A);
|
||||
}
|
||||
}
|
||||
|
||||
public string Expire(string token)
|
||||
{
|
||||
if (tokens.ContainsKey(token) == true)
|
||||
{
|
||||
if(tokens.Remove(token) == true)
|
||||
{
|
||||
return "ok";
|
||||
}
|
||||
}
|
||||
return "Failed to remove token";
|
||||
}
|
||||
|
||||
public bool Allow(string code, IPAddress IPaddress)
|
||||
{
|
||||
if(tokens.ContainsKey(code) == true)
|
||||
{
|
||||
tokenInfo info = tokens[code];
|
||||
if (info.Expires <= helpers.UnixTimeNow())
|
||||
{
|
||||
PurgeExpired();
|
||||
return false;
|
||||
}
|
||||
if (info.IP == IPaddress)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public string CreateToken(IPAddress IP)
|
||||
{
|
||||
PurgeExpired();
|
||||
bool used = true;
|
||||
string last = "";
|
||||
while(used == true)
|
||||
{
|
||||
last = helpers.GetSHA1(last + helpers.UnixTimeNow() + new Random().Next(13256).ToString()).Substring(0, 10);
|
||||
used = tokens.ContainsKey(last);
|
||||
if (used == false)
|
||||
{
|
||||
tokenInfo Info = new tokenInfo();
|
||||
Info.IP = IP;
|
||||
Info.Expires = helpers.UnixTimeNow() + (60 * 10);
|
||||
}
|
||||
}
|
||||
return last;
|
||||
}
|
||||
}
|
||||
|
||||
public class WebApiControllerWithTokens : WebApiController
|
||||
{
|
||||
protected TokenStorage tokens;
|
||||
protected SecondBot bot;
|
||||
|
||||
public WebApiControllerWithTokens(SecondBot mainbot, TokenStorage setuptokens)
|
||||
{
|
||||
bot = mainbot;
|
||||
tokens = setuptokens;
|
||||
}
|
||||
|
||||
/*
|
||||
* not yet supported but will repace the current way soon(TM)
|
||||
[Route(HttpVerbs.Post, "/command/{token}")]
|
||||
public string CommandLib(string token, [JsonData] CommandLibCall request)
|
||||
{
|
||||
return "ok";
|
||||
}
|
||||
*/
|
||||
|
||||
public IPAddress getClientIP()
|
||||
{
|
||||
return HttpContext.Request.RemoteEndPoint.Address;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class tokenInfo
|
||||
{
|
||||
public long Expires = 0;
|
||||
public IPAddress IP = null;
|
||||
}
|
||||
|
||||
}
|
||||
44
CLI/HttpService/Inventory.cs
Normal file
44
CLI/HttpService/Inventory.cs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
using BSB.bottypes;
|
||||
using BSB.Static;
|
||||
using EmbedIO;
|
||||
using EmbedIO.Routing;
|
||||
using EmbedIO.WebApi;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace BetterSecondBot.HttpService
|
||||
{
|
||||
public class SecondbotInventory : WebApiControllerWithTokens
|
||||
{
|
||||
public SecondbotInventory(SecondBot mainbot, TokenStorage setuptokens) : base(mainbot, setuptokens) { }
|
||||
|
||||
[Route(HttpVerbs.Get, "/folders/{token}")]
|
||||
public string folders(string token)
|
||||
{
|
||||
if(tokens.Allow(token, getClientIP()) == true)
|
||||
{
|
||||
return HelperInventory.MapFolderJson(bot);
|
||||
}
|
||||
return "Token not accepted";
|
||||
}
|
||||
|
||||
[Route(HttpVerbs.Get, "/contents/{folderUUID}/{token}")]
|
||||
public string Contents(string folderUUID, string token)
|
||||
{
|
||||
if (tokens.Allow(token, getClientIP()) == true)
|
||||
{
|
||||
if (UUID.TryParse(folderUUID, out UUID folder) == true)
|
||||
{
|
||||
return HelperInventory.MapFolderInventoryJson(bot, folder);
|
||||
}
|
||||
return "Invaild folder UUID";
|
||||
}
|
||||
return "Token not accepted";
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
107
CLI/HttpService/Secondbot.cs
Normal file
107
CLI/HttpService/Secondbot.cs
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
using BetterSecondBotShared.Json;
|
||||
using BetterSecondBotShared.Static;
|
||||
using BSB.bottypes;
|
||||
using OpenMetaverse;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using EmbedIO;
|
||||
using EmbedIO.Routing;
|
||||
using EmbedIO.WebApi;
|
||||
|
||||
|
||||
namespace BetterSecondBot.HttpService
|
||||
{
|
||||
public class SecondbotCoreWebAPi : WebApiControllerWithTokens
|
||||
{
|
||||
|
||||
protected JsonConfig config;
|
||||
public SecondbotCoreWebAPi(JsonConfig myconfig, SecondBot mainbot, TokenStorage setuptokens) : base(mainbot, setuptokens)
|
||||
{
|
||||
config = myconfig;
|
||||
}
|
||||
|
||||
[Route(HttpVerbs.Post, "/gettoken")]
|
||||
public string GetToken([FormField] string authcode)
|
||||
{
|
||||
long now = helpers.UnixTimeNow() + 120;
|
||||
int loop = 0;
|
||||
bool accepted = false;
|
||||
while(loop <= 30)
|
||||
{
|
||||
string hashA = helpers.GetSHA1((now + loop) + "" + config.Security_WebUIKey).Substring(0, 10);
|
||||
string hashB = helpers.GetSHA1((now - loop) + "" + config.Security_WebUIKey).Substring(0, 10);
|
||||
if ((authcode == hashA) || (authcode == hashB))
|
||||
{
|
||||
accepted = true;
|
||||
break;
|
||||
}
|
||||
loop++;
|
||||
}
|
||||
if(accepted == true)
|
||||
{
|
||||
return tokens.CreateToken(HttpContext.Request.RemoteEndPoint.Address);
|
||||
}
|
||||
return "Authcode not accepted";
|
||||
}
|
||||
|
||||
[Route(HttpVerbs.Get, "/logout/{token}")]
|
||||
public string Logout(string token)
|
||||
{
|
||||
if (tokens.Allow(token, getClientIP()) == true)
|
||||
{
|
||||
return tokens.Expire(token);
|
||||
}
|
||||
return "Token not accepted";
|
||||
}
|
||||
|
||||
[Route(HttpVerbs.Get, "/version/{token}")]
|
||||
public string Version(string token)
|
||||
{
|
||||
if (tokens.Allow(token, getClientIP()) == true)
|
||||
{
|
||||
return bot.MyVersion;
|
||||
}
|
||||
return "Token not accepted";
|
||||
}
|
||||
|
||||
[Route(HttpVerbs.Get, "/name/{token}")]
|
||||
public string Name(string token)
|
||||
{
|
||||
if (tokens.Allow(token, getClientIP()) == true)
|
||||
{
|
||||
return bot.GetClient.Self.FirstName + " " + bot.GetClient.Self.LastName;
|
||||
}
|
||||
return "Token not accepted";
|
||||
}
|
||||
|
||||
[Route(HttpVerbs.Post, "/command/{token}")]
|
||||
public async Task<string> command(string token)
|
||||
{
|
||||
if (tokens.Allow(token, getClientIP()) == true)
|
||||
{
|
||||
CommandLibCall data = await HttpContext.GetRequestDataAsync<CommandLibCall>();
|
||||
string compressedArgs = string.Join("~#~", data.Args);
|
||||
string raw = data.Command + compressedArgs;
|
||||
string cooked = helpers.GetSHA1(raw + config.Security_SignedCommandkey);
|
||||
if (cooked == data.AuthCode)
|
||||
{
|
||||
bool status = bot.GetCommandsInterface.Call(data.Command, compressedArgs, UUID.Zero, "~#~");
|
||||
if (status == true)
|
||||
{
|
||||
return "accepted";
|
||||
}
|
||||
}
|
||||
}
|
||||
return "Token not accepted";
|
||||
}
|
||||
}
|
||||
|
||||
public class CommandLibCall
|
||||
{
|
||||
public string Command { get; set; }
|
||||
public string[] Args { get; set; }
|
||||
public string AuthCode { get; set; }
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
.logo
|
||||
{
|
||||
width: 100%;
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 4.5 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 28 KiB |
|
|
@ -1,201 +0,0 @@
|
|||
$( document ).ready(function() {
|
||||
$(".ajaxq").submit(function(e) {
|
||||
e.preventDefault();
|
||||
var form = $(this);
|
||||
var url = form.attr('action');
|
||||
var method = form.attr('method');
|
||||
$.ajax({
|
||||
type: method,
|
||||
url: url,
|
||||
data: form.serialize(),
|
||||
success: function(data)
|
||||
{
|
||||
try
|
||||
{
|
||||
jsondata = JSON.parse(data);
|
||||
var redirectdelay = 1500;
|
||||
if(jsondata.hasOwnProperty('status'))
|
||||
{
|
||||
if(jsondata.hasOwnProperty('message'))
|
||||
{
|
||||
if(jsondata.status == false)
|
||||
{
|
||||
redirectdelay = 3500;
|
||||
if(jsondata.message != "") alert_warning(jsondata.message);
|
||||
}
|
||||
}
|
||||
if(jsondata.hasOwnProperty('redirect'))
|
||||
{
|
||||
if(jsondata.redirect != "")
|
||||
{
|
||||
if(jsondata.redirect != null)
|
||||
{
|
||||
setTimeout(function(){ $(location).attr('href', jsondata.redirect) }, redirectdelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
alert_error("Reply from server is not vaild please reload the page and try again.");
|
||||
}
|
||||
}
|
||||
catch(e)
|
||||
{
|
||||
alert_warning("Unable to process reply, please reload the page and try again!");
|
||||
}
|
||||
},
|
||||
error: function (data)
|
||||
{
|
||||
alert_error(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
$(".ajax").submit(function(e) {
|
||||
e.preventDefault();
|
||||
var form = $(this);
|
||||
var url = form.attr('action');
|
||||
var method = form.attr('method');
|
||||
$.ajax({
|
||||
type: method,
|
||||
url: url,
|
||||
data: form.serialize(),
|
||||
success: function(data)
|
||||
{
|
||||
try
|
||||
{
|
||||
jsondata = JSON.parse(data);
|
||||
var redirectdelay = 1500;
|
||||
if(jsondata.hasOwnProperty('status'))
|
||||
{
|
||||
if(jsondata.hasOwnProperty('message'))
|
||||
{
|
||||
if(jsondata.status == true)
|
||||
{
|
||||
if(jsondata.message != "") alert_success(jsondata.message);
|
||||
}
|
||||
else
|
||||
{
|
||||
redirectdelay = 3500;
|
||||
if(jsondata.message != "") alert_warning(jsondata.message);
|
||||
}
|
||||
}
|
||||
if(jsondata.hasOwnProperty('redirect'))
|
||||
{
|
||||
if(jsondata.redirect != "")
|
||||
{
|
||||
if(jsondata.redirect != null)
|
||||
{
|
||||
setTimeout(function(){ $(location).attr('href', jsondata.redirect) }, redirectdelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
alert_error("Reply from server is not vaild please reload the page and try again.");
|
||||
}
|
||||
}
|
||||
catch(e)
|
||||
{
|
||||
alert_warning("Unable to process reply, please reload the page and try again!");
|
||||
}
|
||||
},
|
||||
error: function (data)
|
||||
{
|
||||
alert_error(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function alert_success(smsg)
|
||||
{
|
||||
alert(smsg,"success");
|
||||
}
|
||||
function alert_error(smsg)
|
||||
{
|
||||
alert(smsg,"error");
|
||||
}
|
||||
function alert_warning(smsg)
|
||||
{
|
||||
alert(smsg,"warning");
|
||||
}
|
||||
function alert(smsg,alerttype)
|
||||
{
|
||||
$.notify({
|
||||
// options
|
||||
message: smsg
|
||||
},{
|
||||
// settings
|
||||
type: alerttype
|
||||
});
|
||||
}
|
||||
|
||||
function update_localchat(urlbase)
|
||||
{
|
||||
$.ajax({
|
||||
type: "post",
|
||||
url: ""+urlbase+"ajax/localchat/get",
|
||||
success: function(data)
|
||||
{
|
||||
try
|
||||
{
|
||||
jsondata = JSON.parse(data);
|
||||
var redirectdelay = 1500;
|
||||
if(jsondata.hasOwnProperty('status'))
|
||||
{
|
||||
if(jsondata.hasOwnProperty('message'))
|
||||
{
|
||||
if(jsondata.status == true)
|
||||
{
|
||||
$('#localchat').prop('readonly',false);
|
||||
var text = "";
|
||||
var addon = "";
|
||||
$.each( JSON.parse(jsondata.message), function( key, value ) {
|
||||
text = ""+value+""+addon+""+text+"";
|
||||
addon = "\n";
|
||||
});
|
||||
$('#localchat').val(text);
|
||||
$('#localchat').prop('readonly',true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(e)
|
||||
{
|
||||
alert_warning("Unable to process reply, please reload the page and try again!");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
function update_groupchat(urlbase,groupuuid)
|
||||
{
|
||||
$.ajax({
|
||||
type: "post",
|
||||
url: ""+urlbase+"ajax/groupchat/get",
|
||||
data: "groupuuid="+groupuuid+"",
|
||||
success: function(data)
|
||||
{
|
||||
try
|
||||
{
|
||||
jsondata = JSON.parse(data);
|
||||
var redirectdelay = 1500;
|
||||
if(jsondata.hasOwnProperty('status'))
|
||||
{
|
||||
if(jsondata.hasOwnProperty('message'))
|
||||
{
|
||||
if(jsondata.status == true)
|
||||
{
|
||||
$("localchat").val(jsondata.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(e)
|
||||
{
|
||||
alert_warning("Unable to process reply, please reload the page and try again!");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha256-L/W5Wfqfa0sdBNIKN9cG6QA5F2qx4qICmU2VgLruv9Y=" crossorigin="anonymous" />
|
||||
<link href="https://stackpath.bootstrapcdn.com/bootswatch/4.4.1/cyborg/bootstrap.min.css" rel="stylesheet" integrity="sha384-l7xaoY0cJM4h9xh1RfazbgJVUZvdtyLWPueWNtLAphf/UbBgOVzqbOTogxPwYLHM" crossorigin="anonymous"/>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.13.0/css/all.min.css" integrity="sha256-h20CPZ0QyXlBuAw7A+KluUYx/3pK+c7lYEpqLTlxjYQ=" crossorigin="anonymous" />
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
<script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.1/js/bootstrap.bundle.js" integrity="sha256-Z1t+wxZ7Eh5T1sK6aePWMhEQOghR4jZVLwjwrUZAReE=" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap-notify@3.1.3/bootstrap-notify.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.13.0/js/all.min.js" integrity="sha256-KzZiKy0DWYsnwMF+X1DvQngQ2/FxF7MF3Ff72XcpuPs=" crossorigin="anonymous"></script>
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>[[html_title]] - [[html_title_after]]</title>
|
||||
[[html_cs_top_cdn]]
|
||||
[[html_cs_top]]
|
||||
<link rel="icon" type="image/png" href="[[url_base]]theme/images/favicon.png">
|
||||
<link rel="stylesheet" type="text/css" href="[[url_base]]theme/css/site.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="row h-75 justify-content-center align-items-center">
|
||||
<div class="col-6">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-6">
|
||||
<a href="[[url_base]]"><img class="logo" src="[[url_base]]theme/images/logo.png" alt="logo"/></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
[[page_content]]
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
[[html_js_bottom_cdn]]
|
||||
[[html_js_bottom]]
|
||||
<script type="text/javascript" src="[[url_base]]theme/js/site.js"></script>
|
||||
<script type="text/javascript">
|
||||
$( document ).ready(function() {
|
||||
[[html_js_onready]]
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
<div class="card border border-success rounded">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Bot webUI auth required</h5>
|
||||
<form action="[[url_base]]ajax/login/start" method="post" class="ajax">
|
||||
<div class="row mt-1">
|
||||
<div class="col-8 offset-2">
|
||||
<input name="logincode" class="form-control" type="password" placeholder="Please enter your auth code"/></div>
|
||||
</div>
|
||||
<div class="row mt-4">
|
||||
<div class="col-12"><button class="btn btn-primary btn-block" type="submit">Login</button></div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>[[html_title]] - [[html_title_after]]</title>
|
||||
[[html_cs_top_cdn]]
|
||||
[[html_cs_top]]
|
||||
<link rel="icon" type="image/png" href="[[url_base]]theme/images/favicon.png">
|
||||
<link rel="stylesheet" type="text/css" href="[[url_base]]theme/css/site.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<div class="row mt-1 justify-content-between">
|
||||
<div class="col-1"><a href="[[url_base]]"><img class="logo" src="[[url_base]]theme/images/logo.png" alt="logo"/></a></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-2">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<ul class="nav nav-pills flex-column">
|
||||
[[html_menu]]
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row align-items-end">
|
||||
<div class="col-12">
|
||||
<br/><br/>
|
||||
<hr/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row align-items-end">
|
||||
<div class="col-5">
|
||||
<a href="[[url_base]]logout"><button type="button" class="btn btn-block btn-secondary btn-sml">Logout</button></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-10">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="row mt-1 justify-content-between">
|
||||
<div class="col-5">[[page_title]]</div>
|
||||
<div class="col-1 justify-content-end">[[page_actions]]</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
[[page_content]]
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
[[html_js_bottom_cdn]]
|
||||
[[html_js_bottom]]
|
||||
<script type="text/javascript" src="[[url_base]]theme/js/site.js"></script>
|
||||
<script type="text/javascript">
|
||||
$( document ).ready(function() {
|
||||
[[html_js_onready]]
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,514 +0,0 @@
|
|||
using BetterSecondBot.HttpServer;
|
||||
using BetterSecondBotShared.Json;
|
||||
using BetterSecondBotShared.Static;
|
||||
using BSB.bottypes;
|
||||
using Microsoft.VisualBasic.CompilerServices;
|
||||
using Newtonsoft.Json;
|
||||
using OpenMetaverse;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace BetterSecondBot.HttpWebUi
|
||||
{
|
||||
public class Webui
|
||||
{
|
||||
private JsonConfig siteconfig;
|
||||
private SecondBot bot;
|
||||
private Dictionary<string, long> vaildlogincookies = new Dictionary<string, long>();
|
||||
public Webui(JsonConfig setconfig, SecondBot setbot)
|
||||
{
|
||||
bot = setbot;
|
||||
siteconfig = setconfig;
|
||||
}
|
||||
protected KeyValuePair<bool, bool> Logincookievaild(HttpListenerResponse resp)
|
||||
{
|
||||
return Logincookievaild(resp, false);
|
||||
}
|
||||
protected KeyValuePair<bool, bool> Logincookievaild(HttpListenerResponse resp, bool force_logout)
|
||||
{
|
||||
foreach (Cookie C in resp.Cookies)
|
||||
{
|
||||
if (C.Name == "logincookie")
|
||||
{
|
||||
if (force_logout == true)
|
||||
{
|
||||
if (vaildlogincookies.ContainsKey(C.Value) == true)
|
||||
{
|
||||
vaildlogincookies.Remove(C.Value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (vaildlogincookies.ContainsKey(C.Value) == true)
|
||||
{
|
||||
if (vaildlogincookies[C.Value] > helpers.UnixTimeNow())
|
||||
{
|
||||
return new KeyValuePair<bool, bool>(true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
resp.Cookies.Remove(C);
|
||||
C.Expired = true;
|
||||
resp.Cookies.Add(C);
|
||||
return new KeyValuePair<bool, bool>(true, false);
|
||||
}
|
||||
}
|
||||
return new KeyValuePair<bool, bool>(false, false);
|
||||
}
|
||||
public bool Post_process(Json_http_reply reply, HttpListenerResponse resp, List<string> args, Dictionary<string, string> post_args)
|
||||
{
|
||||
bool processed = false;
|
||||
if (args != null)
|
||||
{
|
||||
if (args.Count >= 1)
|
||||
{
|
||||
if (args[0] == "ajax")
|
||||
{
|
||||
processed = true;
|
||||
// webui only cares about ajax post requests
|
||||
string mod = "none";
|
||||
string area = "none";
|
||||
if (args.Count >= 2)
|
||||
{
|
||||
mod = args[1];
|
||||
}
|
||||
if (args.Count >= 3)
|
||||
{
|
||||
area = args[2];
|
||||
}
|
||||
KeyValuePair<bool, bool> logincheck = Logincookievaild(resp);
|
||||
|
||||
if (logincheck.Key == false)
|
||||
{
|
||||
if (mod == "login")
|
||||
{
|
||||
if (post_args.ContainsKey("logincode") == true)
|
||||
{
|
||||
if (post_args["logincode"] == siteconfig.Security_WebUIKey)
|
||||
{
|
||||
reply.status = true;
|
||||
string newcookiecode = "";
|
||||
while ((newcookiecode == null) || (newcookiecode == "") || (vaildlogincookies.ContainsKey(newcookiecode) == true))
|
||||
{
|
||||
newcookiecode = helpers.GetSHA1(siteconfig.Security_WebUIKey + helpers.UnixTimeNow().ToString() + new Random().Next(12345).ToString());
|
||||
}
|
||||
vaildlogincookies.Add(newcookiecode, helpers.UnixTimeNow() + (5 * 60)); // auto logout after 5 mins
|
||||
Cookie logincookie = new Cookie("logincookie", newcookiecode)
|
||||
{
|
||||
Expires = DateTime.Now.AddDays(1),
|
||||
Path = "/"
|
||||
};
|
||||
resp.Cookies.Add(logincookie);
|
||||
Console.WriteLine("Cookie created: " + logincookie.Value + " <=> " + newcookiecode + "");
|
||||
reply.message = "ok";
|
||||
reply.redirect = "/";
|
||||
}
|
||||
else
|
||||
{
|
||||
reply.status = false;
|
||||
reply.message = "fail";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reply.status = false;
|
||||
reply.message = "Missing code";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reply.status = false;
|
||||
reply.message = "No access";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (logincheck.Value == true)
|
||||
{
|
||||
reply.status = true;
|
||||
// logged in
|
||||
if (mod == "logout")
|
||||
{
|
||||
Logincookievaild(resp, true);
|
||||
reply.message = "logged-out";
|
||||
reply.redirect = "/";
|
||||
reply.status = false;
|
||||
}
|
||||
else if(mod == "interface")
|
||||
{
|
||||
if (area == "get")
|
||||
{
|
||||
reply.message = JsonConvert.SerializeObject(bot.GetLastCommands(30));
|
||||
}
|
||||
else if(area == "send")
|
||||
{
|
||||
if (post_args.ContainsKey("message") == true)
|
||||
{
|
||||
string[] bits = post_args["message"].Split("|||");
|
||||
if(bits.Length == 2)
|
||||
{
|
||||
reply.status = bot.GetCommandsInterface.Call(bits[0], bits[1]);
|
||||
if(reply.status == true)
|
||||
{
|
||||
reply.message = "ok";
|
||||
}
|
||||
else
|
||||
{
|
||||
reply.message = "Failed";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reply.status = bot.GetCommandsInterface.Call(post_args["message"]);
|
||||
if (reply.status == true)
|
||||
{
|
||||
reply.message = "ok";
|
||||
}
|
||||
else
|
||||
{
|
||||
reply.message = "Failed";
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reply.message = "No message";
|
||||
reply.status = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reply.message = "Unknown area";
|
||||
reply.status = false;
|
||||
}
|
||||
}
|
||||
else if (mod == "localchat")
|
||||
{
|
||||
if (area == "get")
|
||||
{
|
||||
reply.message = JsonConvert.SerializeObject(bot.getLocalChatHistory());
|
||||
}
|
||||
else if (area == "send")
|
||||
{
|
||||
if (post_args.ContainsKey("message") == true)
|
||||
{
|
||||
bot.AddToLocalChat("{Via WebGUI}", post_args["message"]);
|
||||
bot.GetClient.Self.Chat("{Via WebGUI} "+ post_args["message"], 0,ChatType.Normal);
|
||||
reply.message = "ok";
|
||||
reply.status = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
reply.message = "No message";
|
||||
reply.status = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reply.message = "Unknown area";
|
||||
reply.status = false;
|
||||
}
|
||||
}
|
||||
else if (mod == "groupchat")
|
||||
{
|
||||
if (area == "list")
|
||||
{
|
||||
Dictionary<string, string> groupname_uuid = new Dictionary<string, string>();
|
||||
foreach (KeyValuePair<UUID, Group> entry in bot.MyGroups)
|
||||
{
|
||||
groupname_uuid.Add(entry.Key.ToString(), entry.Value.Name);
|
||||
}
|
||||
reply.message = JsonConvert.SerializeObject(groupname_uuid);
|
||||
}
|
||||
if (area == "get")
|
||||
{
|
||||
if (post_args.ContainsKey("groupuuid") == true)
|
||||
{
|
||||
if (UUID.TryParse(post_args["groupuuid"], out UUID groupuuid) == true)
|
||||
{
|
||||
reply.message = JsonConvert.SerializeObject(bot.GetGroupchat(groupuuid));
|
||||
}
|
||||
else
|
||||
{
|
||||
reply.message = "Unable to process group uuid";
|
||||
reply.status = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reply.message = "No groupuuid";
|
||||
reply.status = false;
|
||||
}
|
||||
}
|
||||
else if (area == "send")
|
||||
{
|
||||
if (post_args.ContainsKey("groupuuid") == true)
|
||||
{
|
||||
if (UUID.TryParse(post_args["groupuuid"], out UUID groupuuid) == true)
|
||||
{
|
||||
if (post_args.ContainsKey("message") == true)
|
||||
{
|
||||
bot.GetClient.Self.RequestJoinGroupChat(groupuuid);
|
||||
Thread.Sleep(100);
|
||||
bot.GetClient.Self.InstantMessageGroup(groupuuid, "{Via WebGUI} " + post_args["message"]);
|
||||
bot.AddToGroupchat(groupuuid, "{Via WebGUI}", post_args["message"]);
|
||||
reply.message = "ok";
|
||||
reply.status = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
reply.message = "No message";
|
||||
reply.status = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reply.message = "Unable to process group uuid";
|
||||
reply.status = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reply.message = "No groupuuid";
|
||||
reply.status = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reply.message = "Unknown area";
|
||||
reply.status = false;
|
||||
}
|
||||
}
|
||||
else if (mod == "im")
|
||||
{
|
||||
if (area == "list")
|
||||
{
|
||||
reply.message = JsonConvert.SerializeObject(bot.GetIMChatWindowKeyNames());
|
||||
}
|
||||
else if (area == "send")
|
||||
{
|
||||
if (post_args.ContainsKey("avataruuid") == true)
|
||||
{
|
||||
if (UUID.TryParse(post_args["avataruuid"], out UUID avataruuid) == true)
|
||||
{
|
||||
if (post_args.ContainsKey("message") == true)
|
||||
{
|
||||
bot.GetCommandsInterface.Call("im", "" + avataruuid.ToString() + "~#~{Via WebGUI} " + post_args["message"]);
|
||||
reply.message = "ok";
|
||||
reply.status = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
reply.message = "No message";
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reply.message = "Unable to process avatar uuid";
|
||||
reply.status = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reply.message = "No avataruuid";
|
||||
reply.status = false;
|
||||
}
|
||||
}
|
||||
else if (area == "get")
|
||||
{
|
||||
if (post_args.ContainsKey("avataruuid") == true)
|
||||
{
|
||||
if (UUID.TryParse(post_args["avataruuid"], out UUID avataruuid) == true)
|
||||
{
|
||||
reply.message = JsonConvert.SerializeObject(bot.GetIMChatWindow(avataruuid));
|
||||
}
|
||||
else
|
||||
{
|
||||
reply.message = "Unable to process avatar uuid";
|
||||
reply.status = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reply.message = "No avataruuid";
|
||||
reply.status = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reply.message = "Unknown area";
|
||||
reply.status = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reply.message = "No action";
|
||||
reply.status = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// login expired
|
||||
reply.message = "login-expired";
|
||||
reply.redirect = "/";
|
||||
Logincookievaild(resp, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return !processed;
|
||||
}
|
||||
public string Html_logged_in(string mod, string area)
|
||||
{
|
||||
string layout = helpers.ReadResourceFile(Assembly.GetExecutingAssembly(), "sidemenu.layout");
|
||||
Dictionary<string, string> swaps = new Dictionary<string, string>();
|
||||
|
||||
|
||||
string pagetitle = "Dashboard";
|
||||
string pagecontent = "";
|
||||
string pageactions = "";
|
||||
string js_onready = "";
|
||||
string[] menu_text = new string[] { "Localchat", "Groupchat", "IMs", "Interface" };
|
||||
string[] menu_links = new string[] { "localchat", "groupchat", "im", "interface" };
|
||||
string[] meni_icons = new string[] { "fas fa-comment", "fas fa-comments", "fas fa-users","fas fa-robot" };
|
||||
int loop = 0;
|
||||
string menu = "";
|
||||
while (loop < menu_text.Count())
|
||||
{
|
||||
string active = "";
|
||||
if (menu_links[loop] == mod) active = "active";
|
||||
menu = "" + menu + " <li class=\"nav-item\"><a href=\"[[url_base]]"+menu_links[loop]+"\" class=\"nav-link " + active+ "\"><i class=\"" + meni_icons[loop] + " text-success\"></i> " + menu_text[loop] + "</a></li>";
|
||||
loop++;
|
||||
}
|
||||
swaps.Add("html_menu", menu);
|
||||
if (mod == "none")
|
||||
{
|
||||
pagecontent = "<p>Please select a menu item on the left</p>";
|
||||
}
|
||||
else if(mod == "localchat")
|
||||
{
|
||||
pagetitle = "Localchat";
|
||||
pagecontent = "<textarea readonly cols=\"82\" rows=\"14\" id=\"localchat\" name=\"localchat\"> - Loading local chat please wait -</textarea><br/>" +
|
||||
"<form method=\"post\" class=\"ajaxq\" action=\"[[url_base]]ajax/localchat/send\">" +
|
||||
"<input type=\"text\" name=\"message\" id=\"message\" value=\"\" size=\"33\" class=\"form-control\" placeholder=\"Say in localchat\">" +
|
||||
"<button type\"submit\" class=\"btn btn-primary\">Send</button>"+
|
||||
"</form>";
|
||||
js_onready = "setInterval(function(){ update_localchat(\"" + siteconfig.Http_PublicUrl+"\"); },400);";
|
||||
|
||||
}
|
||||
else if (mod == "groupchat")
|
||||
{
|
||||
pagetitle = "Group chat";
|
||||
pagecontent = "";
|
||||
if (area == "none")
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
swaps.Add("html_title", pagetitle);
|
||||
swaps.Add("page_actions", pageactions);
|
||||
swaps.Add("page_content", pagecontent);
|
||||
swaps.Add("page_title", pagetitle);
|
||||
swaps.Add("html_cs_top", "");
|
||||
swaps.Add("html_js_bottom", "");
|
||||
swaps.Add("html_js_onready", js_onready);
|
||||
foreach (KeyValuePair<string,string> A in swaps)
|
||||
{
|
||||
layout = layout.Replace("[[" + A.Key + "]]", A.Value);
|
||||
}
|
||||
return layout;
|
||||
}
|
||||
public string Html_logged_out()
|
||||
{
|
||||
string layout = helpers.ReadResourceFile(Assembly.GetExecutingAssembly(), "full.layout");
|
||||
layout = layout.Replace("[[page_content]]", helpers.ReadResourceFile(Assembly.GetExecutingAssembly(), "login.block"));
|
||||
layout = layout.Replace("[[html_js_onready]]", "");
|
||||
layout = layout.Replace("[[html_cs_top]]", "");
|
||||
layout = layout.Replace("[[html_js_bottom]]", "");
|
||||
layout = layout.Replace("[[html_title]]", siteconfig.Basic_BotUserName);
|
||||
return layout;
|
||||
}
|
||||
public KeyValuePair<string, byte[]> Get_Process(HttpListenerResponse resp, List<string> args)
|
||||
{
|
||||
|
||||
// get content
|
||||
if (args != null)
|
||||
{
|
||||
if (args.Count >= 1)
|
||||
{
|
||||
if (args[0] == "theme")
|
||||
{
|
||||
if (args.Count >= 2)
|
||||
{
|
||||
if (args[1] == "images")
|
||||
{
|
||||
return new KeyValuePair<string, byte[]>("image/png", helpers.ReadResourceFileBinary(Assembly.GetExecutingAssembly(), args[2]));
|
||||
}
|
||||
else if (args[1] == "css")
|
||||
{
|
||||
return new KeyValuePair<string, byte[]>("text/css", Encoding.UTF8.GetBytes(helpers.ReadResourceFile(Assembly.GetExecutingAssembly(), args[2])));
|
||||
}
|
||||
else if (args[1] == "js")
|
||||
{
|
||||
return new KeyValuePair<string, byte[]>("text/javascript", Encoding.UTF8.GetBytes(helpers.ReadResourceFile(Assembly.GetExecutingAssembly(), args[2])));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// get display
|
||||
KeyValuePair<bool, bool> logincheck = Logincookievaild(resp);
|
||||
if (logincheck.Key == true)
|
||||
{
|
||||
if (logincheck.Value == false)
|
||||
{
|
||||
Logincookievaild(resp, true);
|
||||
}
|
||||
}
|
||||
string reply_with;
|
||||
if (logincheck.Value == false)
|
||||
{
|
||||
reply_with = Html_logged_out();
|
||||
}
|
||||
else
|
||||
{
|
||||
string mod = "none";
|
||||
string area = "none";
|
||||
if (args.Count >= 1)
|
||||
{
|
||||
mod = args[0];
|
||||
}
|
||||
if (args.Count >= 2)
|
||||
{
|
||||
area = args[1];
|
||||
}
|
||||
|
||||
if (mod == "logout")
|
||||
{
|
||||
Logincookievaild(resp, true);
|
||||
reply_with = Html_logged_out();
|
||||
}
|
||||
else
|
||||
{
|
||||
// logged in
|
||||
reply_with = Html_logged_in(mod, area);
|
||||
}
|
||||
}
|
||||
reply_with = reply_with.Replace("[[url_base]]", siteconfig.Http_PublicUrl);
|
||||
reply_with = reply_with.Replace("[[html_title_after]]", "Secondbot web UI");
|
||||
reply_with = reply_with.Replace("[[html_cs_top_cdn]]", helpers.ReadResourceFile(Assembly.GetExecutingAssembly(), "cdn.css.layout"));
|
||||
reply_with = reply_with.Replace("[[html_js_bottom_cdn]]", helpers.ReadResourceFile(Assembly.GetExecutingAssembly(), "cdn.js.layout"));
|
||||
return new KeyValuePair<string, byte[]>("text/html", Encoding.UTF8.GetBytes(reply_with));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +1,28 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Secondbot Wiki [[AREA]]</title>
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.3/css/bootstrap.css" rel="stylesheet" crossorigin="anonymous">
|
||||
<link href="https://cdn.datatables.net/1.10.20/css/dataTables.bootstrap4.min.css" rel="stylesheet" crossorigin="anonymous">
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-md navbar-dark bg-dark mb-3">
|
||||
<div class="container-fluid">
|
||||
<a href="[[RETURNROOT]]index.html" class="navbar-brand mr-3">SecondBot Wiki</a>
|
||||
<button type="button" class="navbar-toggler" data-toggle="collapse" data-target="#navbarCollapse">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarCollapse">
|
||||
<div class="navbar-nav">
|
||||
<a href="[[SUBFOLDER]]Core.html" class="nav-item nav-link [[CoreActive]]">Main commands [Core]</a>
|
||||
<a href="[[SUBFOLDER]]JSON.html" class="nav-item nav-link [[JSONActive]]">JSON [Bot Config file]</a>
|
||||
<a href="[[SUBFOLDER]]RLVapi.html" class="nav-item nav-link [[RLVapiActive]]">Restrained Love API [RLV]</a>
|
||||
<a href="[[SUBFOLDER]]HTTPpost.html" class="nav-item nav-link [[HTTPpostActive]]">Post [HTTP]</a>
|
||||
</div>
|
||||
<div class="navbar-nav ml-auto">
|
||||
<a href="https://bitbucket.org/streamadmin/secondbot/src/master/" target="_BLANK" class="nav-item nav-link">Get Source</a>
|
||||
<a href="secondlife:///app/group/cbda0f35-1c0b-2a45-e693-62a63a8f9d88/about" target="_BLANK" class="nav-item nav-link">SL group</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<html>
|
||||
<head>
|
||||
<title>Secondbot Wiki [[AREA]]</title>
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.3/css/bootstrap.css" rel="stylesheet" crossorigin="anonymous">
|
||||
<link href="https://cdn.datatables.net/1.10.20/css/dataTables.bootstrap4.min.css" rel="stylesheet" crossorigin="anonymous">
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-md navbar-dark bg-dark mb-3">
|
||||
<div class="container-fluid">
|
||||
<a href="[[RETURNROOT]]index.html" class="navbar-brand mr-3">SecondBot Wiki</a>
|
||||
<button type="button" class="navbar-toggler" data-toggle="collapse" data-target="#navbarCollapse">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarCollapse">
|
||||
<div class="navbar-nav">
|
||||
<a href="[[SUBFOLDER]]Core.html" class="nav-item nav-link [[CoreActive]]">Main commands [Core]</a>
|
||||
<a href="[[SUBFOLDER]]JSON.html" class="nav-item nav-link [[JSONActive]]">JSON [Bot Config file]</a>
|
||||
<a href="[[SUBFOLDER]]RLVapi.html" class="nav-item nav-link [[RLVapiActive]]">Restrained Love API [RLV]</a>
|
||||
<a href="[[SUBFOLDER]]HTTP.html" class="nav-item nav-link [[HTTPActive]]">WebLink [HTTP]</a>
|
||||
</div>
|
||||
<div class="navbar-nav ml-auto">
|
||||
<a href="https://bitbucket.org/streamadmin/secondbot/src/master/" target="_BLANK" class="nav-item nav-link">Get Source</a>
|
||||
<a href="secondlife:///app/group/cbda0f35-1c0b-2a45-e693-62a63a8f9d88/about" target="_BLANK" class="nav-item nav-link">SL group</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="container-fluid">
|
||||
|
|
@ -17,7 +17,7 @@ namespace BetterSecondBot.WikiMake
|
|||
protected string html_footer = "";
|
||||
protected string buildVersion = "";
|
||||
protected SimpleIO io;
|
||||
protected Dictionary<string,string> seen_command_names = new Dictionary<string, string>();
|
||||
protected Dictionary<string, string> seen_command_names = new Dictionary<string, string>();
|
||||
|
||||
|
||||
public string CreateRoot()
|
||||
|
|
@ -28,7 +28,7 @@ namespace BetterSecondBot.WikiMake
|
|||
sb.Append(buildVersion);
|
||||
sb.Append("</h3><br/>");
|
||||
sb.Append("<table class='table table-striped table-bordered'><thead><tr><th>Interface</th><th>Commands</th><th>Status</th></tr></thead><tbody>");
|
||||
foreach (KeyValuePair<string,KeyValuePair<int, string>> entry in api_reports)
|
||||
foreach (KeyValuePair<string, KeyValuePair<int, string>> entry in api_reports)
|
||||
{
|
||||
sb.Append("<tr>");
|
||||
sb.Append("<td><a href='[[SUBFOLDER]]");
|
||||
|
|
@ -51,7 +51,7 @@ namespace BetterSecondBot.WikiMake
|
|||
return sb.ToString();
|
||||
}
|
||||
|
||||
protected static StringBuilder MenuActive(StringBuilder index,string area)
|
||||
protected static StringBuilder MenuActive(StringBuilder index, string area)
|
||||
{
|
||||
Dictionary<string, string> active_swaps = new Dictionary<string, string>
|
||||
{
|
||||
|
|
@ -59,18 +59,17 @@ namespace BetterSecondBot.WikiMake
|
|||
{ "CLIActive", "" },
|
||||
{ "JSONActive", "" },
|
||||
{ "RLVapiActive", "" },
|
||||
{ "HTTPgetActive", "" },
|
||||
{ "HTTPpostActive", "" }
|
||||
{ "HTTPActive", "" }
|
||||
};
|
||||
active_swaps["" + area + "Active"] = "Active";
|
||||
foreach(KeyValuePair<string,string> pair in active_swaps)
|
||||
foreach (KeyValuePair<string, string> pair in active_swaps)
|
||||
{
|
||||
index = index.Replace("[["+ pair.Key+"]]", pair.Value);
|
||||
index = index.Replace("[[" + pair.Key + "]]", pair.Value);
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
protected void InterfaceCommands(string area, API_supported_interface shared_interface,bool track_commands=false)
|
||||
protected void InterfaceCommands(string area, API_supported_interface shared_interface, bool track_commands = false)
|
||||
{
|
||||
string[] cmds = shared_interface.GetCommandsList();
|
||||
string interface_name = shared_interface.GetType().Name;
|
||||
|
|
@ -84,7 +83,7 @@ namespace BetterSecondBot.WikiMake
|
|||
}
|
||||
else
|
||||
{
|
||||
LogFormater.Debug("command " + c + " from " + interface_name + " overlaps an ready loaded command from "+ seen_command_names[c]+"");
|
||||
LogFormater.Debug("command " + c + " from " + interface_name + " overlaps an ready loaded command from " + seen_command_names[c] + "");
|
||||
}
|
||||
}
|
||||
string workspace = shared_interface.GetCommandWorkspace(c);
|
||||
|
|
@ -94,7 +93,7 @@ namespace BetterSecondBot.WikiMake
|
|||
sb.Append(buildVersion);
|
||||
sb.Append("</h3><br/>");
|
||||
sb.Append("<h4>Interface: <a href='[[AREA]].html'>[[AREA]]</a>");
|
||||
if(workspace != "")
|
||||
if (workspace != "")
|
||||
{
|
||||
sb.Append(" / <a href='[[AREA]][[WORKSPACE]].html'>[[WORKSPACE]]</a>");
|
||||
}
|
||||
|
|
@ -103,7 +102,7 @@ namespace BetterSecondBot.WikiMake
|
|||
int minargs = shared_interface.GetCommandArgs(c);
|
||||
string[] arg_types = shared_interface.GetCommandArgTypes(c);
|
||||
string[] arg_hints = shared_interface.GetCommandArgHints(c);
|
||||
if(area == "Core")
|
||||
if (area == "Core")
|
||||
{
|
||||
StringBuilder ExampleCall = new StringBuilder();
|
||||
ExampleCall.Append("Example: [[COMMAND]]");
|
||||
|
|
@ -194,9 +193,9 @@ namespace BetterSecondBot.WikiMake
|
|||
sb.Append(buildVersion);
|
||||
sb.Append("</h4><br/>");
|
||||
sb.Append("<table class='datatable table table-striped table-bordered'><thead><tr><th>Command</th><th>Min args</th></tr></thead><tbody>");
|
||||
foreach(string c in cmds)
|
||||
foreach (string c in cmds)
|
||||
{
|
||||
if(shared_interface.GetCommandWorkspace(c) == workspace)
|
||||
if (shared_interface.GetCommandWorkspace(c) == workspace)
|
||||
{
|
||||
int args = shared_interface.GetCommandArgs(c);
|
||||
sb.Append("<tr><td><a href='[[AREA]]");
|
||||
|
|
@ -257,9 +256,9 @@ namespace BetterSecondBot.WikiMake
|
|||
sb.Replace("[[AREA]]", area);
|
||||
sb.Append(html_footer);
|
||||
sb = DebugModeCreateWiki.MenuActive(sb, area);
|
||||
io.writefile(""+ area+".html", sb.ToString());
|
||||
io.writefile("" + area + ".html", sb.ToString());
|
||||
}
|
||||
protected void InterfaceWiki(string area,API_supported_interface shared_interface, bool track_commands = false)
|
||||
protected void InterfaceWiki(string area, API_supported_interface shared_interface, bool track_commands = false)
|
||||
{
|
||||
LogFormater.Info("[WIKI] Starting area " + area + "");
|
||||
// create index
|
||||
|
|
@ -289,21 +288,494 @@ namespace BetterSecondBot.WikiMake
|
|||
|
||||
// CMD
|
||||
BSB.Commands.CoreCommandsInterface cmd = new BSB.Commands.CoreCommandsInterface(null);
|
||||
api_reports.Add("Core", new KeyValuePair<int, string>(cmd.ApiCommandsCount, "Basic"));
|
||||
InterfaceWiki("Core", cmd,true);
|
||||
api_reports.Add("Core", new KeyValuePair<int, string>(cmd.ApiCommandsCount, "Good"));
|
||||
InterfaceWiki("Core", cmd, true);
|
||||
|
||||
// RLVapi
|
||||
BSB.RLV.RLVcontrol RLVapi = new BSB.RLV.RLVcontrol(null);
|
||||
api_reports.Add("RLVapi", new KeyValuePair<int, string>(RLVapi.ApiCommandsCount, "Basic"));
|
||||
api_reports.Add("RLVapi", new KeyValuePair<int, string>(RLVapi.ApiCommandsCount, "Limited"));
|
||||
InterfaceWiki("RLVapi", RLVapi, true);
|
||||
|
||||
// HTTP post
|
||||
BetterSecondBot.HttpServer.HTTPCommandsInterface http_post = new BetterSecondBot.HttpServer.HTTPCommandsInterface(null,null);
|
||||
api_reports.Add("HTTPpost", new KeyValuePair<int, string>(http_post.ApiCommandsCount, "Limited"));
|
||||
InterfaceWiki("HTTPpost", http_post);
|
||||
|
||||
// HTTP
|
||||
HTTPWiki();
|
||||
|
||||
io.ChangeRoot("wiki");
|
||||
io.writefile("index.html", CreateRoot());
|
||||
}
|
||||
}
|
||||
|
||||
protected void HTTPWiki()
|
||||
{
|
||||
LogFormater.Info("[WIKI] Starting area HTTP endpoint");
|
||||
HTTPendpoint HTTP = new HTTPendpoint();
|
||||
|
||||
HTTPmenu("HTTP", HTTP);
|
||||
|
||||
// create workspaces
|
||||
HTTPWorkspaces("HTTP", HTTP);
|
||||
|
||||
// create commands
|
||||
HTTPCommands("HTTP", HTTP);
|
||||
|
||||
HTTP = null;
|
||||
LogFormater.Info("[WIKI] Done with area");
|
||||
}
|
||||
|
||||
protected string getURLargs(Dictionary<string, KeyValuePair<string, string>> values)
|
||||
{
|
||||
List<string> urlargs = new List<string>();
|
||||
foreach (KeyValuePair<string, KeyValuePair<string, string>> entry in values)
|
||||
{
|
||||
if (entry.Value.Key == "URLARG")
|
||||
{
|
||||
urlargs.Add("{"+entry.Key+"}");
|
||||
}
|
||||
}
|
||||
return String.Join('/', urlargs);
|
||||
}
|
||||
|
||||
protected void HTTPCommands(string area, HTTPendpoint HTTP)
|
||||
{
|
||||
foreach (string workspace in HTTP.getEndPoints())
|
||||
{
|
||||
|
||||
foreach (string c in HTTP.getEndpointCommands(workspace))
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append(html_header);
|
||||
sb.Append("<h3>Build: ");
|
||||
sb.Append(buildVersion);
|
||||
sb.Append("</h3><br/>");
|
||||
sb.Append("<h4>Interface: <a href='[[AREA]].html'>[[AREA]]</a>");
|
||||
if (workspace != "")
|
||||
{
|
||||
sb.Append(" / <a href='[[AREA]][[WORKSPACE]].html'>[[WORKSPACE]]</a>");
|
||||
}
|
||||
sb.Append("</h4><hr/><h3>[[COMMAND]]</h3>");
|
||||
sb.Append("[[URLENDPOINT]]/[[WORKSPACE]]/[[COMMAND]]/[[URLADDON]]<br/>");
|
||||
sb.Append("Method: [[APIMETHOD]]");
|
||||
sb.Append("<hr style='border-top: 1px dashed #dcdcdc;'>");
|
||||
sb.Append("[[HELP]]");
|
||||
Dictionary<string, KeyValuePair<string, string>> values = HTTP.getCommandArgs(workspace, c);
|
||||
int ValueCount = values.Count;
|
||||
|
||||
if (ValueCount > 0)
|
||||
{
|
||||
sb.Append("<hr/><h4>Args helper</h4>");
|
||||
sb.Append("<table class='table table-striped table-bordered'><thead><tr><td>Name</td><th>Type</th><th>Hint</th></tr></thead><tbody>");
|
||||
foreach (KeyValuePair<string, KeyValuePair<string, string>> entry in values)
|
||||
{
|
||||
string hint = entry.Value.Value;
|
||||
string type = entry.Value.Key;
|
||||
if (entry.Value.Key.Contains("Optional") == true)
|
||||
{
|
||||
type = type.Replace("Optional", "{Optional} ");
|
||||
}
|
||||
else if (entry.Value.Key == "URLARG")
|
||||
{
|
||||
type = "URL arg";
|
||||
}
|
||||
sb.Append("<tr><td>" + entry.Key + "</td><td>" + type + "</td><td>" + hint + "</td></tr>");
|
||||
}
|
||||
sb.Append("</tbody></table>");
|
||||
}
|
||||
|
||||
string[] returnvalues = HTTP.getReturnsValues(workspace, c);
|
||||
if (returnvalues.Length > 0)
|
||||
{
|
||||
sb.Append("<hr/><h4>Possible replys</h4>");
|
||||
sb.Append("<ul>");
|
||||
foreach (string entry in returnvalues)
|
||||
{
|
||||
sb.Append("<li>"+entry+"</li>");
|
||||
}
|
||||
sb.Append("</ul>");
|
||||
}
|
||||
|
||||
|
||||
|
||||
sb = DebugModeCreateWiki.MenuActive(sb, area);
|
||||
sb.Append(html_footer);
|
||||
sb.Replace("[[COMMAND]]", c);
|
||||
sb.Replace("[[URLENDPOINT]]", "http://localhost:8080");
|
||||
sb.Replace("[[APIMETHOD]]", HTTP.getCommandMethod(workspace, c));
|
||||
sb.Replace("[[URLADDON]]", getURLargs(values));
|
||||
sb.Replace("[[HELP]]", HTTP.getCommandAbout(workspace, c));
|
||||
sb.Replace("[[MINARGS]]", ValueCount.ToString());
|
||||
sb.Replace("[[WORKSPACE]]", workspace);
|
||||
sb.Replace("[[AREA]]", area);
|
||||
sb.Replace("[[SUBFOLDER]]", "");
|
||||
sb.Replace("[[RETURNROOT]]", "../");
|
||||
string target_file = "" + area + "" + workspace + "" + c + ".html";
|
||||
io.writefile(target_file, sb.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void HTTPmenu(string area, HTTPendpoint HTTP)
|
||||
{
|
||||
api_reports.Add(area, new KeyValuePair<int, string>(HTTP.getFullCount(), "Basic"));
|
||||
|
||||
// create index
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append(html_header);
|
||||
sb.Append("<h3>Interface:");
|
||||
sb.Append("HTTP");
|
||||
sb.Append("</h3><br/>");
|
||||
sb.Append("<h4>Build: ");
|
||||
sb.Append(buildVersion);
|
||||
sb.Append("</h4><br/>");
|
||||
sb.Append("<table class='datatable table table-striped table-bordered'><thead><tr><th>Workspace</th><th>Commands</th></tr></thead><tbody>");
|
||||
foreach (string workspace in HTTP.getEndPoints())
|
||||
{
|
||||
string workspace_link = "<a href='[[AREA]]" + workspace + ".html'>" + workspace + "</a>";
|
||||
sb.Append("<tr><td>");
|
||||
sb.Append(workspace_link);
|
||||
sb.Append("</td><td>");
|
||||
sb.Append(HTTP.getEndpointCount(workspace).ToString());
|
||||
sb.Append("</td></tr>");
|
||||
}
|
||||
sb.Append("</tbody></table>");
|
||||
sb.Replace("[[SUBFOLDER]]", "");
|
||||
sb.Replace("[[RETURNROOT]]", "../");
|
||||
sb.Replace("[[AREA]]", area);
|
||||
sb.Append(html_footer);
|
||||
sb = DebugModeCreateWiki.MenuActive(sb, area);
|
||||
io.writefile("" + area + ".html", sb.ToString());
|
||||
}
|
||||
|
||||
protected void HTTPWorkspaces(string area, HTTPendpoint HTTP)
|
||||
{
|
||||
foreach (string workspace in HTTP.getEndPoints())
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append(html_header);
|
||||
sb.Append("<h3>Interface: <a href='[[AREA]].html'>[[AREA]]</a>");
|
||||
if (workspace != "")
|
||||
{
|
||||
sb.Append(" / [[WORKSPACE]]");
|
||||
}
|
||||
sb.Append("</h3><br/>");
|
||||
sb.Append("<h4>Build: ");
|
||||
sb.Append(buildVersion);
|
||||
sb.Append("</h4><br/>");
|
||||
sb.Append("<table class='datatable table table-striped table-bordered'><thead><tr><th>Command</th><th>Min args</th></tr></thead><tbody>");
|
||||
foreach (string c in HTTP.getEndpointCommands(workspace))
|
||||
{
|
||||
int args = HTTP.getCommandArgCount(workspace, c);
|
||||
sb.Append("<tr><td><a href='[[AREA]]");
|
||||
sb.Append(workspace);
|
||||
sb.Append(c);
|
||||
sb.Append(".html'>");
|
||||
sb.Append(c);
|
||||
sb.Append("</a></td><td>");
|
||||
sb.Append(args.ToString());
|
||||
sb.Append("</td></tr>");
|
||||
}
|
||||
sb.Append("</tbody></table>");
|
||||
sb.Replace("[[SUBFOLDER]]", "");
|
||||
sb.Replace("[[RETURNROOT]]", "../");
|
||||
sb.Replace("[[WORKSPACE]]", workspace);
|
||||
sb.Replace("[[AREA]]", area);
|
||||
sb.Append(html_footer);
|
||||
sb = DebugModeCreateWiki.MenuActive(sb, area);
|
||||
string target_file = "" + area + "" + workspace + ".html";
|
||||
io.writefile(target_file, sb.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class HTTPendpoint
|
||||
{
|
||||
protected List<Endpoint> endpoints = new List<Endpoint>();
|
||||
public HTTPendpoint()
|
||||
{
|
||||
Endpoint core = new Endpoint();
|
||||
core.name = "core";
|
||||
core.callable.Add(new gettoken());
|
||||
core.callable.Add(new logout());
|
||||
core.callable.Add(new version());
|
||||
core.callable.Add(new name());
|
||||
core.callable.Add(new command());
|
||||
endpoints.Add(core);
|
||||
Endpoint inventory = new Endpoint();
|
||||
inventory.name = "inventory";
|
||||
inventory.callable.Add(new contents());
|
||||
inventory.callable.Add(new folders());
|
||||
endpoints.Add(inventory);
|
||||
}
|
||||
|
||||
public string getCommandMethod(string endpoint, string command)
|
||||
{
|
||||
foreach (Endpoint End in endpoints)
|
||||
{
|
||||
if (End.name == endpoint)
|
||||
{
|
||||
foreach (APIcall api in End.callable)
|
||||
{
|
||||
if (api.name == command)
|
||||
{
|
||||
return api.type;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return "?";
|
||||
}
|
||||
|
||||
public string getCommandAbout(string endpoint, string command)
|
||||
{
|
||||
foreach (Endpoint End in endpoints)
|
||||
{
|
||||
if (End.name == endpoint)
|
||||
{
|
||||
foreach (APIcall api in End.callable)
|
||||
{
|
||||
if (api.name == command)
|
||||
{
|
||||
return api.about;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return "?";
|
||||
}
|
||||
|
||||
public string[] getReturnsValues(string endpoint, string command)
|
||||
{
|
||||
foreach (Endpoint End in endpoints)
|
||||
{
|
||||
if (End.name == endpoint)
|
||||
{
|
||||
foreach (APIcall api in End.callable)
|
||||
{
|
||||
if (api.name == command)
|
||||
{
|
||||
return api.returns.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return new string[] { };
|
||||
}
|
||||
|
||||
|
||||
|
||||
public int getCommandArgCount(string endpoint, string command)
|
||||
{
|
||||
foreach (Endpoint End in endpoints)
|
||||
{
|
||||
if (End.name == endpoint)
|
||||
{
|
||||
foreach (APIcall api in End.callable)
|
||||
{
|
||||
if (api.name == command)
|
||||
{
|
||||
return api.values.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public string[] getEndpointCommands(string endpointname)
|
||||
{
|
||||
List<string> commands = new List<string>();
|
||||
foreach (Endpoint End in endpoints)
|
||||
{
|
||||
if (End.name == endpointname)
|
||||
{
|
||||
foreach (APIcall api in End.callable)
|
||||
{
|
||||
commands.Add(api.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
return commands.ToArray();
|
||||
}
|
||||
|
||||
public string[] getEndPoints()
|
||||
{
|
||||
List<string> names = new List<string>();
|
||||
foreach (Endpoint End in endpoints)
|
||||
{
|
||||
names.Add(End.name);
|
||||
}
|
||||
return names.ToArray();
|
||||
}
|
||||
|
||||
public int getEndpointCount(string endpointname)
|
||||
{
|
||||
foreach (Endpoint End in endpoints)
|
||||
{
|
||||
if (End.name == endpointname)
|
||||
{
|
||||
return End.callable.Count;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int getFullCount()
|
||||
{
|
||||
int count = 0;
|
||||
foreach (Endpoint End in endpoints)
|
||||
{
|
||||
count += End.callable.Count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
public Dictionary<string, KeyValuePair<string, string>> getCommandArgs(string endpoint, string command)
|
||||
{
|
||||
Dictionary<string, KeyValuePair<string, string>> empty = new Dictionary<string, KeyValuePair<string, string>>();
|
||||
foreach (Endpoint End in endpoints)
|
||||
{
|
||||
if (End.name == endpoint)
|
||||
{
|
||||
foreach (APIcall api in End.callable)
|
||||
{
|
||||
if (api.name == command)
|
||||
{
|
||||
return api.values;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return empty;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class Endpoint
|
||||
{
|
||||
public string name = "Notset";
|
||||
public List<APIcall> callable = new List<APIcall>();
|
||||
}
|
||||
|
||||
abstract public class APIcall
|
||||
{
|
||||
public string name { get { return GetType().Name.ToLowerInvariant(); } }
|
||||
public string type = "get";
|
||||
public string about = "A API call that is missing its about value";
|
||||
public Dictionary<string, KeyValuePair<string, string>> values = new Dictionary<string, KeyValuePair<string, string>>();
|
||||
public List<string> returns = new List<string>();
|
||||
public bool RequiresToken = true;
|
||||
|
||||
public APIcall()
|
||||
{
|
||||
Setup();
|
||||
}
|
||||
public virtual void Setup()
|
||||
{
|
||||
if (RequiresToken == true)
|
||||
{
|
||||
returns.Add("Token not accepted");
|
||||
values.Add(
|
||||
"token",
|
||||
new KeyValuePair<string, string>(
|
||||
"URLARG",
|
||||
"the api access token")
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract public class postAPIcall : APIcall
|
||||
{
|
||||
public override void Setup()
|
||||
{
|
||||
type = "post";
|
||||
base.Setup();
|
||||
}
|
||||
}
|
||||
|
||||
public class gettoken : postAPIcall
|
||||
{
|
||||
public override void Setup()
|
||||
{
|
||||
RequiresToken = false;
|
||||
about = "Requests a new token (Vaild for 10 mins) <br/>to use with all other requests";
|
||||
values.Add("Authcode", new KeyValuePair<string, string>("string", "the first 10 chars of SHA1(unixtime+WebUIkey)<br/>unixtime can be +- 30 of the bots time."));
|
||||
returns = new List<string>();
|
||||
returns.Add("Authcode not accepted");
|
||||
returns.Add("New API Token");
|
||||
base.Setup();
|
||||
}
|
||||
}
|
||||
|
||||
public class logout : APIcall
|
||||
{
|
||||
public override void Setup()
|
||||
{
|
||||
about = "A API call that is missing its about value";
|
||||
returns.Add("ok");
|
||||
returns.Add("Failed to remove token");
|
||||
base.Setup();
|
||||
}
|
||||
}
|
||||
|
||||
public class version : APIcall
|
||||
{
|
||||
public override void Setup()
|
||||
{
|
||||
about = "Gets the bots build version";
|
||||
returns.Add("Bot build version");
|
||||
base.Setup();
|
||||
}
|
||||
}
|
||||
|
||||
public class name : APIcall
|
||||
{
|
||||
public override void Setup()
|
||||
{
|
||||
about = "Gets the name of the bot";
|
||||
returns.Add("Fistname Lastname");
|
||||
base.Setup();
|
||||
}
|
||||
}
|
||||
|
||||
public class command : postAPIcall
|
||||
{
|
||||
public override void Setup()
|
||||
{
|
||||
about = "Makes a request to the core commands lib";
|
||||
values.Add(
|
||||
"body",
|
||||
new KeyValuePair<string, string>(
|
||||
"JsonObject",
|
||||
"A JSON object formated as follows<br/>Command: string<br/>Args: string[]<br/>AuthCode: string<br/>========<br/>See LSL example on how to create a core command auth code")
|
||||
);
|
||||
returns.Add("accepted");
|
||||
base.Setup();
|
||||
}
|
||||
}
|
||||
|
||||
public class contents : APIcall
|
||||
{
|
||||
public override void Setup()
|
||||
{
|
||||
about = "Requests the contents of a folder as an array of InventoryMapItem<br/>Formated as follows<br/>InventoryMapItem<br/><ul><li>id: UUID</li><li>name: String</li><li>typename: String</li></ul>";
|
||||
values.Add(
|
||||
"folderUUID",
|
||||
new KeyValuePair<string, string>(
|
||||
"URLARG",
|
||||
"the folder to fetch (Found via: inventory/folders)")
|
||||
);
|
||||
returns.Add("array of InventoryMapItem");
|
||||
base.Setup();
|
||||
}
|
||||
}
|
||||
|
||||
public class folders : APIcall
|
||||
{
|
||||
public override void Setup()
|
||||
{
|
||||
about = "Requests the inventory folder layout as a json object InventoryMapFolder<br/>Formated as follows<br/>InventoryMapItem<br/><ul><li>id: UUID</li><li>name: String</li><li>subfolders: InventoryMapFolder[]</li></ul>";
|
||||
returns.Add("array of InventoryMapFolder");
|
||||
base.Setup();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@
|
|||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>
|
||||
</PropertyGroup> <ItemGroup>
|
||||
<PackageReference Include="Discord.Net" Version="2.3.0-dev-20201219.2" />
|
||||
<PackageReference Include="Discord.Net.WebSocket" Version="2.3.0-dev-20201219.2" />
|
||||
<PackageReference Include="Discord.Net" Version="2.3.0" />
|
||||
<PackageReference Include="Discord.Net.WebSocket" Version="2.3.0" />
|
||||
<PackageReference Include="RestSharp" Version="106.11.2" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue