1
0
Fork 0

Initial commit.

master
Marrub 7 years ago
commit ae778d37e0

7
.gitignore vendored

@ -0,0 +1,7 @@
.nuget
obj
bin
data
packages
*.swp
Makefile

@ -0,0 +1,14 @@
using System;
using System.Reflection;
using System.Runtime.CompilerServices;
[assembly: AssemblyTitle("vrobot3")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("")]
[assembly: AssemblyCopyright("© 2016 Project Golan")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: CLSCompliant(false)]
[assembly: AssemblyVersion("3.1.*")]

@ -0,0 +1,234 @@
//-----------------------------------------------------------------------------
//
// Copyright © 2016 Project Golan
//
// See "LICENSE" for more information.
//
//-----------------------------------------------------------------------------
//
// Main bot class.
//
//-----------------------------------------------------------------------------
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using CommandFuncDict =
System.Collections.Generic.Dictionary<
System.String,
System.Tuple<
ProjectGolan.Vrobot3.IBotModule,
ProjectGolan.Vrobot3.BotCommandStructure>>;
namespace ProjectGolan.Vrobot3
{
//
// BotCommand
//
// Delegate type for bot commands.
//
public delegate void BotCommand(User usr, Channel channel, String msg);
//
// CommandDict
//
// Dictionary of bot commands.
//
public class CommandDict : Dictionary<String, BotCommandStructure> {}
//
// Bot
//
public partial class Bot
{
public List<IBotModule> modules = new List<IBotModule>();
public CommandFuncDict cmdfuncs = new CommandFuncDict();
public readonly BotInfo info;
private Dictionary<ulong, String> lastLine = new Dictionary<ulong, String>();
private IBotClient client;
public bool isInAudioChannel => false;
public ServerInfo serverInfo => client.info;
//
// Bot constructor
//
public Bot(BotInfo info)
{
this.info = info;
switch(info.serverType)
{
case ServerType.IRC: client = new BotClientIRC(this); break;
case ServerType.Discord: client = new BotClientDiscord(this); break;
}
var modClasses =
from assembly in AppDomain.CurrentDomain.GetAssemblies()
from type in assembly.GetTypes()
where moduleIsValid(type)
select type;
foreach(var mod in modClasses)
modules.Add(Activator.CreateInstance(mod, this) as IBotModule);
foreach(var mod in modules)
foreach(var kvp in mod.commands)
cmdfuncs.Add(kvp.Key, Tuple.Create(mod, kvp.Value));
}
//
// connect
//
public void connect() => client.connect();
//
// disconnect
//
public void disconnect()
{
cmdfuncs.Clear();
modules.Clear();
client.disconnect();
}
//
// reply
//
public void reply(User usr, Channel channel, String msg) =>
message(channel, usr.name + ": " + msg);
public void reply(User usr, ulong id, String msg) =>
message(id, usr.name + ": " + msg);
//
// message
//
public void message(Channel channel, String msg) =>
client.sendMessage(channel, msg);
public void message(ulong id, String msg) =>
client.sendMessage(client.getChannel(id), msg);
//
// action
//
public void action(Channel channel, String msg) =>
client.sendAction(channel, msg);
public void action(ulong id, String msg) =>
client.sendAction(client.getChannel(id), msg);
//
// joinAudioChannel
//
public async Task<bool> joinAudioChannel(User user)
{
var channel = client.getAudioChannel(user);
if(channel != null)
{
await client.joinAudioChannel(channel);
return true;
}
else
return false;
}
//
// partAudioChannel
//
public void partAudioChannel() => client.partAudioChannel();
//
// playAudioFile
//
public Task playAudioFile(String file) => client.playAudioFile(file);
//
// checkModPermissions
//
public bool checkModPermissions(Channel channel, Type mod)
{
String[] enables;
if(info.enables.ContainsKey(channel.name))
enables = info.enables[channel.name];
else if(info.enables.ContainsKey("*"))
enables = info.enables["*"];
else
return true;
foreach(var modname in enables)
{
Type type;
if(modname == "*")
return true;
else if(modname[0] == '@')
type = Type.GetType("ProjectGolan.Vrobot3.Modules." + modname.Substring(1));
else
type = Type.GetType(modname);
if(type == mod)
return true;
}
return false;
}
//
// runCommand
//
private void runCommand(User usr, Channel channel, BotCommand cmd,
String rest)
{
try
{
cmd(usr, channel, rest ?? String.Empty);
}
catch(CommandArgumentException exc)
{
if(exc.Message != null)
reply(usr, channel, exc.Message);
else
Console.WriteLine("{0}: Unknown CommandArgumentException",
info.serverName);
}
catch(Exception exc)
{
reply(usr, channel, "fug it borked");
Console.WriteLine("{0}: Unhandled exception in command: {1}",
info.serverName, exc?.Message ?? "unknown error");
File.WriteAllText(Program.Instance.dataDir + "/cmdexcdump.txt",
exc.ToString());
}
}
//
// moduleIsValid
//
private bool moduleIsValid(Type type)
{
if(!typeof(IBotModule).IsAssignableFrom(type) ||
!type.IsClass || type.IsAbstract)
return false;
foreach(var attribute in type.GetCustomAttributes(false))
{
if((attribute is BotModuleIRCAttribute &&
info.serverType != ServerType.IRC) ||
(attribute is BotModuleDiscordAttribute &&
info.serverType != ServerType.Discord) ||
(attribute is BotModuleRequiresAudioAttribute &&
!serverInfo.hasAudio) ||
attribute is BotModuleDisabledAttribute)
return false;
}
return true;
}
}
}
// EOF

