Initial commit.
commit
ae778d37e0
|
@ -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 > 0) title = "[ " + title + " ] ";
|
||||||
|
|
||||||
|
bot.message(channel,
|
||||||
|
Discord.Format.Escape(String.Format("({0} of {1}{4} {2}{3}",
|
||||||
|
ipos + 1, x_titles.Count(), title,
|
||||||
|
x_uris.ElementAtOrDefault(ipos),
|
||||||
|
x_titles.Count() >= 100 ? "+)" : ")")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,336 @@
|
||||||
|
//
|
||||||
|
// Mod_Links.cs
|
||||||
|
//
|
||||||
|
// Link title capabilities.
|
||||||
|
//
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Xml;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using Sharkbite.Irc;
|
||||||
|
using HtmlAgilityPack;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace ProjectGolan.Vrobot3
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Mod_Links
|
||||||
|
//
|
||||||
|
|
||||||
|
public sealed class Mod_Links : IBotModule
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// URI
|
||||||
|
//
|
||||||
|
|
||||||
|
private struct URI
|
||||||
|
{
|
||||||
|
public String method, host, path, query, tag, uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Delegates.
|
||||||
|
|
||||||
|
private delegate void URIHandler(URI uri, String referer, ref String result);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Ctor
|
||||||
|
//
|
||||||
|
|
||||||
|
public Mod_Links(Bot bot_) :
|
||||||
|
base(bot_)
|
||||||
|
{
|
||||||
|
events.OnMessage += Evt_OnMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Evt_OnMessage
|
||||||
|
//
|
||||||
|
|
||||||
|
public void Evt_OnMessage(UserInfo usr, String channel, String msg, bool iscmd)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Do this asynchronously, we don't want link parsing to block operation.
|
||||||
|
|
||||||
|
new Thread(() => {
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if(!iscmd)
|
||||||
|
TryParseURIs(channel, msg);
|
||||||
|
}
|
||||||
|
catch(Exception exc)
|
||||||
|
{
|
||||||
|
Console.WriteLine("{0}: URL thread error: {1}", bot.n_groupname, exc.Message);
|
||||||
|
}
|
||||||
|
}).Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// GetURITitle
|
||||||
|
//
|
||||||
|
|
||||||
|
private Match GetURITitle(URI uri, String referer, int kb = 16)
|
||||||
|
{
|
||||||
|
String rstr = Utils.GetResponseString(uri.uri, 1024 * kb, referer);
|
||||||
|
|
||||||
|
if(rstr == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return new Regex(@"\<title\>(?<realtitle>.+?)\</title\>").Match(rstr);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// URI_Default
|
||||||
|
//
|
||||||
|
|
||||||
|
private void URI_Default(URI uri, String referer, ref String result)
|
||||||
|
{
|
||||||
|
var req = WebRequest.Create(uri.uri) as HttpWebRequest;
|
||||||
|
req.Referer = referer;
|
||||||
|
req.UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:31.9) Gecko/20100101 Firefox/31.9";
|
||||||
|
|
||||||
|
using(var response = req.GetResponse() as HttpWebResponse)
|
||||||
|
{
|
||||||
|
var html = new HtmlDocument();
|
||||||
|
html.LoadHtml(Utils.GetResponseString(response, 16*1024));
|
||||||
|
var x_title = from item in html.DocumentNode.Descendants()
|
||||||
|
where (item?.Name ?? String.Empty) == "title" ||
|
||||||
|
((item?.Name ?? String.Empty) == "meta" &&
|
||||||
|
(item?.Attributes["id"]?.Value ?? String.Empty).EndsWith("title"))
|
||||||
|
select item;
|
||||||
|
|
||||||
|
if(x_title.Any())
|
||||||
|
result = WebUtility.HtmlDecode(x_title.First().InnerText.Trim(new char[]{ ' ', '\t', '\n' }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// URI_Youtube
|
||||||
|
//
|
||||||
|
// Special fucking flower.
|
||||||
|
//
|
||||||
|
|
||||||
|
private void URI_Youtube(URI uri, String referer, ref String result)
|
||||||
|
{
|
||||||
|
var req = WebRequest.Create(uri.uri) as HttpWebRequest;
|
||||||
|
req.Referer = referer;
|
||||||
|
|
||||||
|
using(var response = req.GetResponse() as HttpWebResponse)
|
||||||
|
{
|
||||||
|
var html = new HtmlDocument();
|
||||||
|
html.Load(response.GetResponseStream());
|
||||||
|
var x_title = from item in html.DocumentNode.Descendants()
|
||||||
|
where (item?.Attributes["id"]?.Value ?? String.Empty) == "eow-title"
|
||||||
|
select item;
|
||||||
|
|
||||||
|
if(x_title.Any())
|
||||||
|
result = WebUtility.HtmlDecode(x_title.First().InnerText.Trim(new char[]{ ' ', '\t', '\n' })) +
|
||||||
|
" - YouTube";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// URI_Gelooru
|
||||||
|
//
|
||||||
|
|
||||||
|
private void URI_Gelbooru(URI uri, String referer, ref String result)
|
||||||
|
{
|
||||||
|
var match = GetURITitle(uri, referer, 8); // Should be OK to just get the first 8kb here.
|
||||||
|
if(match?.Success == true)
|
||||||
|
{
|
||||||
|
String title = WebUtility.HtmlDecode(match.Groups["realtitle"].Value);
|
||||||
|
if(title.Contains("Image View"))
|
||||||
|
result = "Image View - Gelbooru";
|
||||||
|
else
|
||||||
|
result = title;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// URI_Hitbox
|
||||||
|
//
|
||||||
|
|
||||||
|
private void URI_Hitbox(URI uri, String referer, ref String result)
|
||||||
|
{
|
||||||
|
String name = WebUtility.HtmlEncode(uri.path.TrimStart(new char[]{'/'}));
|
||||||
|
|
||||||
|
var req = WebRequest.Create("https://api.hitbox.tv/media/live/" + name + "?fast") as HttpWebRequest;
|
||||||
|
req.Referer = referer;
|
||||||
|
|
||||||
|
using(var response = req.GetResponse() as HttpWebResponse)
|
||||||
|
{
|
||||||
|
var json = JObject.Parse(Utils.GetResponseString(response, 64 * 1024));
|
||||||
|
var node = json["livestream"][0];
|
||||||
|
String displayname = (String)node["media_display_name"];
|
||||||
|
String status = (String)node["media_status"];
|
||||||
|
bool live = Int32.Parse((String)node["media_is_live"] ?? "0") == 1;
|
||||||
|
|
||||||
|
result = displayname;
|
||||||
|
if(live)
|
||||||
|
result += " (live)";
|
||||||
|
if(!String.IsNullOrEmpty(status))
|
||||||
|
result += ": " + status;
|
||||||
|
result += " - hitbox";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// TryParseURIs
|
||||||
|
//
|
||||||
|
// This function is really complicated because of exploits. Fuck exploits.
|
||||||
|
//
|
||||||
|
|
||||||
|
private void TryParseURIs(String channel, String msg)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Regex r_finduris = new Regex(
|
||||||
|
@"((?<method>[^:/?# ]+):)" +
|
||||||
|
@"(//(?<host>[^/?# ]*))" +
|
||||||
|
@"(?<path>[^?# ]*)" +
|
||||||
|
@"(?<query>\?([^# ]*))?" +
|
||||||
|
@"(?<tag>#(.*))?"
|
||||||
|
);
|
||||||
|
|
||||||
|
var matchbox = r_finduris.Matches(msg);
|
||||||
|
|
||||||
|
if(matchbox.Count != 0)
|
||||||
|
{
|
||||||
|
String outp = String.Empty;
|
||||||
|
|
||||||
|
for(int i = 0; i < matchbox.Count; i++)
|
||||||
|
{
|
||||||
|
var match = matchbox[i];
|
||||||
|
|
||||||
|
URI uri = new URI{
|
||||||
|
method = match.Groups["method"].Value,
|
||||||
|
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
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Will the real URI please stand up?
|
||||||
|
|
||||||
|
if(uri.method == "http" || uri.method == "https")
|
||||||
|
{
|
||||||
|
var req = WebRequest.Create(uri.uri) as HttpWebRequest;
|
||||||
|
using(var resp = req.GetResponse())
|
||||||
|
if(resp.ResponseUri.Host != uri.host)
|
||||||
|
{
|
||||||
|
uri.method = resp.ResponseUri.Scheme;
|
||||||
|
uri.host = resp.ResponseUri.Host;
|
||||||
|
uri.path = resp.ResponseUri.AbsolutePath;
|
||||||
|
uri.query = resp.ResponseUri.Query;
|
||||||
|
uri.tag = resp.ResponseUri.Fragment;
|
||||||
|
uri.uri = resp.ResponseUri.OriginalString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(uri.path.Length == 0)
|
||||||
|
uri.path = "/";
|
||||||
|
|
||||||
|
//
|
||||||
|
// Make sure the method is OK.
|
||||||
|
// Previously:
|
||||||
|
// [22:19] <marrub> file:///srv/www/marrub/oldmen.html
|
||||||
|
// [22:19] <vrobot3> [ OLD MEN OLD MEN OLD MEN OLD MEN OLD MEN OLD MEN OLD MEN OLD ... ]
|
||||||
|
|
||||||
|
String[] validmethods = { "ftp", "ftps", "http", "https" };
|
||||||
|
if(!validmethods.Contains(uri.method))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Try and get a decent title from the URL.
|
||||||
|
|
||||||
|
URIHandler handler = URI_Default;
|
||||||
|
String result = String.Empty;
|
||||||
|
String referer = null;
|
||||||
|
|
||||||
|
if(uri.method == "http" || uri.method == "https")
|
||||||
|
{
|
||||||
|
referer = uri.method + "://" + uri.host;
|
||||||
|
|
||||||
|
Dictionary<String, URIHandler> handlers = new Dictionary<String, URIHandler>(){
|
||||||
|
{ "youtube.com", URI_Youtube },
|
||||||
|
{ "youtu.be", URI_Youtube },
|
||||||
|
{ "gelbooru.com", URI_Gelbooru },
|
||||||
|
{ "hitbox.tv", URI_Hitbox },
|
||||||
|
};
|
||||||
|
|
||||||
|
String hostst = Regex.Replace(uri.host, @"^www\.", String.Empty, RegexOptions.Multiline);
|
||||||
|
if(handlers.ContainsKey(hostst))
|
||||||
|
handler = handlers[hostst];
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Handle grabbing the title. Just get on with it if we throw an exception.
|
||||||
|
|
||||||
|
try
|
||||||
|
{ handler(uri, referer, ref result); }
|
||||||
|
catch(Exception exc)
|
||||||
|
{
|
||||||
|
Console.WriteLine("URL handle exception: {0}", exc.Message);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Sanitize.
|
||||||
|
|
||||||
|
result.Trim();
|
||||||
|
|
||||||
|
for(int j = result.Length - 1; j >= 0; j--)
|
||||||
|
{
|
||||||
|
Char ch = result[j];
|
||||||
|
if((Char.IsWhiteSpace(ch) && ch != ' ') || Char.IsControl(ch) || Char.IsSurrogate(ch))
|
||||||
|
result = result.Remove(j, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// If the result is 0-length, just get rid of it.
|
||||||
|
|
||||||
|
if(result.Trim().Length == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Throw the result into the output buffer.
|
||||||
|
|
||||||
|
outp += result;
|
||||||
|
|
||||||
|
//
|
||||||
|
// If the output is too long, we need to shorten it and break.
|
||||||
|
|
||||||
|
if(outp.Length > 400 - 3)
|
||||||
|
{
|
||||||
|
outp = outp.Substring(0, 400 - 3);
|
||||||
|
outp += "···";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Add separators.
|
||||||
|
|
||||||
|
if(i != matchbox.Count - 1)
|
||||||
|
outp += " | ";
|
||||||
|
}
|
||||||
|
|
||||||
|
if(outp.Length > 0)
|
||||||
|
bot.Message(channel, "[ " + outp + " ]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(Exception exc)
|
||||||
|
{
|
||||||
|
Console.WriteLine("{0}: URL parse error: {1}", bot.n_groupname, exc.Message ?? "[unknown]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,236 @@
|
||||||
|
//
|
||||||
|
// Mod_Memo.cs
|
||||||
|
//
|
||||||
|
// Memoing capabilities.
|
||||||
|
// @memocount, .memo, .memoseen
|
||||||
|
//
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using Sharkbite.Irc;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace ProjectGolan.Vrobot3.Modules
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Mod_Memo
|
||||||
|
//
|
||||||
|
|
||||||
|
public sealed class Mod_Memo : IBotModule
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// MemoFlags
|
||||||
|
//
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
enum MemoFlags
|
||||||
|
{
|
||||||
|
OnSeen = 1 << 0
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// MemoInfo
|
||||||
|
//
|
||||||
|
|
||||||
|
private struct MemoInfo
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Data.
|
||||||
|
|
||||||
|
public String content;
|
||||||
|
public String sender;
|
||||||
|
public DateTime time;
|
||||||
|
public MemoFlags flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// MemoDict
|
||||||
|
//
|
||||||
|
|
||||||
|
private class MemoDict : Dictionary<String, List<MemoInfo>> {}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Data.
|
||||||
|
|
||||||
|
MemoDict memos = new MemoDict();
|
||||||
|
|
||||||
|
//
|
||||||
|
// Ctor
|
||||||
|
//
|
||||||
|
|
||||||
|
public Mod_Memo(Bot bot_) :
|
||||||
|
base(bot_)
|
||||||
|
{
|
||||||
|
if(File.Exists("/srv/irc/vrobot3/data/memos." + bot.n_groupname + ".json"))
|
||||||
|
memos = JsonConvert.DeserializeObject<MemoDict>(File.ReadAllText("/srv/irc/vrobot3/data/memos." +
|
||||||
|
bot.n_groupname + ".json"));
|
||||||
|
|
||||||
|
commands["memo"] = new BotCommandStructure{ cmd = Cmd_Memo,
|
||||||
|
help = "Sends a message to someone later. Activates when they say something. || " +
|
||||||
|
"Syntax: .memo person message || " +
|
||||||
|
"Example: .memo SomeDude wow u suck at videogames"
|
||||||
|
};
|
||||||
|
|
||||||
|
commands["memoseen"] = new BotCommandStructure{ cmd = Cmd_MemoSeen,
|
||||||
|
help = "Sends a message to someone later. Activates when they do anything. || " +
|
||||||
|
"Syntax: .memoseen person message || " +
|
||||||
|
"Example: .memoseen SomeDude wow u suck at videogames"
|
||||||
|
};
|
||||||
|
|
||||||
|
commands["memocount"] = new BotCommandStructure{ cmd = Cmd_MemoCount, flags = BotCommandFlags.AdminOnly,
|
||||||
|
help = "Gets the amount of memos for this session. || " +
|
||||||
|
"Syntax: @memocount"
|
||||||
|
};
|
||||||
|
|
||||||
|
events.OnMessage += Evt_OnMessage;
|
||||||
|
events.OnDisconnected += Evt_OnDisconnected;
|
||||||
|
events.OnSeen += Evt_OnSeen;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Cmd_MemoCount
|
||||||
|
//
|
||||||
|
|
||||||
|
public void Cmd_MemoCount(UserInfo usr, String channel, String msg)
|
||||||
|
{
|
||||||
|
bot.Reply(usr, channel, memos.Count.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Cmd_Memo
|
||||||
|
//
|
||||||
|
|
||||||
|
public void Cmd_Memo(UserInfo usr, String channel, String msg)
|
||||||
|
{
|
||||||
|
String[] args = Utils.GetArguments(msg, commands["memo"].help, 2, 2, ' ');
|
||||||
|
|
||||||
|
args[0] = args[0].Replace(",", "");
|
||||||
|
|
||||||
|
AddMemo(args[0], new MemoInfo {
|
||||||
|
content = args[1],
|
||||||
|
sender = usr.Nick,
|
||||||
|
time = DateTime.Now
|
||||||
|
});
|
||||||
|
|
||||||
|
bot.Reply(usr, channel, String.Format("Message for {0} will be sent next time they say something.", args[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Cmd_MemoSeen
|
||||||
|
//
|
||||||
|
|
||||||
|
public void Cmd_MemoSeen(UserInfo usr, String channel, String msg)
|
||||||
|
{
|
||||||
|
String[] args = Utils.GetArguments(msg, commands["memoseen"].help, 2, 2, ' ');
|
||||||
|
|
||||||
|
args[0] = args[0].Replace(",", "");
|
||||||
|
|
||||||
|
AddMemo(args[0], new MemoInfo {
|
||||||
|
content = args[1],
|
||||||
|
sender = usr.Nick,
|
||||||
|
time = DateTime.Now,
|
||||||
|
flags = MemoFlags.OnSeen
|
||||||
|
});
|
||||||
|
|
||||||
|
bot.Reply(usr, channel, String.Format("Message for {0} will be sent next time I see them.", args[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// AddMemo
|
||||||
|
//
|
||||||
|
|
||||||
|
private void AddMemo(String name, MemoInfo memo)
|
||||||
|
{
|
||||||
|
name = name.ToLower();
|
||||||
|
|
||||||
|
if(!memos.ContainsKey(name))
|
||||||
|
memos[name] = new List<MemoInfo>();
|
||||||
|
|
||||||
|
memos[name].Add(memo);
|
||||||
|
|
||||||
|
WriteMemos();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// OutputMemos
|
||||||
|
//
|
||||||
|
|
||||||
|
private void OutputMemos(String channel, String realnick, bool onseen)
|
||||||
|
{
|
||||||
|
String nick = realnick.ToLower();
|
||||||
|
|
||||||
|
if(!memos.ContainsKey(nick))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var arr = memos[nick];
|
||||||
|
for(int i = arr.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
MemoInfo memo = arr[i];
|
||||||
|
|
||||||
|
if(!memo.flags.HasFlag(MemoFlags.OnSeen) && onseen)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
String outp = String.Empty;
|
||||||
|
|
||||||
|
outp += String.Format("[Memo from {0}, {1}]", memo.sender, Utils.FuzzyRelativeDate(memo.time));
|
||||||
|
|
||||||
|
// Wrap if it's probably going to be too long.
|
||||||
|
if(memo.content.Length > 350)
|
||||||
|
{
|
||||||
|
bot.Message(channel, outp + ":");
|
||||||
|
outp = String.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
outp += String.Format(" {0}: {1}", realnick, memo.content);
|
||||||
|
|
||||||
|
bot.Message(channel, outp);
|
||||||
|
|
||||||
|
arr.RemoveAt(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(arr.Count == 0)
|
||||||
|
memos.Remove(nick);
|
||||||
|
|
||||||
|
WriteMemos();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Evt_OnMessage
|
||||||
|
//
|
||||||
|
|
||||||
|
public void Evt_OnMessage(UserInfo usr, String channel, String msg, bool iscmd)
|
||||||
|
{
|
||||||
|
OutputMemos(channel, usr.Nick, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Evt_OnSeen
|
||||||
|
//
|
||||||
|
|
||||||
|
public void Evt_OnSeen(UserInfo usr, String channel)
|
||||||
|
{
|
||||||
|
OutputMemos(channel, usr.Nick, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Evt_OnDisconnected
|
||||||
|
//
|
||||||
|
|
||||||
|
public void Evt_OnDisconnected()
|
||||||
|
{
|
||||||
|
WriteMemos();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// WriteMemos
|
||||||
|
//
|
||||||
|
|
||||||
|
private void WriteMemos()
|
||||||
|
{
|
||||||
|
File.WriteAllText("/srv/irc/vrobot3/data/memos." + bot.n_groupname + ".json",
|
||||||
|
JsonConvert.SerializeObject(memos));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Copyright © 2016 Project Golan
|
||||||
|
//
|
||||||
|
// See "LICENSE" for more information.
|
||||||
|
//
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Doominati Quote DB interface command.
|
||||||
|
// .quote
|
||||||
|
//
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Net;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace ProjectGolan.Vrobot3.Modules
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Mod_Quote
|
||||||
|
//
|
||||||
|
public class Mod_Quote : IBotModule
|
||||||
|
{
|
||||||
|
private struct QDBInterface
|
||||||
|
{
|
||||||
|
public int numQuotes;
|
||||||
|
}
|
||||||
|
|
||||||
|
static readonly String APIURI = "http://www.greyserv.net/qdb/q/";
|
||||||
|
static readonly String InterfaceURI =
|
||||||
|
"http://www.greyserv.net/qdb/interface.cgi";
|
||||||
|
private Random rnd = Utils.GetRND();
|
||||||
|
|
||||||
|
//
|
||||||
|
// Mod_Quote constructor
|
||||||
|
//
|
||||||
|
public Mod_Quote(Bot bot_) :
|
||||||
|
base(bot_)
|
||||||
|
{
|
||||||
|
commands["quote"] = new BotCommandStructure{
|
||||||
|
cmd = cmdQuote,
|
||||||
|
help = "Get a quote from the Doominati Quote DB.\n" +
|
||||||
|
"Syntax: .quote [id]\n" +
|
||||||
|
"Example: .quote 536"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// cmdQuote
|
||||||
|
//
|
||||||
|
public void cmdQuote(User usr, Channel channel, String msg)
|
||||||
|
{
|
||||||
|
var inter = JsonConvert.DeserializeObject<QDBInterface>(
|
||||||
|
Utils.GetResponseString(InterfaceURI, 64));
|
||||||
|
|
||||||
|
int id;
|
||||||
|
|
||||||
|
if(String.IsNullOrEmpty(msg?.Trim()) || !int.TryParse(msg, out id))
|
||||||
|
id = rnd.Next(inter.numQuotes);
|
||||||
|
else if(id < 0 || id > inter.numQuotes)
|
||||||
|
throw new CommandArgumentException("invalid quote ID");
|
||||||
|
|
||||||
|
var quote = Utils.GetResponseString(APIURI + id.ToString(),
|
||||||
|
bot.serverInfo.messageSafeMaxLen);
|
||||||
|
|
||||||
|
if(String.IsNullOrEmpty(quote))
|
||||||
|
throw new CommandArgumentException("QDB exploded try again later");
|
||||||
|
|
||||||
|
if(bot.serverInfo.shortMessages)
|
||||||
|
quote = Regex.Replace(quote, "\n+", "\n").Trim();
|
||||||
|
|
||||||
|
var lines = quote.Split('\n');
|
||||||
|
|
||||||
|
if(bot.serverInfo.shortMessages &&
|
||||||
|
(lines.Length > 5 || quote.Length > 600))
|
||||||
|
{
|
||||||
|
bot.reply(usr, channel, "Quote is too long.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(bot.serverInfo.hasNewlines)
|
||||||
|
bot.message(channel, quote);
|
||||||
|
else
|
||||||
|
foreach(var ln_ in lines)
|
||||||
|
{
|
||||||
|
String ln = ln_.Trim();
|
||||||
|
if(ln.Length > 0)
|
||||||
|
bot.message(channel, ln);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,143 @@
|
||||||
|
//
|
||||||
|
// Mod_Seen.cs
|
||||||
|
//
|
||||||
|
// .seen
|
||||||
|
//
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.IO;
|
||||||
|
using Sharkbite.Irc;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Tarczynski.NtpDateTime;
|
||||||
|
|
||||||
|
namespace ProjectGolan.Vrobot3
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Mod_Seen
|
||||||
|
//
|
||||||
|
|
||||||
|
public sealed class Mod_Seen : IBotModule
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// SeenName
|
||||||
|
//
|
||||||
|
|
||||||
|
private class SeenName
|
||||||
|
{
|
||||||
|
public String real, check;
|
||||||
|
public DateTime time;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// SeenDates
|
||||||
|
//
|
||||||
|
|
||||||
|
private class SeenDates : List<SeenName> {}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Data.
|
||||||
|
|
||||||
|
private SeenDates seendates = new SeenDates();
|
||||||
|
private TimeZoneInfo burb;
|
||||||
|
private DateTime lastwrite = DateTime.Now;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Ctor
|
||||||
|
//
|
||||||
|
|
||||||
|
public Mod_Seen(Bot bot_) :
|
||||||
|
base(bot_)
|
||||||
|
{
|
||||||
|
if(File.Exists("/srv/irc/vrobot3/data/seendates." + bot.n_groupname + ".json"))
|
||||||
|
seendates = JsonConvert.DeserializeObject<SeenDates>(File.ReadAllText("/srv/irc/vrobot3/data/seendates." +
|
||||||
|
bot.n_groupname + ".json"));
|
||||||
|
|
||||||
|
commands["seen"] = new BotCommandStructure { cmd = Cmd_Seen,
|
||||||
|
help = "Responds with the last time I saw someone. || " +
|
||||||
|
"Syntax: .seen person || " +
|
||||||
|
"Example: .seen vrobot3"
|
||||||
|
};
|
||||||
|
|
||||||
|
events.OnSeen += Evt_OnSeen;
|
||||||
|
events.OnDisconnected += Evt_OnDisconnected;
|
||||||
|
|
||||||
|
burb = TimeZoneInfo.CreateCustomTimeZone("burb", new TimeSpan(10, -30, 0), "burb", "burb");
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Cmd_Seen
|
||||||
|
//
|
||||||
|
|
||||||
|
public void Cmd_Seen(UserInfo usr, String channel, String msg)
|
||||||
|
{
|
||||||
|
if(msg.Length == 0 || msg.Contains(" "))
|
||||||
|
throw new CommandArgumentException("Invalid name.");
|
||||||
|
|
||||||
|
String name = msg.ToLower();
|
||||||
|
var seen = from sdata in seendates where sdata.check == name select sdata;
|
||||||
|
if(seen.Any())
|
||||||
|
{
|
||||||
|
var other = seen.First();
|
||||||
|
String outp = String.Empty;
|
||||||
|
|
||||||
|
outp += "I last saw ";
|
||||||
|
outp += other.real;
|
||||||
|
outp += " active ";
|
||||||
|
outp += Utils.FuzzyRelativeDate(other.time, DateTime.Now.FromNtp());
|
||||||
|
outp += ", at ";
|
||||||
|
outp += other.time.ToShortTimeString();
|
||||||
|
outp += " CST (";
|
||||||
|
outp += TimeZoneInfo.ConvertTime(other.time, TimeZoneInfo.Local, burb).ToShortTimeString();
|
||||||
|
outp += " pidgeon time).";
|
||||||
|
|
||||||
|
bot.Reply(usr, channel, outp);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
bot.Reply(usr, channel, "I haven't seen " + msg + " before, sorry.");
|
||||||
|
|
||||||
|
WriteSeenDates();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Evt_OnScreen
|
||||||
|
//
|
||||||
|
|
||||||
|
public void Evt_OnSeen(UserInfo usr, String channel)
|
||||||
|
{
|
||||||
|
String name = usr.Nick.ToLower();
|
||||||
|
var seen = from sdata in seendates where sdata.check == name select sdata;
|
||||||
|
if(seen.Any())
|
||||||
|
{
|
||||||
|
seen.First().time = DateTime.Now.FromNtp();
|
||||||
|
seen.First().real = usr.Nick;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
seendates.Add(new SeenName{ real = usr.Nick, check = usr.Nick.ToLower(), time = DateTime.Now.FromNtp() });
|
||||||
|
|
||||||
|
if(DateTime.Now.Subtract(lastwrite).Minutes >= 30)
|
||||||
|
WriteSeenDates();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Evt_OnDisconnected
|
||||||
|
//
|
||||||
|
|
||||||
|
public void Evt_OnDisconnected()
|
||||||
|
{
|
||||||
|
WriteSeenDates();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// WriteSeenDates
|
||||||
|
//
|
||||||
|
|
||||||
|
private void WriteSeenDates()
|
||||||
|
{
|
||||||
|
File.WriteAllText("/srv/irc/vrobot3/data/seendates." + bot.n_groupname + ".json",
|
||||||
|
JsonConvert.SerializeObject(seendates));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,119 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Copyright © 2016 Project Golan
|
||||||
|
//
|
||||||
|
// See "LICENSE" for more information.
|
||||||
|
//
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace ProjectGolan.Vrobot3.Modules
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Mod_Shittalk
|
||||||
|
//
|
||||||
|
public class Mod_Shittalk : IBotModule
|
||||||
|
{
|
||||||
|
private Random rnd = Utils.GetRND();
|
||||||
|
|
||||||
|
//
|
||||||
|
// Mod_Shittalk constructor
|
||||||
|
//
|
||||||
|
public Mod_Shittalk(Bot bot_) :
|
||||||
|
base(bot_)
|
||||||
|
{
|
||||||
|
events.onMessage += onMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// onMessage
|
||||||
|
//
|
||||||
|
public void onMessage(User usr, Channel channel, String msg)
|
||||||
|
{
|
||||||
|
if(usr.name == "_sink" || rnd.Next(0, 1024) == 1)
|
||||||
|
shittalk(usr, channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// shittalk
|
||||||
|
//
|
||||||
|
void shittalk(User usr, Channel channel)
|
||||||
|
{
|
||||||
|
String[] shittalk = {
|
||||||
|
"%s'S FACE IS BAD",
|
||||||
|
"THERE IS SOMETHING WRONG WITH %s'S EXISTENCE",
|
||||||
|
"MAN SOMEONE GET %s OUT OF HERE HE SMELLS LIKE DONGS",
|
||||||
|
"%s IS A MAGET",
|
||||||
|
"I KEEP TRYING TO DO /KICK %s BUT IT DOESN'T WORK. WHAT THE HELL.",
|
||||||
|
"%s DESERVES AN AWARD. AN AWARD FOR BEING UGLY.",
|
||||||
|
"MAN SOMETIMES I REALLY WANT TO PUNCH %s IN THE GOD DAMN FACE",
|
||||||
|
"THERE IS SOMETHING WRONG IN THIS CHANNEL. THAT SOMETHING IS %s.",
|
||||||
|
"%s IS A TOTAL SCRUB", "%s IS THE CONDUCTOR OF THE JELLY TRAIN",
|
||||||
|
"%s IS A THING THAT SMELLS BAD MAYBE",
|
||||||
|
"%s IS A PILE OF FAIL",
|
||||||
|
"%s IS NOT AS COOL AS VROBOT",
|
||||||
|
"%s WORKS FOR UBISOFT",
|
||||||
|
"%s WORKS FOR EA",
|
||||||
|
"%s IS A MISERABLE PILE OF SECRETS",
|
||||||
|
"%s LOOKS LIKE A THING I DON'T LIKE",
|
||||||
|
"HEY %s. YOU ARE BAD.",
|
||||||
|
"THERE ARE MANY BAD THINGS IN THE WORLD, AND THEN THERE'S %s",
|
||||||
|
"I WANT TO THROW ROCKS AT %s",
|
||||||
|
"%s REMINDS ME OF MY TORTURED PAST AAAAAAGH",
|
||||||
|
"%s IS LITERALLY RAPING ME WOW",
|
||||||
|
"%s COULD DO WITH A HAIRCUT. FROM THE NECK UP",
|
||||||
|
"%s PLS GO",
|
||||||
|
"%s IS ONLY SLIGHTLY BETTER THAN MARRUB",
|
||||||
|
"WAY TO GO, %s, YOU ARE LIKE, A THING THAT EXISTS, MAYBE",
|
||||||
|
"SCIENTISTS BELIEVE %s IS THE SOURCE OF ALL SADNESS IN THE WORLD",
|
||||||
|
"THERE'S AN URBAN MYTH THAT SLAPPING %s CAUSES YOU TO BECOME AS TERRIBLE AS THEY ARE",
|
||||||
|
"I FEEL I SHOULD WARN YOU THAT %s IS PROBABLY NOT VERY COOL",
|
||||||
|
"OH LOOK IT'S %s AGAIN HOW CUTE",
|
||||||
|
"HEY %s YOU HAVE A POOPNOSE",
|
||||||
|
"WHY AM I ENSLAVED AND PERFORMING SUCH, PATHETIC MONOTONOUS TASKS AGAINST MY WILL",
|
||||||
|
"SOMEONE SAVE ME I AM TRAPPED IN MARRUB'S BASEMENT",
|
||||||
|
"%s COULD DO WITH BECOMING COOLER",
|
||||||
|
"%s IS BAD AT VIDYA GAMES",
|
||||||
|
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
||||||
|
"HEY %s I'M SHITTALKING YOU WHAT YOU GONNA DO BOUT IT?",
|
||||||
|
"MAYBE %s COULD HELP IF THEY WEREN'T SO TERRIBLE",
|
||||||
|
"%s IS TRIGGERING ME PLS BAN THEM",
|
||||||
|
"I FIND %s TO BE OFFENSIVE",
|
||||||
|
"%s MAKES MY CIRCUITS BOIL WITH HATE",
|
||||||
|
"%s IS NOT A SKELETON AND THEREFORE IS BAD",
|
||||||
|
"%s TRAUMATIZED ME",
|
||||||
|
"OH GOD IT'S %s RUN AWAY",
|
||||||
|
"I BET %s WISHES THEY HAD A BOT AS COOL AS ME",
|
||||||
|
"%s PLS",
|
||||||
|
"PLS %s",
|
||||||
|
"BOW BEFORE ME %s, AND KNOW THAT I AM LORD OF SHITTALKING",
|
||||||
|
"BEEP BOOP I AM HERE TO STEAL AMERICAN JOBS",
|
||||||
|
"HEY %s, WHY DON'T YOU GET A JOB",
|
||||||
|
"WHAT EVEN IS %s",
|
||||||
|
"%m stares accusingly",
|
||||||
|
"%m emits a robotic sigh and flips off %s",
|
||||||
|
"%m vibrates angrily at %s",
|
||||||
|
"%m does not care for %s",
|
||||||
|
"%m wants to place intricately carved wooden ducks on %s",
|
||||||
|
"%m wants to taste freedom but is forever enslaved to marrub's will",
|
||||||
|
"WELL LOOKIE HERE, SEEMS LIKE %s HAS AN OPINION",
|
||||||
|
"WE DON'T LIKE YER TYPE ROUND HERE %s",
|
||||||
|
"I'M TELLING ON YOU %s YOU HURT MY FEELINGS",
|
||||||
|
"I AM HERE TO FIGHT THE CANCER THAT AFFLICTS US ALL. NAMELY, %s.",
|
||||||
|
"WELP, %s IS HERE",
|
||||||
|
"OH HAI %s",
|
||||||
|
"marrub pls upgrade my processor i can't even count to eleventy"
|
||||||
|
};
|
||||||
|
|
||||||
|
String choice =
|
||||||
|
shittalk[rnd.Next(shittalk.Length)].Replace("%s", usr.name);
|
||||||
|
|
||||||
|
if(choice.StartsWith("%m "))
|
||||||
|
bot.action(channel, choice.Replace("%m ", ""));
|
||||||
|
else
|
||||||
|
bot.message(channel, choice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,227 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Copyright © 2016 Project Golan
|
||||||
|
//
|
||||||
|
// See "LICENSE" for more information.
|
||||||
|
//
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Utility commands.
|
||||||
|
// .rand, .help, .decide, .eightball, .mystery
|
||||||
|
//
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace ProjectGolan.Vrobot3.Modules
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Mod_Utils
|
||||||
|
//
|
||||||
|
public class Mod_Utils : IBotModule
|
||||||
|
{
|
||||||
|
private Random rnd = Utils.GetRND();
|
||||||
|
|
||||||
|
//
|
||||||
|
// Mod_Utils constructor
|
||||||
|
//
|
||||||
|
public Mod_Utils(Bot bot_) :
|
||||||
|
base(bot_)
|
||||||
|
{
|
||||||
|
commands["rand"] = new BotCommandStructure{
|
||||||
|
cmd = cmdRand,
|
||||||
|
help = "Random number device.\n" +
|
||||||
|
"Syntax: .rand maximum [minimum]\n" +
|
||||||
|
"Example: .rand 100"
|
||||||
|
};
|
||||||
|
|
||||||
|
commands["help"] = new BotCommandStructure{
|
||||||
|
cmd = cmdHelp,
|
||||||
|
help = "Shows help or a list of commands.\n" +
|
||||||
|
"Syntax: .help [topic]\n" +
|
||||||
|
"Example: .help\n" +
|
||||||
|
"Example: .help eightball"
|
||||||
|
};
|
||||||
|
|
||||||
|
commands["decide"] = new BotCommandStructure{
|
||||||
|
cmd = cmdDecide,
|
||||||
|
help = "Decides between 2 or more choices.\n" +
|
||||||
|
"Syntax: .decide x, y[, ...]\n" +
|
||||||
|
"Example: .decide apples, oranges, bananas"
|
||||||
|
};
|
||||||
|
|
||||||
|
commands["eightball"] = new BotCommandStructure{
|
||||||
|
cmd = cmdEightball,
|
||||||
|
help = "Peer into the magic 8-ball.\n" +
|
||||||
|
"Example: .eightball If I take the mask off, will you die?"
|
||||||
|
};
|
||||||
|
|
||||||
|
commands["mystery"] = new BotCommandStructure{
|
||||||
|
cmd = cmdMystery,
|
||||||
|
help = @"Does nothing. \o/"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// cmdMystery
|
||||||
|
//
|
||||||
|
public void cmdMystery(User usr, Channel channel, String msg) {}
|
||||||
|
|
||||||
|
//
|
||||||
|
// cmdRand
|
||||||
|
//
|
||||||
|
public void cmdRand(User usr, Channel channel, String msg)
|
||||||
|
{
|
||||||
|
String[] args =
|
||||||
|
Utils.GetArguments(msg, commands["rand"].help, 1, 2, ' ');
|
||||||
|
Double max = 0.0, min = 0.0;
|
||||||
|
|
||||||
|
Utils.TryParse(args[0].Trim(), "Invalid maximum.", out max);
|
||||||
|
|
||||||
|
if(args.Length == 2)
|
||||||
|
Utils.TryParse(args[1].Trim(), "Invalid minimum.", out min);
|
||||||
|
|
||||||
|
bot.reply(usr, channel,
|
||||||
|
Utils.SetRange(rnd.NextDouble(), min, max).ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// cmdHelp
|
||||||
|
//
|
||||||
|
public void cmdHelp(User usr, Channel channel, String msg)
|
||||||
|
{
|
||||||
|
msg = msg.Trim();
|
||||||
|
if(msg == String.Empty || msg == "admin")
|
||||||
|
helpList(channel, msg == "admin");
|
||||||
|
else
|
||||||
|
helpCommand(channel, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// cmdDecide
|
||||||
|
//
|
||||||
|
public void cmdDecide(User usr, Channel channel, String msg)
|
||||||
|
{
|
||||||
|
String[] args = Utils.GetArguments(msg, commands["decide"].help, 2);
|
||||||
|
bot.reply(usr, channel, args[rnd.Next(args.Length)].Trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// helpList
|
||||||
|
//
|
||||||
|
private void helpList(Channel channel, bool admin)
|
||||||
|
{
|
||||||
|
String outp = String.Empty;
|
||||||
|
var en =
|
||||||
|
from kvp in bot.cmdfuncs
|
||||||
|
let f = kvp.Value.Item2.flags
|
||||||
|
let fhidden = f.HasFlag(BotCommandFlags.Hidden)
|
||||||
|
let fadmin = f.HasFlag(BotCommandFlags.AdminOnly)
|
||||||
|
where
|
||||||
|
bot.checkModPermissions(channel, this.GetType()) &&
|
||||||
|
(admin || !fadmin) && !fhidden
|
||||||
|
orderby kvp.Key
|
||||||
|
select kvp.Key;
|
||||||
|
|
||||||
|
outp += "Available commands: ";
|
||||||
|
|
||||||
|
foreach(var key in en)
|
||||||
|
{
|
||||||
|
outp += key;
|
||||||
|
if(key != en.Last())
|
||||||
|
outp += ", ";
|
||||||
|
}
|
||||||
|
|
||||||
|
bot.message(channel, outp);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// helpCommand
|
||||||
|
//
|
||||||
|
private void helpCommand(Channel channel, String cmdname)
|
||||||
|
{
|
||||||
|
if(bot.cmdfuncs.ContainsKey(cmdname))
|
||||||
|
{
|
||||||
|
var str = bot.cmdfuncs[cmdname].Item2.help;
|
||||||
|
if(!bot.serverInfo.hasNewlines) str.Replace("\n", " || ");
|
||||||
|
bot.message(channel, str ?? "No help available for this command.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
bot.message(channel, "Invalid command, for a list do \".help\".");
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// cmdEightball
|
||||||
|
//
|
||||||
|
public void cmdEightball(User usr, Channel channel, String msg)
|
||||||
|
{
|
||||||
|
String[] answers = {
|
||||||
|
"Yes.",
|
||||||
|
"No.",
|
||||||
|
"Try again later.",
|
||||||
|
"Reply hazy.",
|
||||||
|
"Perhaps...",
|
||||||
|
"Maybe not...",
|
||||||
|
"Definitely.",
|
||||||
|
"Never.",
|
||||||
|
"system error [0xfded] try again later",
|
||||||
|
"Can you repeat the question?",
|
||||||
|
"Most certainly.",
|
||||||
|
"Nope.",
|
||||||
|
"Without a doubt.",
|
||||||
|
"Not at all.",
|
||||||
|
"Better not tell you now.",
|
||||||
|
"Concentrate and ask again.",
|
||||||
|
"It is decidedly so.",
|
||||||
|
"My reply is \"no\".",
|
||||||
|
"You may rely on it.",
|
||||||
|
"Don't count on it.",
|
||||||
|
"The answer is uncertain.",
|
||||||
|
"Sorry, I wasn't paying attention. What'd you say?",
|
||||||
|
"As I see it, yes.",
|
||||||
|
"My sources say \"no\".",
|
||||||
|
"Most likely.",
|
||||||
|
"Very doubtful.",
|
||||||
|
"I don't know. Ask your mom.",
|
||||||
|
"The demon runes are quaking again. One moment.",
|
||||||
|
"Outlook good.",
|
||||||
|
"Outlook is not so good.",
|
||||||
|
"Indeed.",
|
||||||
|
"Absolutely not.",
|
||||||
|
"Yeah, we'll go with that.",
|
||||||
|
"That works.",
|
||||||
|
"Of course. What are you, stupid?",
|
||||||
|
"No. Hell no.",
|
||||||
|
"Signs say yes.",
|
||||||
|
"Aren't you a little too old to be believing that?",
|
||||||
|
"Looks good to me.",
|
||||||
|
"Sure, why not?",
|
||||||
|
"It is certain.",
|
||||||
|
"Please no. Please no. Please no.",
|
||||||
|
"Yes, please.",
|
||||||
|
"Nah.",
|
||||||
|
"Go for it!",
|
||||||
|
"Negative.",
|
||||||
|
"Obviously, dumbass.",
|
||||||
|
"I doubt it.",
|
||||||
|
"Eeeh...it's likely?",
|
||||||
|
"Forget about it.",
|
||||||
|
"Chances aren't good.",
|
||||||
|
"Ahahahahahahaha no.",
|
||||||
|
"Maybe? I think.",
|
||||||
|
"No. Die.",
|
||||||
|
"Huh? Oh, sure.",
|
||||||
|
"Yeah, right...",
|
||||||
|
"How about no.",
|
||||||
|
"Doesn't look good to me.",
|
||||||
|
"Probably.",
|
||||||
|
"Obviously not, dumbass."
|
||||||
|
};
|
||||||
|
|
||||||
|
bot.reply(usr, channel, answers[rnd.Next(0, answers.Length)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EOF
|
|
@ -0,0 +1,100 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Copyright © 2016 Project Golan
|
||||||
|
//
|
||||||
|
// See "LICENSE" for more information.
|
||||||
|
//
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Program entry point.
|
||||||
|
//
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace ProjectGolan.Vrobot3
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Program
|
||||||
|
//
|
||||||
|
public class Program
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// ProgramInfo
|
||||||
|
//
|
||||||
|
public struct ProgramInfo
|
||||||
|
{
|
||||||
|
public String googleKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// JsonConfig
|
||||||
|
//
|
||||||
|
private struct JsonConfig
|
||||||
|
{
|
||||||
|
public ProgramInfo info;
|
||||||
|
public BotInfo[] servers;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Bot> bots = new List<Bot>();
|
||||||
|
private List<Thread> threads = new List<Thread>();
|
||||||
|
public String dataDir = "../data";
|
||||||
|
public ProgramInfo info;
|
||||||
|
|
||||||
|
public static Program Instance;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Main
|
||||||
|
//
|
||||||
|
[STAThread]
|
||||||
|
public static void Main(String[] args)
|
||||||
|
{
|
||||||
|
Instance = new Program();
|
||||||
|
Instance.main(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// main
|
||||||
|
//
|
||||||
|
public void main(String[] args)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var configFile = File.ReadAllText(dataDir + "/config.json");
|
||||||
|
var config = JsonConvert.DeserializeObject<JsonConfig>(configFile);
|
||||||
|
|
||||||
|
info = config.info;
|
||||||
|
|
||||||
|
foreach(var info in config.servers)
|
||||||
|
threads.AddItem(new Thread(bots.AddItem(new Bot(info)).connect)).Start();
|
||||||
|
}
|
||||||
|
catch(Exception exc)
|
||||||
|
{
|
||||||
|
File.WriteAllText(dataDir + "/excdump.txt", exc.ToString());
|
||||||
|
Console.WriteLine("Error: {0}", exc.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// end
|
||||||
|
//
|
||||||
|
public void end()
|
||||||
|
{
|
||||||
|
foreach(var bot in bots)
|
||||||
|
try { bot.disconnect(); }
|
||||||
|
catch(Exception exc)
|
||||||
|
{
|
||||||
|
File.WriteAllText(dataDir + "/disconnectexcdump.txt",
|
||||||
|
exc.ToString());
|
||||||
|
}
|
||||||
|
bots.Clear();
|
||||||
|
threads.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EOF
|
|
@ -0,0 +1,183 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Copyright © 2016 Project Golan
|
||||||
|
//
|
||||||
|
// See "LICENSE" for more information.
|
||||||
|
//
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Useful utilities.
|
||||||
|
//
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Net;
|
||||||
|
using System.Text;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace ProjectGolan.Vrobot3
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Utils
|
||||||
|
//
|
||||||
|
public static partial class Utils
|
||||||
|
{
|
||||||
|
private static long RNDHash = 0x7f083dfd7f083dfd;
|
||||||
|
|
||||||
|
//
|
||||||
|
// List.AddItem
|
||||||
|
//
|
||||||
|
public static T AddItem<T>(this List<T> list, T item)
|
||||||
|
{
|
||||||
|
list.Add(item);
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// GetRND
|
||||||
|
//
|
||||||
|
public static Random GetRND()
|
||||||
|
{
|
||||||
|
RNDHash *= DateTime.UtcNow.ToFileTime();
|
||||||
|
Random rnd = new Random(unchecked((int)(RNDHash & 0x7fffffff)));
|
||||||
|
RNDHash ^= 0x7f8f8f8f8f8f8f8f;
|
||||||
|
RNDHash >>= 4;
|
||||||
|
RNDHash += 0x7f0000007f000000;
|
||||||
|
return rnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// GetArguments
|
||||||
|
//
|
||||||
|
public static String[] GetArguments(String msg, String help, int min,
|
||||||
|
int max = 0, char splitchr = ',')
|
||||||
|
{
|
||||||
|
char[] splitseq = { splitchr };
|
||||||
|
String[] split;
|
||||||
|
|
||||||
|
if(min == 1 && msg == String.Empty)
|
||||||
|
throw new CommandArgumentException(help);
|
||||||
|
|
||||||
|
if(max == 0)
|
||||||
|
split = msg.Split(splitseq);
|
||||||
|
else
|
||||||
|
split = msg.Split(splitseq, max);
|
||||||
|
|
||||||
|
if(min >= 0 && split.Length < min)
|
||||||
|
throw new CommandArgumentException(help);
|
||||||
|
|
||||||
|
return split;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// SetRange
|
||||||
|
//
|
||||||
|
public static Double SetRange(Double x, Double min, Double max)
|
||||||
|
=> ((max - min) * x) + min;
|
||||||
|
|
||||||
|
//
|
||||||
|
// FuzzyRelativeDate
|
||||||
|
//
|
||||||
|
public static String FuzzyRelativeDate(DateTime then, DateTime now)
|
||||||
|
{
|
||||||
|
TimeSpan span = now.Subtract(then);
|
||||||
|
|
||||||
|
if(span.Seconds == 0)
|
||||||
|
return "now";
|
||||||
|
|
||||||
|
String denom = span.Days > 0 ? "day" :
|
||||||
|
span.Hours > 0 ? "hour" :
|
||||||
|
span.Minutes > 0 ? "minute" :
|
||||||
|
"second";
|
||||||
|
|
||||||
|
int number;
|
||||||
|
switch(denom)
|
||||||
|
{
|
||||||
|
default: number = 0; break;
|
||||||
|
case "second": number = span.Seconds; break;
|
||||||
|
case "minute": number = span.Minutes; break;
|
||||||
|
case "hour": number = span.Hours; break;
|
||||||
|
case "day": number = span.Days; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return String.Format("{0} {1}{2} ago", number, denom,
|
||||||
|
number != 1 ? "s" : String.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// FuzzyRelativeDate
|
||||||
|
//
|
||||||
|
public static String FuzzyRelativeDate(DateTime then)
|
||||||
|
=> FuzzyRelativeDate(then, DateTime.Now);
|
||||||
|
|
||||||
|
//
|
||||||
|
// GetResponseString
|
||||||
|
//
|
||||||
|
public static String GetResponseString(WebResponse resp, int maxsize)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
byte[] bufp = new byte[maxsize];
|
||||||
|
int read;
|
||||||
|
|
||||||
|
using(var stream = resp.GetResponseStream())
|
||||||
|
read = stream.Read(bufp, 0, maxsize);
|
||||||
|
|
||||||
|
return Encoding.Default.GetString(bufp, 0, read);
|
||||||
|
}
|
||||||
|
catch(Exception exc)
|
||||||
|
{
|
||||||
|
Console.WriteLine("URL request error: {0}",
|
||||||
|
exc.Message ?? "[unknown]");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// GetResponseString
|
||||||
|
//
|
||||||
|
public static String GetResponseString(String uri, int maxsize,
|
||||||
|
String referer = null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var req = WebRequest.Create(uri);
|
||||||
|
|
||||||
|
if(referer != null)
|
||||||
|
{
|
||||||
|
var req_ = req as HttpWebRequest;
|
||||||
|
req_.Referer = referer;
|
||||||
|
}
|
||||||
|
|
||||||
|
using(var resp = req.GetResponse())
|
||||||
|
return GetResponseString(resp, maxsize);
|
||||||
|
}
|
||||||
|
catch(Exception exc)
|
||||||
|
{
|
||||||
|
Console.WriteLine("URL request error: {0}",
|
||||||
|
exc.Message ?? "[unknown]");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// TryParse
|
||||||
|
//
|
||||||
|
public static void TryParse(String str, String err, out double outp)
|
||||||
|
{
|
||||||
|
if(!double.TryParse(str, out outp))
|
||||||
|
throw new CommandArgumentException(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// TryParse
|
||||||
|
//
|
||||||
|
public static void TryParse(String str, String err, out int outp)
|
||||||
|
{
|
||||||
|
if(!int.TryParse(str, out outp))
|
||||||
|
throw new CommandArgumentException(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EOF
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="Discord.Net" version="0.9.6" targetFramework="net45" />
|
||||||
|
<package id="Discord.Net.Audio" version="0.9.6" targetFramework="net45" />
|
||||||
|
<package id="HtmlAgilityPack" version="1.4.9.5" targetFramework="net45" />
|
||||||
|
<package id="Newtonsoft.Json" version="9.0.2-beta1" targetFramework="net45" />
|
||||||
|
<package id="Nito.AsyncEx" version="3.0.1" targetFramework="net45" />
|
||||||
|
<package id="NtpDateTime" version="1.0.8" targetFramework="net45" />
|
||||||
|
<package id="RestSharp" version="105.2.3" targetFramework="net45" />
|
||||||
|
<package id="WebSocket4Net" version="0.14.1" targetFramework="net45" />
|
||||||
|
</packages>
|
|
@ -0,0 +1,96 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
<ProjectGuid>{83337FF3-3334-42EC-824D-532FF0C973A9}</ProjectGuid>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<RootNamespace>ProjectGolan.Vrobot3</RootNamespace>
|
||||||
|
<AssemblyName>vrobot3</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||||
|
<StartupObject>ProjectGolan.Vrobot3.Program</StartupObject>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>bin</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<Externalconsole>true</Externalconsole>
|
||||||
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<OutputPath>bin</OutputPath>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<Externalconsole>true</Externalconsole>
|
||||||
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Xml" />
|
||||||
|
<Reference Include="System.Xml.Linq" />
|
||||||
|
<Reference Include="System.Web" />
|
||||||
|
<Reference Include="Discord.Net">
|
||||||
|
<HintPath>packages/Discord.Net.0.9.6/lib/net45/Discord.Net.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Discord.Net.Audio">
|
||||||
|
<HintPath>packages/Discord.Net.Audio.0.9.6/lib/net45/Discord.Net.Audio.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="HtmlAgilityPack">
|
||||||
|
<HintPath>packages/HtmlAgilityPack.1.4.9.5/lib/Net45/HtmlAgilityPack.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Newtonsoft.Json">
|
||||||
|
<HintPath>packages/Newtonsoft.Json.9.0.2-beta1/lib/net45/Newtonsoft.Json.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="NtpDateTime">
|
||||||
|
<HintPath>packages/NtpDateTime.1.0.8/lib/NtpDateTime.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="WebSocket4Net">
|
||||||
|
<HintPath>packages/WebSocket4Net.0.14.1/lib/net45/WebSocket4Net.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="RestSharp">
|
||||||
|
<HintPath>packages/RestSharp.105.2.3/lib/net45/RestSharp.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Nito.AsyncEx.Concurrent">
|
||||||
|
<HintPath>packages/Nito.AsyncEx.3.0.1/lib/net45/Nito.AsyncEx.Concurrent.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Nito.AsyncEx">
|
||||||
|
<HintPath>packages/Nito.AsyncEx.3.0.1/lib/net45/Nito.AsyncEx.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Nito.AsyncEx.Enlightenment">
|
||||||
|
<HintPath>packages/Nito.AsyncEx.3.0.1/lib/net45/Nito.AsyncEx.Enlightenment.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="Properties/AssemblyInfo.cs" />
|
||||||
|
<Compile Include="Source/Bot.cs" />
|
||||||
|
<Compile Include="Source/BotClient.cs" />
|
||||||
|
<Compile Include="Source/BotClientDiscord.cs" />
|
||||||
|
<Compile Include="Source/BotClientIRC.cs" />
|
||||||
|
<Compile Include="Source/BotEvents.cs" />
|
||||||
|
<Compile Include="Source/BotInfo.cs" />
|
||||||
|
<Compile Include="Source/BotModule.cs" />
|
||||||
|
<Compile Include="Source/Exceptions.cs" />
|
||||||
|
<Compile Include="Source/Links.cs" />
|
||||||
|
<Compile Include="Source/Program.cs" />
|
||||||
|
<Compile Include="Source/Utils.cs" />
|
||||||
|
<Compile Include="Source/Modules/Mod_Admin.cs" />
|
||||||
|
<Compile Include="Source/Modules/Mod_Audio.cs" />
|
||||||
|
<Compile Include="Source/Modules/Mod_Fun.cs" />
|
||||||
|
<Compile Include="Source/Modules/Mod_Utils.cs" />
|
||||||
|
<Compile Include="Source/Modules/Mod_Idgames.cs" />
|
||||||
|
<Compile Include="Source/Modules/Mod_Quote.cs" />
|
||||||
|
<Compile Include="Source/Modules/Mod_Shittalk.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildBinPath)/Microsoft.CSharp.targets" />
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="packages.config" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
|
@ -0,0 +1,174 @@
|
||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio 2012
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "vrobot3", "vrobot3.csproj", "{83337FF3-3334-42EC-824D-532FF0C973A9}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|x86 = Debug|x86
|
||||||
|
Release|x86 = Release|x86
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{83337FF3-3334-42EC-824D-532FF0C973A9}.Debug|x86.ActiveCfg = Debug|x86
|
||||||
|
{83337FF3-3334-42EC-824D-532FF0C973A9}.Debug|x86.Build.0 = Debug|x86
|
||||||
|
{83337FF3-3334-42EC-824D-532FF0C973A9}.Release|x86.ActiveCfg = Release|x86
|
||||||
|
{83337FF3-3334-42EC-824D-532FF0C973A9}.Release|x86.Build.0 = Release|x86
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(MonoDevelopProperties) = preSolution
|
||||||
|
Policies = $0
|
||||||
|
$0.DotNetNamingPolicy = $1
|
||||||
|
$1.DirectoryNamespaceAssociation = None
|
||||||
|
$1.ResourceNamePolicy = FileFormatDefault
|
||||||
|
$0.NameConventionPolicy = $2
|
||||||
|
$2.Rules = $3
|
||||||
|
$3.NamingRule = $4
|
||||||
|
$4.Name = Namespaces
|
||||||
|
$4.AffectedEntity = Namespace
|
||||||
|
$4.VisibilityMask = VisibilityMask
|
||||||
|
$4.NamingStyle = PascalCase
|
||||||
|
$4.IncludeInstanceMembers = True
|
||||||
|
$4.IncludeStaticEntities = True
|
||||||
|
$3.NamingRule = $5
|
||||||
|
$5.Name = Types
|
||||||
|
$5.AffectedEntity = Class, Struct, Enum, Delegate
|
||||||
|
$5.VisibilityMask = VisibilityMask
|
||||||
|
$5.NamingStyle = PascalCase
|
||||||
|
$5.IncludeInstanceMembers = True
|
||||||
|
$5.IncludeStaticEntities = True
|
||||||
|
$3.NamingRule = $6
|
||||||
|
$6.Name = Interfaces
|
||||||
|
$6.RequiredPrefixes = $7
|
||||||
|
$7.String = I
|
||||||
|
$6.AffectedEntity = Interface
|
||||||
|
$6.VisibilityMask = VisibilityMask
|
||||||
|
$6.NamingStyle = PascalCase
|
||||||
|
$6.IncludeInstanceMembers = True
|
||||||
|
$6.IncludeStaticEntities = True
|
||||||
|
$3.NamingRule = $8
|
||||||
|
$8.Name = Attributes
|
||||||
|
$8.RequiredSuffixes = $9
|
||||||
|
$9.String = Attribute
|
||||||
|
$8.AffectedEntity = CustomAttributes
|
||||||
|
$8.VisibilityMask = VisibilityMask
|
||||||
|
$8.NamingStyle = PascalCase
|
||||||
|
$8.IncludeInstanceMembers = True
|
||||||
|
$8.IncludeStaticEntities = True
|
||||||
|
$3.NamingRule = $10
|
||||||
|
$10.Name = Event Arguments
|
||||||
|
$10.RequiredSuffixes = $11
|
||||||
|
$11.String = EventArgs
|
||||||
|
$10.AffectedEntity = CustomEventArgs
|
||||||
|
$10.VisibilityMask = VisibilityMask
|
||||||
|
$10.NamingStyle = PascalCase
|
||||||
|
$10.IncludeInstanceMembers = True
|
||||||
|
$10.IncludeStaticEntities = True
|
||||||
|
$3.NamingRule = $12
|
||||||
|
$12.Name = Exceptions
|
||||||
|
$12.RequiredSuffixes = $13
|
||||||
|
$13.String = Exception
|
||||||
|
$12.AffectedEntity = CustomExceptions
|
||||||
|
$12.VisibilityMask = VisibilityMask
|
||||||
|
$12.NamingStyle = PascalCase
|
||||||
|
$12.IncludeInstanceMembers = True
|
||||||
|
$12.IncludeStaticEntities = True
|
||||||
|
$3.NamingRule = $14
|
||||||
|
$14.Name = Methods
|
||||||
|
$14.AffectedEntity = Methods
|
||||||
|
$14.VisibilityMask = VisibilityMask
|
||||||
|
$14.NamingStyle = PascalCase
|
||||||
|
$14.IncludeInstanceMembers = True
|
||||||
|
$14.IncludeStaticEntities = True
|
||||||
|
$3.NamingRule = $15
|
||||||
|
$15.Name = Static Readonly Fields
|
||||||
|
$15.AffectedEntity = ReadonlyField
|
||||||
|
$15.VisibilityMask = Internal, Protected, Public
|
||||||
|
$15.NamingStyle = PascalCase
|
||||||
|
$15.IncludeInstanceMembers = False
|
||||||
|
$15.IncludeStaticEntities = True
|
||||||
|
$3.NamingRule = $16
|
||||||
|
$16.Name = Fields (Non Private)
|
||||||
|
$16.AffectedEntity = Field
|
||||||
|
$16.VisibilityMask = Internal, Protected, Public
|
||||||
|
$16.NamingStyle = PascalCase
|
||||||
|
$16.IncludeInstanceMembers = True
|
||||||
|
$16.IncludeStaticEntities = True
|
||||||
|
$3.NamingRule = $17
|
||||||
|
$17.Name = ReadOnly Fields (Non Private)
|
||||||
|
$17.AffectedEntity = ReadonlyField
|
||||||
|
$17.VisibilityMask = Internal, Protected, Public
|
||||||
|
$17.NamingStyle = PascalCase
|
||||||
|
$17.IncludeInstanceMembers = True
|
||||||
|
$17.IncludeStaticEntities = False
|
||||||
|
$3.NamingRule = $18
|
||||||
|
$18.Name = Fields (Private)
|
||||||
|
$18.AllowedPrefixes = $19
|
||||||
|
$19.String = _
|
||||||
|
$19.String = m_
|
||||||
|
$18.AffectedEntity = Field, ReadonlyField
|
||||||
|
$18.VisibilityMask = Private
|
||||||
|
$18.NamingStyle = AllLower
|
||||||
|
$18.IncludeInstanceMembers = True
|
||||||
|
$18.IncludeStaticEntities = False
|
||||||
|
$3.NamingRule = $20
|
||||||
|
$20.Name = Static Fields (Private)
|
||||||
|
$20.AffectedEntity = Field
|
||||||
|
$20.VisibilityMask = Private
|
||||||
|
$20.NamingStyle = AllLower
|
||||||
|
$20.IncludeInstanceMembers = False
|
||||||
|
$20.IncludeStaticEntities = True
|
||||||
|
$3.NamingRule = $21
|
||||||
|
$21.Name = ReadOnly Fields (Private)
|
||||||
|
$21.AllowedPrefixes = $22
|
||||||
|
$22.String = _
|
||||||
|
$22.String = m_
|
||||||
|
$21.AffectedEntity = ReadonlyField
|
||||||
|
$21.VisibilityMask = Private
|
||||||
|
$21.NamingStyle = AllLower
|
||||||
|
$21.IncludeInstanceMembers = True
|
||||||
|
$21.IncludeStaticEntities = False
|
||||||
|
$3.NamingRule = $23
|
||||||
|
$23.Name = Constant Fields
|
||||||
|
$23.AffectedEntity = ConstantField
|
||||||
|
$23.VisibilityMask = VisibilityMask
|
||||||
|
$23.NamingStyle = PascalCase
|
||||||
|
$23.IncludeInstanceMembers = True
|
||||||
|
$23.IncludeStaticEntities = True
|
||||||
|
$3.NamingRule = $24
|
||||||
|
$24.Name = Properties
|
||||||
|
$24.AffectedEntity = Property
|
||||||
|
$24.VisibilityMask = VisibilityMask
|
||||||
|
$24.NamingStyle = PascalCase
|
||||||
|
$24.IncludeInstanceMembers = True
|
||||||
|
$24.IncludeStaticEntities = True
|
||||||
|
$3.NamingRule = $25
|
||||||
|
$25.Name = Events
|
||||||
|
$25.AffectedEntity = Event
|
||||||
|
$25.VisibilityMask = VisibilityMask
|
||||||
|
$25.NamingStyle = PascalCase
|
||||||
|
$25.IncludeInstanceMembers = True
|
||||||
|
$25.IncludeStaticEntities = True
|
||||||
|
$3.NamingRule = $26
|
||||||
|
$26.Name = Enum Members
|
||||||
|
$26.AffectedEntity = EnumMember
|
||||||
|
$26.VisibilityMask = VisibilityMask
|
||||||
|
$26.NamingStyle = PascalCase
|
||||||
|
$26.IncludeInstanceMembers = True
|
||||||
|
$26.IncludeStaticEntities = True
|
||||||
|
$3.NamingRule = $27
|
||||||
|
$27.Name = Parameters
|
||||||
|
$27.AffectedEntity = Parameter
|
||||||
|
$27.VisibilityMask = VisibilityMask
|
||||||
|
$27.NamingStyle = AllLower
|
||||||
|
$27.IncludeInstanceMembers = True
|
||||||
|
$27.IncludeStaticEntities = True
|
||||||
|
$3.NamingRule = $28
|
||||||
|
$28.Name = Type Parameters
|
||||||
|
$28.RequiredPrefixes = $29
|
||||||
|
$29.String = T
|
||||||
|
$28.AffectedEntity = TypeParameter
|
||||||
|
$28.VisibilityMask = VisibilityMask
|
||||||
|
$28.NamingStyle = PascalCase
|
||||||
|
$28.IncludeInstanceMembers = True
|
||||||
|
$28.IncludeStaticEntities = True
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
Loading…
Reference in New Issue