@ -0,0 +1,53 @@
//-----------------------------------------------------------------------------
//
// Copyright © 2016 Project Golan
//
// See "LICENSE" for more information.
//
//-----------------------------------------------------------------------------
//
// Connection method interface.
//
//-----------------------------------------------------------------------------
using System;
using System.Threading.Tasks;
namespace ProjectGolan.Vrobot3
{
//
// IBotClient
//
public abstract class IBotClient
{
protected Bot bot;
public ServerInfo info;
public IBotClient(Bot bot) { this.bot = bot; }
// Connect
public abstract void connect();
public abstract void disconnect();
// Send
public abstract void sendAction(Channel channel, String msg);
public abstract void sendMessage(Channel channel, String msg);
// Channel
public abstract Channel getChannel(ulong id);
public virtual void joinChannel(Channel channel) {}
public virtual void partChannel(Channel channel) {}
// Audio
public virtual ChannelAudio getAudioChannel(User user) =>
new ChannelAudio();
public virtual async Task joinAudioChannel(ChannelAudio channel) =>
await Task.FromResult(0);
public virtual void partAudioChannel() {}
public virtual bool isInAudioChannel() => false;
public virtual async Task playAudioFile(String file) =>
await Task.FromResult(0);
}
}
// EOF

@ -0,0 +1,238 @@
//-----------------------------------------------------------------------------
//
// Copyright © 2016 Project Golan
//
// See "LICENSE" for more information.
//
//-----------------------------------------------------------------------------
//
// Discord client.
//
//-----------------------------------------------------------------------------
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Diagnostics;
using Discord.Audio;
namespace ProjectGolan.Vrobot3
{
//
// BotClientDiscord
//
public class BotClientDiscord : IBotClient
{
//
// AudioBuffer
//
class AudioBuffer
{
// private static const int MaxSize = 20 * 1024 * 1024;
private readonly String name;
private ulong num = 0;
private ulong next = 1;
private ulong bufSize = 0;
public bool completed { get; private set; } = false;
public AudioBuffer()
{
name = System.IO.Path.GetRandomFileName() + ".";
}
}
private Discord.DiscordClient client = new Discord.DiscordClient();
private Discord.Audio.IAudioClient audioClient;
private Discord.Server server;
//
// BotClientDiscord constructor
//
public BotClientDiscord(Bot bot) :
base(bot)
{
info.hasAudio = true;
info.hasColors = false;
info.hasNewlines = true;
info.messageSafeMaxLen = 1777;
info.shortMessages = false;
client.MessageReceived += (sender, evt) =>
{
if(!evt.Message.IsAuthor && !evt.User.IsBot &&
(bot.info.channels == null ||
bot.info.channels.Contains("#" + evt.Channel.Name)) &&
evt.Server.Id.ToString() == bot.info.serverAddr)
{
var usr = new User{};
var channel = new Channel{};
usr.hostname = evt.User.Id.ToString();
usr.name = evt.User.Nickname ?? evt.User.Name;
channel.id = evt.Channel.Id;
channel.name = "#" + evt.Channel.Name;
bot.onMessage(usr, channel, evt.Message.Text);
}
};
client.UsingAudio(x => x.Mode = AudioMode.Outgoing);
}
//
// getUser
//
private Discord.User getUser(User usr)
{
if(server == null)
server = client.GetServer(ulong.Parse(bot.info.serverAddr));
return server.GetUser(ulong.Parse(usr.hostname));
}
//
// connect
//
public override void connect()
{
Console.WriteLine("{0}: Creating connection.", bot.info.serverName);
client.ExecuteAndWait(async () =>
{
await client.Connect(bot.info.serverPass, Discord.TokenType.Bot);
client.SetGame("vrobot 3.1 series");
});
}
//
// disconnect
//
public override void disconnect()
{
if(client != null)
{
partAudioChannel();
client.Disconnect();
client = null;
}
}
//
// sendAction
//
public override void sendAction(Channel channel, String msg) =>
client.GetChannel(channel.id)?.SendMessage(
"_" + Discord.Format.Escape(msg) + "_");
//
// sendMessage
//
public override void sendMessage(Channel channel, String msg) =>
client.GetChannel(channel.id)?.SendMessage(Discord.Format.Escape(msg));
//
// getChannel
//
public override Channel getChannel(ulong id)
{
var dchannel = client.GetChannel(id);
var channel = new Channel{};
channel.id = dchannel.Id;
channel.name = "#" + dchannel.Name;
return channel;
}
//
// getAudioChannel
//
public override ChannelAudio getAudioChannel(User usr)
{
var dchannel = getUser(usr).VoiceChannel;
if(dchannel == null) return null;
var channel = new ChannelAudio{};
channel.id = dchannel.Id;
channel.name = dchannel.Name;
return channel;
}
//
// joinAudioChannel
//
public override async Task joinAudioChannel(ChannelAudio channel)
{
if(channel == null)
return;
var dchannel = client.GetChannel(channel.id);
if(!isInAudioChannel())
audioClient = await dchannel.JoinAudio();
else
await audioClient.Join(dchannel);
}
//
// partAudioChannel
//
public override void partAudioChannel()
{
if(isInAudioChannel())
{
audioClient.Clear();
audioClient.Wait();
audioClient.Disconnect();
}
audioClient = null;
}
//
// isInAudioChannel
//
public override bool isInAudioChannel() =>
audioClient?.State == Discord.ConnectionState.Connected;
//
// playAudioFile
//
public override async Task playAudioFile(String file)
{
if(!isInAudioChannel()) return;
var proc = Process.Start(new ProcessStartInfo{
FileName = "ffmpeg",
Arguments = $"-i {file} -f s16le -ar 48000 -ac 2 " +
"-loglevel quiet pipe:1",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = false,
CreateNoWindow = true
});
var buf = new byte[3840 * 32];
var ostream = audioClient.OutputStream;
var istream = proc.StandardOutput.BaseStream;
int count;
try
{
while(!proc.HasExited &&
(count = await istream.ReadAsync(buf, 0, buf.Length)) != 0)
{
Thread.Sleep(8);
await ostream.WriteAsync(buf, 0, count);
}
}
catch(OperationCanceledException)
{
Console.WriteLine("{0}: Canceled audio stream.",
bot.info.serverName);
}
finally
{
istream.Dispose();
ostream.Dispose();
}
}
}
}
// EOF

@ -0,0 +1,45 @@
//-----------------------------------------------------------------------------
//
// Copyright © 2016 Project Golan
//
// See "LICENSE" for more information.
//
//-----------------------------------------------------------------------------
//
// IRC client.
//
//-----------------------------------------------------------------------------
using System;
namespace ProjectGolan.Vrobot3
{
//
// BotClientIRC
//
public class BotClientIRC : IBotClient
{
//
// BotClientIRC constructor
//
public BotClientIRC(Bot bot) :
base(bot)
{
info.hasAudio = false;
info.hasColors = true;
info.hasNewlines = false;
info.messageSafeMaxLen = 601;
info.shortMessages = true;
}
public override void connect() {}
public override void disconnect() {}
public override Channel getChannel(ulong id) => new Channel{};
public override void joinChannel(Channel channel) {}
public override void partChannel(Channel channel) {}
public override void sendAction(Channel channel, String msg) {}
public override void sendMessage(Channel channel, String msg) {}
}
}
// EOF

@ -0,0 +1,106 @@
//-----------------------------------------------------------------------------
//
// Copyright © 2016 Project Golan
//
// See "LICENSE" for more information.
//
//-----------------------------------------------------------------------------
//
// Bot events.
//
//-----------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
namespace ProjectGolan.Vrobot3
{
//
// Bot
//
public partial class Bot
{
//
// onSeen
//
public void onSeen(User usr, Channel channel)
{
foreach(var mod in modules)
if(checkModPermissions(channel, mod.GetType()))
mod.events.raiseOnSeen(usr, channel);
}
//
// onMessage
//
// Attempt to run commands.
//
public void onMessage(User usr, Channel channel, String msg)
{
var validCmdPreceders = ".%".ToCharArray();
String rest = null;
if(msg.Length >= 1 && validCmdPreceders.Contains(msg[0]))
{
Predicate<BotCommandStructure> pred;
if(msg[0] == '%')
pred = fn => fn.flags.HasFlag(BotCommandFlags.AdminOnly);
else
pred = fn => !fn.flags.HasFlag(BotCommandFlags.AdminOnly);
var dict = from fn in cmdfuncs where pred(fn.Value.Item2) select fn;
// Get the command name.
String[] splt = msg.Substring(1).Split(" ".ToCharArray(), 2);
String cmdname = splt[0].ToLower();
// Handle commands ending with "^".
// These take the last message as input.
if(cmdname.EndsWith("^"))
{
cmdname = cmdname.Substring(0, cmdname.Length - 1);
lastLine.TryGetValue(channel.id, out rest);
}
var tcmd =
from kvp in dict where kvp.Key == cmdname select kvp.Value;
if(tcmd.Any())
{
var tcmdr = tcmd.First();
// Check permissions.
if(usr.hostname != info.adminId &&
(tcmdr.Item2.flags.HasFlag(BotCommandFlags.AdminOnly) ||
!checkModPermissions(channel, tcmdr.Item1.GetType())))
goto RaiseMessage;
// If we have input, grab that too.
if(rest == null && splt.Length > 1)
rest = splt[1];
else if(rest == null)
rest = String.Empty;
// Go over each module and raise a command message event.
foreach(var mod in modules)
if(checkModPermissions(channel, mod.GetType()))
mod.events.raiseOnCmdMessage(usr, channel, msg);
runCommand(usr, channel, tcmdr.Item2.cmd, rest);
return;
}
}
RaiseMessage:
// Go over each module and raise a message event.
foreach(var mod in modules)
if(checkModPermissions(channel, mod.GetType()))
mod.events.raiseOnMessage(usr, channel, msg);
lastLine[channel.id] = msg;
}
}
}
// EOF

@ -0,0 +1,81 @@
//-----------------------------------------------------------------------------
//
// Copyright © 2016 Project Golan
//
// See "LICENSE" for more information.
//
//-----------------------------------------------------------------------------
//
// Bot informational classes.
//
//-----------------------------------------------------------------------------
using System;
using System.Collections.Generic;
namespace ProjectGolan.Vrobot3
{
//
// ServerType
//
public enum ServerType
{
IRC,
Discord
}
//
// ServerInfo
//
public struct ServerInfo
{
public bool hasAudio;
public bool hasColors;
public bool hasNewlines;
public int messageSafeMaxLen;
public bool shortMessages;
}
//
// BotInfo
//
public struct BotInfo
{
public Dictionary<String, String[]> enables;
public ServerType serverType;
public String serverName;
public String serverPass;
public String serverAddr;
public String adminId;
public String[] channels;
}
//
// User
//
public struct User
{
public String hostname; // A consistent identifier for the user.
public String name; // Nickname for replying and etc.
}
//
// Channel
//
public struct Channel
{
public ulong id;
public String name;
}
//
// ChannelAudio
//
public class ChannelAudio
{
public ulong id;
public String name;
}
}
// EOF

@ -0,0 +1,123 @@
//-----------------------------------------------------------------------------
//
// Copyright © 2016 Project Golan
//
// See "LICENSE" for more information.
//
//-----------------------------------------------------------------------------
//
// Base module classes.
//
//-----------------------------------------------------------------------------
using System;
using System.Collections.Generic;
namespace ProjectGolan.Vrobot3
{
namespace Modules.EventType
{
public delegate void OnMessage(User usr, Channel channel, String msg);
public delegate void OnSeen(User usr, Channel channel);
}
//
// BotModuleRequiresAudioAttribute
//
public class BotModuleRequiresAudioAttribute : Attribute
{
public override String ToString() => "Bot Module Requires Audio";
}
//
// BotModuleDisabledAttribute
//
public class BotModuleDisabledAttribute : Attribute
{
public override String ToString() => "Bot Module Disabled";
}
//
// BotModuleDiscordAttribute
//
public class BotModuleDiscordAttribute : Attribute
{
public override String ToString() => "Bot Module is Discord only";
}
//
// BotModuleIRCAttribute
//
public class BotModuleIRCAttribute : Attribute
{
public override String ToString() => "Bot Module is IRC only";
}
//
// BotCommandFlags
//
// Flags for command registration.
//
[Flags]
public enum BotCommandFlags
{
AdminOnly = 1 << 0,
Hidden = 1 << 1
}
//
// BotCommandStructure
//
// Used for registering commands in a module.
//
public struct BotCommandStructure
{
public BotCommand cmd;
public BotCommandFlags flags;
public String help;
}
//
// IBotModule
//
// Base module class. Inherit this for your modules.
//
public abstract class IBotModule
{
//
// Events
//
public struct Events
{
public event Modules.EventType.OnMessage onCmdMessage;
public event Modules.EventType.OnMessage onMessage;
public event Modules.EventType.OnSeen onSeen;
public void raiseOnCmdMessage(User usr, Channel channel, String msg)
{
if(onCmdMessage != null)
onCmdMessage(usr, channel, msg);
}
public void raiseOnMessage(User usr, Channel channel, String msg)
{
if(onMessage != null)
onMessage(usr, channel, msg);
}
public void raiseOnSeen(User usr, Channel channel)
{
if(onSeen != null)
onSeen(usr, channel);
}
}
public IBotModule(Bot bot) { this.bot = bot; }
public CommandDict commands = new CommandDict();
public Events events;
protected Bot bot;
}
}
// EOF

@ -0,0 +1,31 @@
//-----------------------------------------------------------------------------
//
// Copyright © 2016 Project Golan
//
// See "LICENSE" for more information.
//
//-----------------------------------------------------------------------------
//
// Exception types.
//
//-----------------------------------------------------------------------------
using System;
namespace ProjectGolan.Vrobot3
{
//
// CommandArgumentException
//
public class CommandArgumentException : Exception
{
public CommandArgumentException() {}
public CommandArgumentException(String message) : base(message) {}
public CommandArgumentException(String message, Exception inner) :
base(message, inner)
{
}
}
}
// EOF

@ -0,0 +1,81 @@
//-----------------------------------------------------------------------------
//
// Copyright © 2016 Project Golan
//
// See "LICENSE" for more information.
//
//-----------------------------------------------------------------------------
//
// Link (URI) utilities.
//
//-----------------------------------------------------------------------------
using System;
using System.Text.RegularExpressions;
namespace ProjectGolan.Vrobot3
{
//
// Utils
//
public static partial class Utils
{
//
// URI
//
public struct URI
{
public String method, host, path, query, tag, uri;
//
// ToString
//
public override String ToString() => uri;
//
// Finder
//
public static Regex Finder = new Regex(
@"((?<method>[^:/?# ]+)?:)" +
@"(//(?<host>[^/?# ]*))" +
@"(?<path>[^?# ]*)" +
@"(?<query>\?([^# ]*))?" +
@"(?<tag>#(.*))?");
//
// FromMatch
//
public static URI FromMatch(Match match)
{
return new URI{
method = match.Groups["method"]?.Value ?? String.Empty,
host = match.Groups["host"]?.Value,
path = match.Groups["path"]?.Value,
query = match.Groups["query"]?.Value ?? String.Empty,
tag = match.Groups["tag"]?.Value ?? String.Empty,
uri = match.Value
};
}
//
// Match
//
public static URI Match(String str) => FromMatch(Finder.Match(str));
//
// Matches
//
public static URI[] Matches(String str)
{
var matchbox = Finder.Matches(str);
if(matchbox.Count == 0) return null;
var matches = new URI[matchbox.Count];
for(int i = 0; i < matchbox.Count; i++)
matches[i] = FromMatch(matchbox[i]);
return matches;
}
}
}
}
// EOF

@ -0,0 +1,82 @@
//-----------------------------------------------------------------------------
//
// Copyright © 2016 Project Golan
//
// See "LICENSE" for more information.
//
//-----------------------------------------------------------------------------
//
// Admin commands module.
// %kill, %msg, %action
//
//-----------------------------------------------------------------------------
using System;
using System.Linq;
namespace ProjectGolan.Vrobot3.Modules
{
//
// Mod_Admin
//
public class Mod_Admin : IBotModule
{
//
// Mod_Admin constructor
//
public Mod_Admin(Bot bot_) :
base(bot_)
{
commands["kill"] = new BotCommandStructure{
cmd = cmdKill,
flags = BotCommandFlags.AdminOnly,
help = "Kills all bot instances.\n" +
"Syntax: %kill"
};
commands["msg"] = new BotCommandStructure{
cmd = cmdMsg,
flags = BotCommandFlags.AdminOnly,
help = "Sends a message.\n" +
"Syntax: %msg channel, msg\n" +
"Example: %msg #general, ur all dumb"
};
commands["action"] = new BotCommandStructure{
cmd = cmdAction,
flags = BotCommandFlags.AdminOnly,
help = "Sends an action.\n" +
"Syntax: %action channel, msg\n" +
"Example: %action #general, explodes violently"
};
}
//
// cmdKill
//
public void cmdKill(User usr, Channel channel, String msg)
{
Console.WriteLine("{0}: Killing all instances.", bot.info.serverName);
Program.Instance.end();
}
//
// cmdMsg
//
public void cmdMsg(User usr, Channel channel, String msg)
{
String[] args = Utils.GetArguments(msg, commands["msg"].help, 2, 2);
bot.message(ulong.Parse(args[0]), args[1].Trim());
}
//
// cmdAction
//
public void cmdAction(User usr, Channel channel, String msg)
{
String[] args = Utils.GetArguments(msg, commands["action"].help, 2, 2);
bot.action(ulong.Parse(args[0]), args[1].Trim());
}
}
}

@ -0,0 +1,202 @@
//-----------------------------------------------------------------------------
//
// Copyright © 2016 Project Golan
//
// See "LICENSE" for more information.
//
//-----------------------------------------------------------------------------
//
// Audio-based commands.
//
//-----------------------------------------------------------------------------
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
namespace ProjectGolan.Vrobot3.Modules
{
//
// Mod_Audio
//
[BotModuleRequiresAudio, BotModuleDisabled]
public class Mod_Audio : IBotModule
{
//
// QueueItem
//
class QueueItem
{
Utils.URI uri;
public QueueItem(Utils.URI uri)
{
this.uri = uri;
}
public override String ToString() => String.Empty;
}
//
// Queue
//
class Queue
{
TimeSpan curTime;
List<QueueItem> items;
int pos;
public Queue()
{
this.curTime = new TimeSpan();
this.items = new List<QueueItem>();
this.pos = 0;
}
public bool addItem(Utils.URI uri)
{
var item = new QueueItem(uri);
items.Add(item);
return true;
}
}
String[] validMethods = { "http", "https", "ftp", "ftps" };
Random rnd = Utils.GetRND();
Queue queue = new Queue();
//
// Mod_Audio constructor
//
public Mod_Audio(Bot bot_) :
base(bot_)
{
commands["queue"] = new BotCommandStructure{
cmd = cmdQueue,
help = "Add an item (or items) to the audio queue.\n" +
"Syntax: .queue uri...\n" +
"Example: .queue https://www.youtube.com/watch?v=13pL0TiOiHM"
};
commands["play"] = new BotCommandStructure{
cmd = cmdPlay,
help = "Set the currently playing item in the queue. " +
"If a URL is given, queues and plays that.\n" +
"Syntax: .play [number|uri]\n" +
"Example: .play 5\n" +
"Example: .play https://www.youtube.com/watch?v=13pL0TiOiHM"
};
commands["lsqueue"] = new BotCommandStructure{
cmd = cmdListQueue,
help = "Lists queue items.\n" +
"Syntax: .lsqueue"
};
commands["fugoff"] = new BotCommandStructure{
cmd = cmdFugOff,
help = "GET ME COGS OR FUG OFF",
flags = BotCommandFlags.AdminOnly
};
commands["summon"] = new BotCommandStructure{
cmd = cmdSummon,
help = "Makes the bot join your audio channel.\n" +
"Syntax: .summon"
};
commands["vanquish"] = new BotCommandStructure{
cmd = cmdVanquish,
help = "Makes the bot leave their audio channel.\n" +
"Syntax: %vanquish",
flags = BotCommandFlags.AdminOnly
};
}
//
// summon
//
async Task<bool> summon(User usr, Channel channel)
{
if(bot.isInAudioChannel)
return true;
if(!await bot.joinAudioChannel(usr))
{
bot.reply(usr, channel,
"Couldn't find audio channel. " +
"If you are already in an audio channel, please reconnect to " +
"it and try again.");
return false;
}
return true;
}
//
// cmdQueue
//
public void cmdQueue(User usr, Channel channel, String msg)
{
var uris = Utils.URI.Matches(msg);
if(uris == null)
throw new CommandArgumentException("no valid URIs");
int loadPass = 0;
foreach(var uri_ in uris)
{
var uri = uri_;
if(uri.method == String.Empty)
uri.method = "http";
if(validMethods.Contains(uri.method) &&
queue.addItem(uri))
loadPass++;
}
bot.reply(usr, channel,
$"Added {loadPass} item{loadPass == 1 ? "" : "s"} to the queue.");
}
//
// cmdPlay
//
public void cmdPlay(User usr, Channel channel, String msg)
{
}
//
// cmdListQueue
//
public void cmdListQueue(User usr, Channel channel, String msg)
{
}
//
// cmdFugOff
//
public async void cmdFugOff(User usr, Channel channel, String msg)
{
if(!await summon(usr, channel))
return;
await bot.playAudioFile("\"/home/marrub/musix/MusixDL/Shadowfax - Shadowdance/01 New Electric India.mp3\"").ConfigureAwait(false);
}
//
// cmdSummon
//
public void cmdSummon(User usr, Channel channel, String msg) =>
summon(usr, channel);
//
// cmdVanquish
//
public void cmdVanquish(User usr, Channel channel, String msg) =>
bot.partAudioChannel();
}
}

@ -0,0 +1,112 @@
//-----------------------------------------------------------------------------
//
// Copyright © 2016 Project Golan
//
// See "LICENSE" for more information.
//
//-----------------------------------------------------------------------------
//
// Fun stuff.
// .carmack, .revenant, .wan, .nyan, .:^)
//
//-----------------------------------------------------------------------------
using System;
namespace ProjectGolan.Vrobot3.Modules
{
//
// Mod_Fun
//
public class Mod_Fun : IBotModule
{
//
// ShitpostingDevice
//
private class ShitpostingDevice
{
private String word, final;
private Random rnd = Utils.GetRND();
private int min, max;
private Bot bot;
//
// ShitpostingDevice constructor
//
public ShitpostingDevice(String word, String final, int min, int max,
Bot bot)
{
this.word = word;
this.final = final;
this.min = min;
this.max = max;
this.bot = bot;
}
//
// run
//
public void run(User usr, Channel channel, String msg)
{
int n = rnd.Next(min, max);
String outp = String.Empty;
if(bot.serverInfo.hasColors && rnd.Next(0, 8) == 1)
for(int i = 0; i < 6; i++)
{
String[] colors = { "04", "07", "08", "09", "12", "06" };
outp += "\x03";
outp += colors[i];
outp += word;
outp += word;
}
else
for(int i = 0; i < n; i++)
outp += word;
bot.reply(usr, channel, outp + final);
}
}
//
// Mod_Fun constructor
//
public Mod_Fun(Bot bot_) :
base(bot_)
{
commands["carmack"] = new BotCommandStructure{
cmd = new ShitpostingDevice("MM", "", 3, 20, bot).run,
flags = BotCommandFlags.Hidden
};
commands["revenant"] = new BotCommandStructure{
cmd = new ShitpostingDevice("AA", "", 3, 20, bot).run,
flags = BotCommandFlags.Hidden
};
commands["wan"] = new BotCommandStructure{
cmd = new ShitpostingDevice("wan ", "- !", 2, 12, bot).run,
flags = BotCommandFlags.Hidden
};
commands["nyan"] = new BotCommandStructure{
cmd = new ShitpostingDevice("nyan ", "!~", 2, 12, bot).run,
flags = BotCommandFlags.Hidden
};
commands[":^)"] = new BotCommandStructure{
cmd = (usr, channel, msg) => bot.message(channel, ":^)"),
flags = BotCommandFlags.Hidden
};
events.onMessage += onMessage;
}
//
// onMessage
//
public void onMessage(User usr, Channel channel, String msg)
{
if(msg.Contains("OLD MEN"))
bot.message(channel, "WARNING! WARNING!");
}
}
}
// EOF

@ -0,0 +1,182 @@
//-----------------------------------------------------------------------------
//
// Copyright © 2016 Project Golan
//
// See "LICENSE" for more information.
//
//-----------------------------------------------------------------------------
//
// Idgames search module.
// .idgames
//
//-----------------------------------------------------------------------------
using System;
using System.Net;
using System.Web;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Linq;
namespace ProjectGolan.Vrobot3.Modules
{
//
// Mod_Idgames
//
public class Mod_Idgames : IBotModule
{
static readonly String APIURI =
"http://doomworld.com/idgames/api/api.php";
private Random rnd = Utils.GetRND();
//
// Mod_Idgames constructor
//
public Mod_Idgames(Bot bot_) :
base(bot_)
{
commands["idgames"] = new BotCommandStructure{
cmd = cmdIdgames,
help = "Gets an entry from the idgames archive.\n" +
"Syntax: .idgames [name or ID[, type[, position]]]\n" +
"Example: .idgames scythe, filename, 4\n" +
"Example: .idgames"
};
}
//
// cmdIdgames
//
public void cmdIdgames(User usr, Channel channel, String msg)
{
String[] args =
Utils.GetArguments(msg, commands["idgames"].help, 0, 3);
switch(args.Length)
{
case 1:
int id;
if(args[0].Trim().Length == 0)
idgamesRandom(usr, channel);
else if(Int32.TryParse(args[0], out id))
idgamesID(usr, channel, id);
else
idgames(usr, channel, args[0]);
break;
case 2: idgames(usr, channel, args[0], args[1]); break;
case 3:
if(args[2].Trim().ToLower() == "random")
idgames(usr, channel, args[0], args[1], "random");
else
idgames(usr, channel, args[0], args[1], args[2].Trim());
break;
}
}
//
// idgamesRandom
//
private void idgamesRandom(User usr, Channel channel)
{
var req = WebRequest.Create("http://doomworld.com/idgames/?random")
as HttpWebRequest;
req.Referer = "http://doomworld.com/idgames/";
bot.message(channel,
Discord.Format.Escape(req.GetResponse().ResponseUri.ToString()));
}
//
// idgamesID
//
private void idgamesID(User usr, Channel channel, int id)
{
var req = WebRequest.Create(APIURI + "?action=get&id=" + id)
as HttpWebRequest;
using(var response = req.GetResponse())
{
var xml = XDocument.Load(response.GetResponseStream());
var x_title =
from item in xml.Descendants("title") select item.Value;
var x_uri = from item in xml.Descendants("url") select item.Value;
if(!x_title.Any())
{
bot.message(channel, "Nothing found.");
return;
}
bot.message(channel,
Discord.Format.Escape(x_title.First() + ": " + x_uri.First()));
}
}
//
// idgames
//
private void idgames(User usr, Channel channel, String inquiry,
String type = "title", String pos = "1")
{
int ipos = 0;
if(pos != "random")
{
Utils.TryParse(pos, "Invalid position.", out ipos);
if(ipos < 1)
throw new CommandArgumentException("Invalid position.");
ipos = ipos - 1;
}
inquiry = HttpUtility.UrlEncode(inquiry.Trim());
type = HttpUtility.UrlEncode(type.Trim().ToLower());
if(type == "name") type = "title"; // >_>'
String[] validtypes = {
"filename", "title", "author", "email",
"description", "credits", "editors", "textfile"
};
if(!validtypes.Contains(type))
throw new CommandArgumentException("Invalid inquiry type.");
String uri = APIURI + "?action=search&sort=rating&query=" +
inquiry + "&type=" + type;
var req = WebRequest.Create(uri);
Console.WriteLine("idgames query: {0}", uri);
using(var response = req.GetResponse())
{
var xml = XDocument.Load(response.GetResponseStream());
var x_titles =
from item in xml.Descendants("title") select item.Value;
var x_uris = from item in xml.Descendants("url") select item.Value;
if(!x_titles.Any())
{
bot.message(channel, "Nothing found.");
return;
}
if(pos == "random") ipos = rnd.Next(0, x_titles.Count());
if(ipos >= x_titles.Count()) ipos = x_titles.Count() - 1;
String title = x_titles.ElementAtOrDefault(ipos);
if(title.Trim().Length