Converting to Git.
Bazaar log was: ------------------------------------------------------------ revno: 6 committer: dev <dev@aninix.net> branch nick: TheRaven timestamp: Thu 2016-06-02 23:21:56 -0500 message: Updating to include dedicated raven user. Adding RavenExecute.Command() static function to handle OS integration. Updating RavenCommand to use Djinni package for mailing. ------------------------------------------------------------ revno: 5 committer: dev <dev@aninix.net> branch nick: TheRaven timestamp: Wed 2016-04-20 16:59:51 -0500 message: Updated Makefile to include OS dependencies Updated ircservermessage to fix dropped sections of searches from things like "r.image :blar:" Added new tinyURL feature -- WARNING: this requires that the OS has a script api-keys in the path that will return an API key for the TinyURL service ------------------------------------------------------------ revno: 4 committer: dev <dev@aninix.net> branch nick: TheRaven timestamp: Mon 2016-03-28 14:40:00 -0500 message: MailerCount is now added to limit the number of pages sent to admins. ------------------------------------------------------------ revno: 3 committer: ircd <ircd@aninix.net> branch nick: TheRaven timestamp: Fri 2016-01-15 14:36:54 -0600 message: Removing an unneeded file -- requests are tracked in keep.google.com now Makefile had a run rule added to run the bot in the foreground with the verbose flag. This prevents the user from accidentally compiling and installing TheRaven with higher verbosity and filling system logs. Ravencommand was modified to check for user modes r (registerd on UnrealIRCd) or G (registered on InspIRCd) before running whitelist commands. connection.csharp had functions added to check for users being authenticated (IRC response code 330 in a WHOIS request) and do get modes ------------------------------------------------------------ revno: 2 committer: root <root@aninix.net> branch nick: TheRaven timestamp: Mon 2015-12-14 14:58:02 -0600 message: Updated commenting Removed requests file in favor of keep.google.com Updated ircservermessage.csharp et. al. for better verbosity options Edited connection.csharp to not need a Raven instance and instead take host and port Updated private globals to standard Moved Verbosity enum to not be inside ReportMessage class ------------------------------------------------------------ revno: 1 committer: ircd <ircd@aninix.net> branch nick: TheRaven timestamp: Thu 2015-12-10 21:58:41 -0600 message: Adding all my files.
This commit is contained in:
parent
ace6d41661
commit
dede6245b9
36
Makefile
Normal file
36
Makefile
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
raven.mono: /usr/bin/mcs /usr/bin/mono clean ./raven.csharp ./connection.csharp ./ravencommand.csharp /bin/bash /usr/bin/mail /usr/bin/wget
|
||||||
|
mkdir -p /usr/local/etc/TheRaven
|
||||||
|
mcs -out:raven.mono reportmessage.csharp *exception.csharp irc*message.csharp connection.csharp raven*.csharp
|
||||||
|
id raven || useradd -M -G bzr,ircd,api raven
|
||||||
|
id raven || usermod -d /usr/local/etc/TheRaven raven
|
||||||
|
chown raven:raven /usr/local/etc/TheRaven
|
||||||
|
|
||||||
|
clean:
|
||||||
|
if [ "$$(ls ./*~ 2>/dev/null | wc -l)" -gt 0 ]; then rm -Rf *~; fi
|
||||||
|
if [ "$$(ls ./*.mono 2>/dev/null | wc -l)" -gt 0 ]; then rm -Rf *.mono; fi
|
||||||
|
if [ "$$(ls ./\#* 2>/dev/null | wc -l)" -gt 0 ]; then rm -Rf \#*; fi
|
||||||
|
if [ -f raven.mono ]; then rm raven.mono; fi
|
||||||
|
|
||||||
|
edit:
|
||||||
|
emacs -nw raven.csharp
|
||||||
|
|
||||||
|
test: raven.mono
|
||||||
|
su raven -c 'script -c "mono ./raven.mono -c /usr/local/etc/TheRaven-Test -v" /tmp/raven-test.log'
|
||||||
|
|
||||||
|
check-for-verbosity:
|
||||||
|
grep Console.WriteLine *.csharp | egrep -v 'verbosity|raven.csharp'; echo
|
||||||
|
|
||||||
|
install: raven.mono
|
||||||
|
cp raven.mono /opt/raven.mono
|
||||||
|
[ ! -d /usr/local/etc/TheRaven ] || mkdir -p /usr/local/etc/TheRaven
|
||||||
|
chown -R raven:raven /opt/raven.mono /usr/local/etc/TheRaven*
|
||||||
|
chmod 0600 /opt/raven.mono /usr/local/etc/TheRaven*/*
|
||||||
|
chmod 0700 /usr/local/etc/TheRaven*
|
||||||
|
cp ./raven.service /usr/lib/systemd/system/raven.service
|
||||||
|
/usr/bin/bash make-conf-dir.bash /usr/local/etc/TheRaven
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable raven
|
||||||
|
|
||||||
|
commit: /usr/bin/bzr
|
||||||
|
bzr commit
|
||||||
|
chown -R raven:bzr .bzr
|
13
alreadyidentifiedexception.csharp
Normal file
13
alreadyidentifiedexception.csharp
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace AniNIX.TheRaven {
|
||||||
|
public class AlreadyIdentifiedException : System.Exception {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new AlreadyIdentifiedException to identify this event
|
||||||
|
/// </summary>
|
||||||
|
public AlreadyIdentifiedException(String message) : base(message) { }
|
||||||
|
public AlreadyIdentifiedException() : base(null) { }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
177
connection.csharp
Normal file
177
connection.csharp
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
using System;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace AniNIX.TheRaven {
|
||||||
|
|
||||||
|
public class Connection : IDisposable {
|
||||||
|
|
||||||
|
private const int _ircReadTimeout = 200000; // We set this to the IRC mimimum of two minutes in microseconds
|
||||||
|
|
||||||
|
//These privates will be the socket we use.
|
||||||
|
private NetworkStream _networkStream = null; // This is the stream to use.
|
||||||
|
private TcpClient _tcpClient = null; // This is the TCP socket for the stream.
|
||||||
|
private StreamWriter _streamWriter = null; // This is the stream to write to
|
||||||
|
private StreamReader _streamReader = null; // This is the stream to read from
|
||||||
|
private String _host = null; // This is DNS name or IP of the host to talk to
|
||||||
|
private int _port = 0; // this is the port number to connect to
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Connect to the host, populating the socket from the configuration options
|
||||||
|
/// </summary>
|
||||||
|
public Connection(String host, int port) {
|
||||||
|
ReportMessage.Log(Verbosity.Verbose,String.Format("Connecting to host {0}...",host));
|
||||||
|
this._host = host;
|
||||||
|
this._port = port;
|
||||||
|
this._tcpClient = new TcpClient(this._host,this._port);
|
||||||
|
this._tcpClient.ReceiveTimeout = Connection._ircReadTimeout;
|
||||||
|
this._networkStream = this._tcpClient.GetStream();
|
||||||
|
this._streamWriter = new StreamWriter(this._networkStream);
|
||||||
|
this._streamReader = new StreamReader(this._networkStream);
|
||||||
|
ReportMessage.Log(Verbosity.VeryVerbose,"... Connected.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a line from the socket
|
||||||
|
/// </summary>
|
||||||
|
/// <returns> A string read from the socket </returns>
|
||||||
|
public IRCServerMessage Read() {
|
||||||
|
String response = null;
|
||||||
|
while (response == null) {
|
||||||
|
try {
|
||||||
|
response = this._streamReader.ReadLine();
|
||||||
|
} catch (IOException e) { // If the socket times out, make sure the host is still alive.
|
||||||
|
try {
|
||||||
|
IRCPongMessage pingHost = new IRCPongMessage(String.Format("PING :{0}",this._host));
|
||||||
|
Write(pingHost);
|
||||||
|
response = this._streamReader.ReadLine();
|
||||||
|
} catch (IOException f) { // If we get this, then the socket is dead and we need to signal
|
||||||
|
throw new RavenTimedOutException(String.Format("{0}\n{1}\n",e.Message,f.Message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (response != null && response.Length > 3 && response.Substring(0,4).Equals("PING")) { // if the response is a PING message, PONG and read again.
|
||||||
|
IRCPongMessage pong = new IRCPongMessage(response);
|
||||||
|
Write(pong);
|
||||||
|
response = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
IRCServerMessage readMessage = new IRCServerMessage(response);
|
||||||
|
ReportMessage.Log(Verbosity.VeryVerbose,readMessage.ToString());
|
||||||
|
return readMessage;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes a line to the socket
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="toWrite">
|
||||||
|
/// The string to write
|
||||||
|
/// </param>
|
||||||
|
public void Write(IRCMessage toWrite) {
|
||||||
|
ReportMessage.Log(Verbosity.VeryVerbose,toWrite.ToString());
|
||||||
|
this._streamWriter.WriteLine(String.Format("{0}\r\n",toWrite.GetOutgoingIRCString()));
|
||||||
|
this._streamWriter.Flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Is the user logged in?
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="userName">
|
||||||
|
/// The username to check
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// A boolean value representing whether the user is logged in or not
|
||||||
|
/// </returns>
|
||||||
|
public bool IsLoggedIn(String userName) {
|
||||||
|
ReportMessage.Log(Verbosity.VeryVerbose,String.Format("Asking for user {0} login status.",userName));
|
||||||
|
String outgoing = String.Format("WHOIS {0}\r\n",userName);
|
||||||
|
ReportMessage.Log(Verbosity.VeryVerbose,String.Format("<<< {0}",outgoing.Trim()));
|
||||||
|
this._streamWriter.WriteLine(outgoing);
|
||||||
|
this._streamWriter.Flush();
|
||||||
|
String[] bySpace;
|
||||||
|
do {
|
||||||
|
String response = this._streamReader.ReadLine();
|
||||||
|
ReportMessage.Log(Verbosity.VeryVerbose,String.Format(">>> {0}",response));
|
||||||
|
bySpace = response.Split(' ');
|
||||||
|
if (bySpace.Length > 1 && bySpace[1].Equals("330")) {
|
||||||
|
ReportMessage.Log(Verbosity.VeryVerbose,String.Format("User {0} is authenticated.",userName));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} while (bySpace.Length < 2 || !bySpace[1].Equals("318"));
|
||||||
|
ReportMessage.Log(Verbosity.VeryVerbose,String.Format("User {0} is not authenticated.",userName));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the modes for a user
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="userName">
|
||||||
|
/// the username to check
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// A string with the modes.
|
||||||
|
/// </returns>
|
||||||
|
public String GetModes(String userName) {
|
||||||
|
ReportMessage.Log(Verbosity.VeryVerbose,String.Format("Asking for user {0} mode.",userName));
|
||||||
|
String outgoing = String.Format("MODE {0}\r\n",userName);
|
||||||
|
ReportMessage.Log(Verbosity.VeryVerbose,String.Format("<<< {0}",outgoing.Trim()));
|
||||||
|
this._streamWriter.WriteLine(outgoing);
|
||||||
|
this._streamWriter.Flush();
|
||||||
|
String[] bySpace;
|
||||||
|
do {
|
||||||
|
String response = this._streamReader.ReadLine();
|
||||||
|
ReportMessage.Log(Verbosity.VeryVerbose,String.Format(">>> {0}",response));
|
||||||
|
bySpace = response.Split(' ');
|
||||||
|
if (bySpace.Length > 9 && bySpace[1].Equals("330")) {
|
||||||
|
ReportMessage.Log(Verbosity.VeryVerbose,String.Format("User {0} has modes {1}.",userName,bySpace[4]));
|
||||||
|
return bySpace[4];
|
||||||
|
}
|
||||||
|
} while (bySpace.Length < 2 || !bySpace[1].Equals("502"));
|
||||||
|
ReportMessage.Log(Verbosity.VeryVerbose,String.Format("Cannot get user modes -- not a netadmin.",userName));
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* CONNECTION NEEDS TO BE DISPOSABLE BECAUSE IT HOLDS A SOCKET */
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clean up this Connection, implementing IDisposable
|
||||||
|
/// </summary>
|
||||||
|
public void Dispose() {
|
||||||
|
Dispose(true); // Dispose of this instance
|
||||||
|
GC.SuppressFinalize(this); //The Garbage Collector doesn't need to finalize it.
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Force the GarbageCollector to Dispose if programmer does not
|
||||||
|
/// </summary>
|
||||||
|
~Connection() {
|
||||||
|
Dispose(false);
|
||||||
|
ReportMessage.Log(Verbosity.Error,"Programmer forgot to dispose of Connection. Marking for Garbage Collector");
|
||||||
|
}
|
||||||
|
|
||||||
|
// This bool indicates whether we are disposed of yet or not
|
||||||
|
bool _isDisposed = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dispose of this Connection's resources responsibly.
|
||||||
|
/// </summary>
|
||||||
|
protected virtual void Dispose(bool disposing) {
|
||||||
|
if (!this._isDisposed) { //if we haven't already disposed of this, we should.
|
||||||
|
if (disposing) {
|
||||||
|
//No managed resources for this class.
|
||||||
|
}
|
||||||
|
// Cleaning unmanaged resources
|
||||||
|
this._streamReader.Dispose();
|
||||||
|
this._streamWriter.Dispose();
|
||||||
|
this._tcpClient.Close();
|
||||||
|
this._networkStream.Dispose();
|
||||||
|
}
|
||||||
|
this._isDisposed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
exitedexception.csharp
Normal file
17
exitedexception.csharp
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace AniNIX.TheRaven {
|
||||||
|
public class RavenExitedException : System.Exception {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new RavenTimedOutException to identify this event
|
||||||
|
/// </summary>
|
||||||
|
public RavenExitedException(String message) : base(message) { }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new RavenTimedOutException to identify this event
|
||||||
|
/// </summary>
|
||||||
|
public RavenExitedException() : base(null) { }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
37
ircclientmessage.csharp
Normal file
37
ircclientmessage.csharp
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
using System;
|
||||||
|
using System.Text;
|
||||||
|
namespace AniNIX.TheRaven {
|
||||||
|
|
||||||
|
//IRC messages are a primitive data type for us to use.
|
||||||
|
public class IRCClientMessage : IRCMessage {
|
||||||
|
|
||||||
|
//No incoming string
|
||||||
|
public new string GetIncomingIRCString() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CreateCustomMessage(String ircString) {
|
||||||
|
outgoingIRCString = ircString;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void NickServIdent(String nickServPass) {
|
||||||
|
outgoingIRCString = String.Format("PRIVMSG NickServ IDENTIFY {0}",nickServPass);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CreateJoinMessage(String channel) {
|
||||||
|
outgoingIRCString = String.Format("JOIN {0}",channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CreatePartMessage(String channel) {
|
||||||
|
outgoingIRCString = String.Format("PART {0}",channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PrivMsg(String message, string destination) {
|
||||||
|
outgoingIRCString = String.Format("PRIVMSG {0} :{1}",destination,message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ActionMsg(String message, string destination) {
|
||||||
|
outgoingIRCString = String.Format("PRIVMSG {0} :\u0001ACTION {1}",destination,message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
28
ircmessage.csharp
Normal file
28
ircmessage.csharp
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace AniNIX.TheRaven {
|
||||||
|
|
||||||
|
public abstract class IRCMessage {
|
||||||
|
|
||||||
|
protected string incomingIRCString;
|
||||||
|
protected string outgoingIRCString;
|
||||||
|
|
||||||
|
public string GetOutgoingIRCString() {
|
||||||
|
return outgoingIRCString;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetIncomingIRCString() {
|
||||||
|
return incomingIRCString;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString() {
|
||||||
|
if (outgoingIRCString == null || outgoingIRCString.Length < 1) {
|
||||||
|
return String.Format(">>> {0}",incomingIRCString);
|
||||||
|
} else if (incomingIRCString == null || incomingIRCString.Length < 1) {
|
||||||
|
return String.Format("<<< {0}",outgoingIRCString);
|
||||||
|
} else {
|
||||||
|
return string.Format(">>> {1}\n<<< {0}",outgoingIRCString,incomingIRCString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
ircpongmessage.csharp
Normal file
17
ircpongmessage.csharp
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
using System;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace AniNIX.TheRaven {
|
||||||
|
|
||||||
|
public class IRCPongMessage : IRCMessage {
|
||||||
|
|
||||||
|
public IRCPongMessage(String serverString) {
|
||||||
|
incomingIRCString = String.Copy(serverString.Trim());
|
||||||
|
outgoingIRCString = String.Copy(incomingIRCString);
|
||||||
|
char[] arr = outgoingIRCString.ToCharArray();
|
||||||
|
arr[1] = 'O';
|
||||||
|
outgoingIRCString = new String(arr);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
59
ircservermessage.csharp
Normal file
59
ircservermessage.csharp
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace AniNIX.TheRaven {
|
||||||
|
|
||||||
|
//IRC messages are a primitive data type for us to use.
|
||||||
|
public class IRCServerMessage : IRCMessage {
|
||||||
|
|
||||||
|
public string user { get; protected set; }
|
||||||
|
public string server { get; protected set; }
|
||||||
|
public string msgCode { get; protected set; }
|
||||||
|
public string target { get; protected set; }
|
||||||
|
public string message { get; protected set; }
|
||||||
|
|
||||||
|
public IRCServerMessage(String serverString) {
|
||||||
|
incomingIRCString = serverString.Trim();
|
||||||
|
try {
|
||||||
|
String[] byColon = incomingIRCString.Split(':');
|
||||||
|
user = byColon[0];
|
||||||
|
String[] bySpace = byColon[1].Split(' ');
|
||||||
|
List<String> messageL = new List<String>(byColon);
|
||||||
|
messageL.RemoveAt(0);
|
||||||
|
messageL.RemoveAt(0);
|
||||||
|
message = String.Join(":",messageL.ToArray());
|
||||||
|
if (bySpace[0].Contains("!")) {
|
||||||
|
String[] byExclamation = bySpace[0].Split('!');
|
||||||
|
user = byExclamation[0];
|
||||||
|
server = byExclamation[1];
|
||||||
|
} else {
|
||||||
|
user = null;
|
||||||
|
server = bySpace[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
msgCode = bySpace[1];
|
||||||
|
target = bySpace[2];
|
||||||
|
} catch (IndexOutOfRangeException e) {
|
||||||
|
ReportMessage.Log(Verbosity.Error,String.Format("!!! Can't translate string:\n{0}",serverString,e.ToString()));
|
||||||
|
user = null;
|
||||||
|
server = null;
|
||||||
|
msgCode = null;
|
||||||
|
target = null;
|
||||||
|
message = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// There is no outgoing string.
|
||||||
|
public new string GetOutgoingIRCString() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString() {
|
||||||
|
if (Raven.verbosity == Verbosity.Explicit) {
|
||||||
|
return String.Format(">>> {0}\nUser: {1}\nServer: {2}\nmsgCode: {3}\nTarget: {4}\nMessage: {5}\n",incomingIRCString,user,server,msgCode,target,message);
|
||||||
|
} else {
|
||||||
|
return String.Format(">>> {0}",incomingIRCString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
make-conf-dir.bash
Normal file
16
make-conf-dir.bash
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#!/usr/bin/bash
|
||||||
|
|
||||||
|
mkdir -p $1
|
||||||
|
touch $1/blacklist.txt
|
||||||
|
touch $1/crowfacts.txt
|
||||||
|
touch $1/crowfacts.txt.bak
|
||||||
|
touch $1/hangman.txt
|
||||||
|
touch $1/keepalive-loginDefaults.txt
|
||||||
|
touch $1/magic8.txt
|
||||||
|
touch $1/todo.txt
|
||||||
|
touch $1/whitelist.txt
|
||||||
|
touch $1/loginDefaults.txt
|
||||||
|
touch $1/rooms.txt
|
||||||
|
touch $1/searches.txt
|
||||||
|
touch $1/helptext.txt
|
||||||
|
|
15
raven-local.service
Normal file
15
raven-local.service
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=AniNIX::Raven IRC Bot for Local
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
type=simple
|
||||||
|
ExecStart=/usr/bin/mono /opt/raven.mono -c /usr/local/etc/TheRaven-Local
|
||||||
|
ExecReload=/bin/kill -HUP $MAINPID
|
||||||
|
KillMode=process
|
||||||
|
Restart=always
|
||||||
|
User=ircd
|
||||||
|
Group=ircd
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
387
raven.csharp
Normal file
387
raven.csharp
Normal file
@ -0,0 +1,387 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace AniNIX.TheRaven {
|
||||||
|
|
||||||
|
public sealed class Raven : IDisposable {
|
||||||
|
|
||||||
|
/* These are the private globals for this instance
|
||||||
|
* They should never be accessible outside this Raven
|
||||||
|
*/
|
||||||
|
//These are the basic configuration information to be overwritten
|
||||||
|
public String Host { get; private set; } //This is the Host we are connecting to.
|
||||||
|
public int Port { get; private set; } // This is Port to connect on
|
||||||
|
private string _nick; // This is the _nickname for this Raven to use.
|
||||||
|
private string _nickServPass; // This is the password we will send to NickServ to identify
|
||||||
|
private string _autoSend; // This is the command we will automatically send to the Host
|
||||||
|
private string configDir; // This is the configuration directory.
|
||||||
|
private Connection _connection; //This is the socket to the Host
|
||||||
|
|
||||||
|
public List<String> channels = new List<String>(); //This is the list of channels to join
|
||||||
|
public List<String> whitelist = new List<String>(); //This is the list of admin users.
|
||||||
|
public List<String> blacklist = new List<String>(); // This is the list of blocked people.
|
||||||
|
public String helpText = null; // This is the text to send when people ask for help -- this is configurable to allow for skinning
|
||||||
|
public List<String> searches = new List<String>(); //These are the searches
|
||||||
|
public String[] magic8 = null; //These are the strings to return like a Magic 8-ball to questions.
|
||||||
|
public String[] crowFacts = null; //These are the possible CrowFacts
|
||||||
|
public List<String> crowFactsSubscribers = new List<String>(); //These are the subscribers to CrowFacts
|
||||||
|
public Dictionary<String,String> notifications = new Dictionary<String,String>(); // This is the notifications list for TheRaven.
|
||||||
|
|
||||||
|
public Random randomSeed = new Random(); //This is our random seed
|
||||||
|
|
||||||
|
public Dictionary<String,int> MailerCount = new Dictionary<String,int>(); // Messages may only be sent up to a maximum to the admins.
|
||||||
|
|
||||||
|
public static Verbosity verbosity { get; private set; } // This is the level to which this and all Raven instances will log.
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Show the settings used by this Raven.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A string representing this Raven</returns>
|
||||||
|
public override string ToString() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.Append("### AniNIX::TheRaven -- Running Values ###\n");
|
||||||
|
sb.Append(String.Format("Host: {0}\n",Host));
|
||||||
|
sb.Append(String.Format("Port: {0}\n",Port));
|
||||||
|
sb.Append(String.Format("Nick: {0}\n",_nick));
|
||||||
|
sb.Append("NickServPass: ****\n");
|
||||||
|
sb.Append(String.Format("Auto: {0}\n",_autoSend));
|
||||||
|
sb.Append(String.Format("Conf: {0}\n",configDir));
|
||||||
|
sb.Append(String.Format("Verbosity: {0}\n",Raven.verbosity));
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read from the files in the directory to configure this Raven
|
||||||
|
/// </summary>
|
||||||
|
// TODO: This and ParseArgs may get punted into their own static class to improve readability.
|
||||||
|
private void ConfigureSelfFromFiles(String configDir) {
|
||||||
|
|
||||||
|
if (configDir==null || configDir == "" || !Directory.Exists(configDir)) {
|
||||||
|
ReportMessage.Log(Verbosity.Error,"Configuration directory does not exist!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ReportMessage.Log(Verbosity.Always,String.Format("Reading from files in {0}...",configDir));
|
||||||
|
|
||||||
|
//These are locals that will be used throughout
|
||||||
|
ReportMessage.Log(Verbosity.Verbose,"Reading login defaults");
|
||||||
|
String[] loginDefaults = RavenConfigure.ReadLineDelimitedFile(Path.Combine(configDir,"loginDefaults.txt")).ToArray();
|
||||||
|
//We have to populate these properties fom the list explicitly
|
||||||
|
if (loginDefaults.Length < 4) {
|
||||||
|
ReportMessage.Log(Verbosity.Error,"Login defaults are incomplete. No changes made.");
|
||||||
|
} else {
|
||||||
|
Host = (Host==null) ? loginDefaults[0] : Host;
|
||||||
|
try {
|
||||||
|
Port = (Port == 0) ? Int32.Parse(loginDefaults[1]) : Port;
|
||||||
|
} catch (Exception e) {
|
||||||
|
ReportMessage.Log(Verbosity.Verbose,"Cannot parse Port.");
|
||||||
|
e.ToString();
|
||||||
|
Port = 6667;
|
||||||
|
}
|
||||||
|
_nick = (_nick == null) ? loginDefaults[2] : _nick;
|
||||||
|
_nickServPass = (_nickServPass == null) ? loginDefaults[3] : _nickServPass;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Read all the channels to join.
|
||||||
|
List<String> tempChannels = RavenConfigure.ReadLineDelimitedFile(Path.Combine(configDir,"rooms.txt"));
|
||||||
|
// Because the convention is to use # for comments, channels in the rooms.txt file do not start with a #
|
||||||
|
foreach (String channel in tempChannels) {
|
||||||
|
channels.Add(String.Format("#{0}",channel));
|
||||||
|
}
|
||||||
|
|
||||||
|
//Read the whitelist of folks allowed to execute administrative commands
|
||||||
|
whitelist = RavenConfigure.ReadLineDelimitedFile(Path.Combine(configDir,"whitelist.txt"));
|
||||||
|
|
||||||
|
//Read the blacklist of folks not allowed to do anything.
|
||||||
|
blacklist = RavenConfigure.ReadLineDelimitedFile(Path.Combine(configDir,"blacklist.txt"));
|
||||||
|
|
||||||
|
//Read the helptext
|
||||||
|
helpText = RavenConfigure.ReadFirstLineFromFile(Path.Combine(configDir,"helptext.txt"));
|
||||||
|
|
||||||
|
//Read the searches to use
|
||||||
|
searches = RavenConfigure.ReadLineDelimitedFile(Path.Combine(configDir,"searches.txt"));
|
||||||
|
|
||||||
|
//Read the Magic8 options
|
||||||
|
magic8 = RavenConfigure.ReadLineDelimitedFileToArr(Path.Combine(configDir,"magic8.txt"));
|
||||||
|
|
||||||
|
//Read the CrowFacts
|
||||||
|
crowFacts = RavenConfigure.ReadLineDelimitedFileToArr(Path.Combine(configDir,"crowfacts.txt"));
|
||||||
|
|
||||||
|
//Read the notifications
|
||||||
|
foreach (String combo in RavenConfigure.ReadLineDelimitedFileToArr(Path.Combine(configDir,"notifications.txt"))) {
|
||||||
|
String[] byPipe = combo.Split('|');
|
||||||
|
notifications.Add(String.Format("#{0}",byPipe[0]),byPipe[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parse arguments from the command line.
|
||||||
|
/// </summary>
|
||||||
|
//TODO: Move this to RavenConfigure and add struct for these configurations
|
||||||
|
public void ParseArguments(String[] args) {
|
||||||
|
if (args != null) {
|
||||||
|
for (int i = 0; i < args.Length; i++) {
|
||||||
|
ReportMessage.Log(Verbosity.Verbose,String.Format("Handling Argument {0}: {1}",i,args[i]));
|
||||||
|
switch (args[i]) {
|
||||||
|
case "-n":
|
||||||
|
if (i < args.Length-1) _nick = args[++i];
|
||||||
|
break;
|
||||||
|
case "-h":
|
||||||
|
if (i < args.Length-1) Host = args[++i];
|
||||||
|
break;
|
||||||
|
case "-p":
|
||||||
|
if (i < args.Length-1) try {
|
||||||
|
Port = Int32.Parse(args[++i]);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.ToString();
|
||||||
|
Port = 6667;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "-v":
|
||||||
|
Raven.verbosity = Verbosity.VeryVerbose;
|
||||||
|
break;
|
||||||
|
case "-q":
|
||||||
|
Raven.verbosity = Verbosity.Quiet;
|
||||||
|
break;
|
||||||
|
case "-P":
|
||||||
|
if (i < args.Length-1) _nickServPass = args[++i];
|
||||||
|
break;
|
||||||
|
//TODO: Add daemonizing?
|
||||||
|
case "-a":
|
||||||
|
if (i < args.Length-1) _autoSend = args[++i];
|
||||||
|
break;
|
||||||
|
case "--help":
|
||||||
|
//TODO Add helptext
|
||||||
|
break;
|
||||||
|
case "-c":
|
||||||
|
if (i < args.Length-1) configDir = args[++i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new Raven instance
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="args">
|
||||||
|
/// The arguments for creating the bot
|
||||||
|
/// </param>
|
||||||
|
public Raven(string[] args) {
|
||||||
|
|
||||||
|
ReportMessage.Log(Verbosity.Always,"Reading arguments...");
|
||||||
|
|
||||||
|
// If we have arguments
|
||||||
|
this.ParseArguments(args);
|
||||||
|
|
||||||
|
this.ConfigureSelfFromFiles(configDir);
|
||||||
|
|
||||||
|
ReportMessage.Log(Verbosity.VeryVerbose,"Started with these values:");
|
||||||
|
ReportMessage.Log(Verbosity.VeryVerbose,this.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a raven with default settings.
|
||||||
|
/// </summary>
|
||||||
|
public Raven(String host = "localhost", int port = 6667, String nick = "TheRaven-Guest", String nickServPass = "null", String autoSend = null, String configDir = "/usr/local/etc/TheRaven-Local", Verbosity verbosity = Verbosity.Verbose) {
|
||||||
|
this.Host = host;
|
||||||
|
Port = port;
|
||||||
|
_nick = nick;
|
||||||
|
_nickServPass = nickServPass;
|
||||||
|
_autoSend = autoSend;
|
||||||
|
this.configDir = configDir;
|
||||||
|
Raven.verbosity = verbosity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Identify to the server and join the initial channels
|
||||||
|
/// </summary>
|
||||||
|
public void IdentifySelfToServer() {
|
||||||
|
ReportMessage.Log(Verbosity.Always,"Identifying to the server");
|
||||||
|
//Read for the initial two NOTICE messages about Hostnames
|
||||||
|
IRCServerMessage response = null;
|
||||||
|
int countNotice = 0;
|
||||||
|
while (countNotice < 2) {
|
||||||
|
response = _connection.Read();
|
||||||
|
if (response.msgCode.Equals("NOTICE")) countNotice += 1;
|
||||||
|
}
|
||||||
|
ReportMessage.Log(Verbosity.VeryVerbose,"Past the notices.");
|
||||||
|
|
||||||
|
//Send USER and NICK lines to identify.
|
||||||
|
IRCClientMessage send = new IRCClientMessage();
|
||||||
|
send.CreateCustomMessage(String.Format("NICK {0}\nUSER {0} * * :{0}",_nick));
|
||||||
|
_connection.Write(send);
|
||||||
|
ReportMessage.Log(Verbosity.VeryVerbose,"USER and NICK sent");
|
||||||
|
//thanks to cfrayne for the refactor
|
||||||
|
|
||||||
|
do {
|
||||||
|
response = _connection.Read();
|
||||||
|
if (response.msgCode != null && response.msgCode.Equals("433")) throw new AlreadyIdentifiedException();
|
||||||
|
} while (response.msgCode == null || !response.msgCode.Equals("266"));
|
||||||
|
|
||||||
|
//Identify to NickServ
|
||||||
|
send.NickServIdent(_nickServPass);
|
||||||
|
_connection.Write(send);
|
||||||
|
ReportMessage.Log(Verbosity.VeryVerbose,"Identified to NickServ");
|
||||||
|
|
||||||
|
//Send the autosend
|
||||||
|
send.CreateCustomMessage(_autoSend);
|
||||||
|
_connection.Write(send);
|
||||||
|
ReportMessage.Log(Verbosity.VeryVerbose,"Sent autosend");
|
||||||
|
|
||||||
|
//Join the default channels
|
||||||
|
foreach (String channel in channels) {
|
||||||
|
if (channel != null && channel.Length > 2 && channel[0] == '#') {
|
||||||
|
send.CreateJoinMessage(channel);
|
||||||
|
_connection.Write(send);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read from the connection, and for each message act appropriately.
|
||||||
|
/// </summary>
|
||||||
|
public void LoopOnTraffic() {
|
||||||
|
ReportMessage.Log(Verbosity.Verbose,"Looping on trafffic now! We're useful!");
|
||||||
|
while (true) {
|
||||||
|
IRCServerMessage response = _connection.Read();
|
||||||
|
if (response != null && response.message != null && response.message.Length > 3 && response.message.Substring(0,2).Equals("r.")) {
|
||||||
|
RavenCommand.Respond(_connection,response,this);
|
||||||
|
} else if (response != null) {
|
||||||
|
//Try to notify the admins when a given string is found in a given channel
|
||||||
|
String result;
|
||||||
|
if (notifications.TryGetValue(response.target,out result)) {
|
||||||
|
if (response.message.Contains(result)) {
|
||||||
|
try {
|
||||||
|
RavenExecute.Command(String.Format("djinni admin \"Found {1} in {0}\"",response.target,result));
|
||||||
|
} catch (Exception e) {
|
||||||
|
ReportMessage.Log(Verbosity.Error,e.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO Implement the dialog options and link reponse
|
||||||
|
|
||||||
|
/* CROWFACTS the deserving */
|
||||||
|
|
||||||
|
if (crowFactsSubscribers.Contains(response.user) && randomSeed.Next(10) < 8) {
|
||||||
|
IRCClientMessage send = new IRCClientMessage();
|
||||||
|
int location = randomSeed.Next(crowFacts.Length);
|
||||||
|
send.PrivMsg(crowFacts[location],response.user);
|
||||||
|
_connection.Write(send);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Close the _connection
|
||||||
|
/// </summary>
|
||||||
|
private void CloseConnection() {
|
||||||
|
this._connection.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Execute the work of connecting the IRCbot to the network and handle the traffic.
|
||||||
|
/// </summaray>
|
||||||
|
/// <returns> The proper OS-level exit status -- if there are problems, return 1; else return 0 </returns>
|
||||||
|
public int Run() {
|
||||||
|
ReportMessage.Log(Verbosity.Verbose,"Beginning...");
|
||||||
|
|
||||||
|
//create a new _connection to the Host.
|
||||||
|
try {
|
||||||
|
_connection = new Connection(this.Host,this.Port);
|
||||||
|
IdentifySelfToServer();
|
||||||
|
LoopOnTraffic();
|
||||||
|
// Allow the program to exit cleanly
|
||||||
|
|
||||||
|
} catch (RavenExitedException e) {
|
||||||
|
this.CloseConnection();
|
||||||
|
this.Dispose();
|
||||||
|
ReportMessage.Log(Verbosity.Always,String.Format("Exited cleanly.\n{0}",e.Message));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
//Cleanly exit
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make a Raven disposable */
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clean up this Connection, implementing IDisposable
|
||||||
|
/// </summary>
|
||||||
|
public void Dispose() {
|
||||||
|
this.Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Force the GarbageCollector to Dispose if programmer does not
|
||||||
|
/// </summary>
|
||||||
|
~Raven() {
|
||||||
|
Dispose(false);
|
||||||
|
ReportMessage.Log(Verbosity.Error,"Programmer forgot to dispose of Raven. Marking for Garbage Collector");
|
||||||
|
}
|
||||||
|
|
||||||
|
// This bool indicates whether we have disposed of this Raven
|
||||||
|
public bool _isDisposed = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dispose of this Raven's's resources responsibly.
|
||||||
|
/// </summary>
|
||||||
|
private void Dispose(bool disposing) {
|
||||||
|
if (!_isDisposed) {
|
||||||
|
if (disposing) {
|
||||||
|
Host = null;
|
||||||
|
Port = 0;
|
||||||
|
_nickServPass = null;
|
||||||
|
_autoSend = null;
|
||||||
|
configDir = null;
|
||||||
|
whitelist = null;
|
||||||
|
blacklist = null;
|
||||||
|
magic8 = null;
|
||||||
|
crowFacts = null;
|
||||||
|
crowFactsSubscribers = null;
|
||||||
|
channels = null;
|
||||||
|
searches = null;
|
||||||
|
|
||||||
|
}
|
||||||
|
_connection.Dispose();
|
||||||
|
}
|
||||||
|
this._isDisposed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The default function
|
||||||
|
/// </summary>
|
||||||
|
static int Main(string[] args) {
|
||||||
|
Raven theRaven = new Raven(args);
|
||||||
|
ReportMessage.Log(Verbosity.Verbose,"### AniNIX::TheRaven ###");
|
||||||
|
//Continue until we cleanly exit.
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
return theRaven.Run();
|
||||||
|
//If we are already identified, we're done.
|
||||||
|
} catch (AlreadyIdentifiedException e) {
|
||||||
|
ReportMessage.Log(Verbosity.Error,"There is already a Raven on this Host.");
|
||||||
|
ReportMessage.Log(Verbosity.Error,e.Message);
|
||||||
|
return 0;
|
||||||
|
// Timeouts should result in a respawn
|
||||||
|
} catch (RavenTimedOutException e) {
|
||||||
|
ReportMessage.Log(Verbosity.Always,"Connection timed out. Respawning");
|
||||||
|
ReportMessage.Log(Verbosity.Verbose,e.Message);
|
||||||
|
continue;
|
||||||
|
//If an exception gets here, something went wrong
|
||||||
|
} catch (Exception e) {
|
||||||
|
ReportMessage.Log(Verbosity.Error,"Unexpected exception caught!");
|
||||||
|
ReportMessage.Log(Verbosity.Error,e.ToString());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
BIN
raven.mono
Executable file
BIN
raven.mono
Executable file
Binary file not shown.
14
raven.service
Normal file
14
raven.service
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=AniNIX::Raven IRC Bot for ACWiki
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
ExecStart=/usr/bin/mono /opt/raven.mono -c /usr/local/etc/TheRaven
|
||||||
|
ExecReload=/bin/kill -HUP $MAINPID
|
||||||
|
KillMode=process
|
||||||
|
Restart=always
|
||||||
|
User=raven
|
||||||
|
Group=raven
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
326
ravencommand.csharp
Normal file
326
ravencommand.csharp
Normal file
@ -0,0 +1,326 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace AniNIX.TheRaven {
|
||||||
|
|
||||||
|
public static class RavenCommand {
|
||||||
|
|
||||||
|
// This is the string we return when people poorly format a command.
|
||||||
|
public static String helpString = "Bad formatting! Type r.help for help.";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This is a contextual match of commands with actions for the bot to take.
|
||||||
|
/// This will be a very long function, but watch for the /* HEADER */ sections to subdivide it.
|
||||||
|
/// <summary>
|
||||||
|
/// <param name=connection>The socket being used represented by a Connection</param>
|
||||||
|
/// <param name=incoming>The message from the IRC server to respond to</param>
|
||||||
|
/// <param name=theRaven>The Raven instance -- we use this to update various lists</param>
|
||||||
|
public static void Respond(Connection connection,IRCServerMessage incoming, Raven theRaven) {
|
||||||
|
|
||||||
|
IRCClientMessage send = new IRCClientMessage();
|
||||||
|
|
||||||
|
/* CANCEL ON BLACKLIST */
|
||||||
|
if (theRaven.blacklist.Contains(incoming.user)) { return; } // Blacklisted people can't do anything.
|
||||||
|
|
||||||
|
//Splits placed here for performance reasons
|
||||||
|
String[] bySpace = incoming.message.Split(' ');
|
||||||
|
String command = bySpace[0];
|
||||||
|
|
||||||
|
/* IDENTIFY the command and IRCClientMessage to send */
|
||||||
|
|
||||||
|
/* COMMON Commands everyone can use */
|
||||||
|
switch (command) {
|
||||||
|
case "r.raven":
|
||||||
|
send.ActionMsg("quoth, \"Nevermore!\"",incoming.target);
|
||||||
|
connection.Write(send);
|
||||||
|
return;
|
||||||
|
case "r.help":
|
||||||
|
send.PrivMsg(theRaven.helpText,incoming.target);
|
||||||
|
connection.Write(send);
|
||||||
|
return;
|
||||||
|
case "r.magic8":
|
||||||
|
if (theRaven.magic8 == null) {
|
||||||
|
send.PrivMsg("Magic8 not loaded",incoming.target);
|
||||||
|
} else {
|
||||||
|
int location = theRaven.randomSeed.Next(theRaven.magic8.Length);
|
||||||
|
send.PrivMsg(theRaven.magic8[location],incoming.target);
|
||||||
|
}
|
||||||
|
connection.Write(send);
|
||||||
|
return;
|
||||||
|
case "r.tinyurl":
|
||||||
|
if (bySpace.Length < 2) {
|
||||||
|
send.PrivMsg(theRaven.helpText,incoming.user);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
send.PrivMsg(RavenExecute.Command(String.Format("wget -q -O - \"http://tiny-url.info/api/v1/create?format=text&apikey=\"$(api-keys tinyurl)\"&provider=x_co&url={0}\"",bySpace[1])),incoming.target);
|
||||||
|
} catch (Exception e) {
|
||||||
|
ReportMessage.Log(Verbosity.Error,e.ToString());
|
||||||
|
send.PrivMsg("TinyURL error. Could not get link.",incoming.target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
connection.Write(send);
|
||||||
|
return;
|
||||||
|
case "r.msg":
|
||||||
|
if (bySpace.Length < 2) {
|
||||||
|
send.PrivMsg(theRaven.helpText,incoming.user);
|
||||||
|
} else {
|
||||||
|
if (!theRaven.MailerCount.ContainsKey(incoming.user)) theRaven.MailerCount.Add(incoming.user,0);
|
||||||
|
if (theRaven.MailerCount[incoming.user] >= 5) {
|
||||||
|
send.PrivMsg("You cannot send more than 5 messages to the admins between resets. This has been logged.",incoming.user);
|
||||||
|
connection.Write(send);
|
||||||
|
ReportMessage.Log(Verbosity.Error,String.Format("!!! {0} tried to send a message but couldn't -- attempts exceeded.",incoming.user));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
theRaven.MailerCount[incoming.user] = theRaven.MailerCount[incoming.user] + 1;
|
||||||
|
//Try to append the message to the log
|
||||||
|
StringBuilder sb = new StringBuilder(bySpace[1]);
|
||||||
|
for (int i = 2; i < bySpace.Length; i++) {
|
||||||
|
sb.Append(" ");
|
||||||
|
sb.Append(bySpace[i]);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
StreamWriter writer = new StreamWriter("/var/log/r.msg.log",true);
|
||||||
|
writer.Write(String.Format("{0} - {1} left a message on {2} in {3}\n{4}\n\n",DateTime.Now,incoming.user,theRaven.Host,incoming.target,sb.ToString()));
|
||||||
|
writer.Close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
ReportMessage.Log(Verbosity.Error,e.Message);
|
||||||
|
ReportMessage.Log(Verbosity.Error,"Make sure user raven can write to /var/log/r.msg.log");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
RavenExecute.Command(String.Format("djinni admin \"Page from {0}\"",incoming.user));
|
||||||
|
send.PrivMsg("Sent!",incoming.user);
|
||||||
|
} catch (Exception e) {
|
||||||
|
ReportMessage.Log(Verbosity.Error,e.ToString());
|
||||||
|
send.PrivMsg("Mailer error. Could not send.",incoming.user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
connection.Write(send);
|
||||||
|
return;
|
||||||
|
case "r.uptime":
|
||||||
|
try {
|
||||||
|
send.PrivMsg(RavenExecute.Command("uptime"),incoming.target);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.ToString();
|
||||||
|
send.PrivMsg("Can't get uptime",incoming.target);
|
||||||
|
}
|
||||||
|
connection.Write(send);
|
||||||
|
return;
|
||||||
|
case "r.heartbeat":
|
||||||
|
try {
|
||||||
|
String[] byLine = RavenExecute.Command("heartbeat-client").Split('\n');
|
||||||
|
for (int i = 0; i < byLine.Length; i++) {
|
||||||
|
send.PrivMsg(byLine[i],incoming.user);
|
||||||
|
connection.Write(send);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.ToString();
|
||||||
|
send.PrivMsg("Can't get heartbeat",incoming.user);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SEARCHES */
|
||||||
|
foreach (String search in theRaven.searches) {
|
||||||
|
if (search == null) continue;
|
||||||
|
String[] byPipe = search.Split('|');
|
||||||
|
if (byPipe.Length < 3) {
|
||||||
|
ReportMessage.Log(Verbosity.Error,String.Format("Incomplete search: {0}",search));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (byPipe[0].Equals(command)) {
|
||||||
|
send.PrivMsg(FormatSearch(byPipe[1],incoming.message,byPipe[2]),incoming.target);
|
||||||
|
connection.Write(send);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ADMIN commands are allowed to whitelisted users */
|
||||||
|
if (theRaven.whitelist.Contains(incoming.user) && connection.IsLoggedIn(incoming.user)) switch (command) {
|
||||||
|
case "r.quit":
|
||||||
|
send.CreateCustomMessage("QUIT :Caw, caw, caw!");
|
||||||
|
connection.Write(send);
|
||||||
|
throw new RavenExitedException();
|
||||||
|
case "r.cf":
|
||||||
|
if (bySpace.Length < 2) {
|
||||||
|
send.PrivMsg("This is the CrowFacts list of subscribers:",incoming.user);
|
||||||
|
connection.Write(send);
|
||||||
|
foreach (String user in theRaven.crowFactsSubscribers) {
|
||||||
|
send.PrivMsg(user,incoming.user);
|
||||||
|
connection.Write(send);
|
||||||
|
}
|
||||||
|
send.PrivMsg("End subscribers",incoming.user);
|
||||||
|
|
||||||
|
send.PrivMsg(theRaven.helpText,incoming.target);
|
||||||
|
} else if (theRaven.crowFacts == null) {
|
||||||
|
send.PrivMsg("CrowFacts not loaded.",incoming.target);
|
||||||
|
} else {
|
||||||
|
if (!theRaven.crowFactsSubscribers.Contains(bySpace[1])) {
|
||||||
|
theRaven.crowFactsSubscribers.Add(bySpace[1]);
|
||||||
|
send.PrivMsg(String.Format("{0} has been subscribed to CrowFacts!",bySpace[1]),incoming.target);
|
||||||
|
} else {
|
||||||
|
send.PrivMsg("Subscriber already added",incoming.target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
connection.Write(send);
|
||||||
|
return;
|
||||||
|
case "r.us":
|
||||||
|
if (bySpace.Length < 2) {
|
||||||
|
send.PrivMsg(theRaven.helpText,incoming.target);
|
||||||
|
} else {
|
||||||
|
if (theRaven.crowFactsSubscribers.Contains(bySpace[1])) {
|
||||||
|
theRaven.crowFactsSubscribers.Remove(bySpace[1]);
|
||||||
|
send.PrivMsg(String.Format("{0} has been unsubscribed.",bySpace[1]),incoming.target);
|
||||||
|
} else {
|
||||||
|
send.PrivMsg("No such subscriber",incoming.target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
connection.Write(send);
|
||||||
|
return;
|
||||||
|
case "r.join":
|
||||||
|
if (bySpace.Length < 2) {
|
||||||
|
send.PrivMsg(theRaven.helpText,incoming.target);
|
||||||
|
} else {
|
||||||
|
if (!theRaven.channels.Contains(bySpace[1])) {
|
||||||
|
theRaven.channels.Add(bySpace[1]);
|
||||||
|
send.CreateJoinMessage(bySpace[1]);
|
||||||
|
} else {
|
||||||
|
send.PrivMsg("Already joined channel",incoming.target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
connection.Write(send);
|
||||||
|
return;
|
||||||
|
case "r.part":
|
||||||
|
if (bySpace.Length < 2) {
|
||||||
|
send.PrivMsg(theRaven.helpText,incoming.target);
|
||||||
|
} else {
|
||||||
|
if (theRaven.channels.Contains(bySpace[1])) {
|
||||||
|
theRaven.channels.Remove(bySpace[1]);
|
||||||
|
send.CreatePartMessage(bySpace[1]);
|
||||||
|
} else {
|
||||||
|
send.PrivMsg("No such channel",incoming.target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
connection.Write(send);
|
||||||
|
return;
|
||||||
|
case "r.say":
|
||||||
|
if (bySpace.Length < 3 || !bySpace[1].StartsWith("#")) {
|
||||||
|
send.PrivMsg(theRaven.helpText,incoming.target);
|
||||||
|
} else {
|
||||||
|
StringBuilder newMsg = new StringBuilder(bySpace[2]);
|
||||||
|
for (int i = 3; i < bySpace.Length; i++) {
|
||||||
|
newMsg.Append(" ");
|
||||||
|
newMsg.Append(bySpace[i]);
|
||||||
|
}
|
||||||
|
send.PrivMsg(newMsg.ToString(),bySpace[1]);
|
||||||
|
}
|
||||||
|
connection.Write(send);
|
||||||
|
return;
|
||||||
|
case "r.act":
|
||||||
|
if (bySpace.Length < 3 || !bySpace[1].StartsWith("#")) {
|
||||||
|
send.PrivMsg(theRaven.helpText,incoming.target);
|
||||||
|
} else {
|
||||||
|
StringBuilder newAction = new StringBuilder(bySpace[2]);
|
||||||
|
for (int i = 3; i < bySpace.Length; i++) {
|
||||||
|
newAction.Append(" ");
|
||||||
|
newAction.Append(bySpace[i]);
|
||||||
|
}
|
||||||
|
send.ActionMsg(newAction.ToString(),bySpace[1]);
|
||||||
|
}
|
||||||
|
connection.Write(send);
|
||||||
|
return;
|
||||||
|
case "r.whitelist":
|
||||||
|
if (bySpace.Length < 2) {
|
||||||
|
send.PrivMsg("This is the whitelist:",incoming.user);
|
||||||
|
connection.Write(send);
|
||||||
|
foreach (String user in theRaven.whitelist) {
|
||||||
|
send.PrivMsg(user,incoming.user);
|
||||||
|
connection.Write(send);
|
||||||
|
}
|
||||||
|
send.PrivMsg("End whitelist",incoming.user);
|
||||||
|
} else {
|
||||||
|
if (!theRaven.whitelist.Contains(bySpace[1]) && !theRaven.blacklist.Contains(bySpace[1])) {
|
||||||
|
theRaven.whitelist.Add(bySpace[1]);
|
||||||
|
send.PrivMsg(String.Format("{0} has been whitelisted.",bySpace[1]),incoming.user);
|
||||||
|
} else {
|
||||||
|
send.PrivMsg("Already whitelisted or is a blacklisted user",incoming.user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
connection.Write(send);
|
||||||
|
return;
|
||||||
|
case "r.blacklist":
|
||||||
|
if (bySpace.Length < 2) {
|
||||||
|
send.PrivMsg("This is the blacklist:",incoming.user);
|
||||||
|
connection.Write(send);
|
||||||
|
foreach (String user in theRaven.blacklist) {
|
||||||
|
send.PrivMsg(user,incoming.user);
|
||||||
|
connection.Write(send);
|
||||||
|
}
|
||||||
|
send.PrivMsg("End blacklist",incoming.user);
|
||||||
|
} else {
|
||||||
|
if (!theRaven.blacklist.Contains(bySpace[1]) && !theRaven.whitelist.Contains(bySpace[1])) {
|
||||||
|
theRaven.blacklist.Add(bySpace[1]);
|
||||||
|
send.PrivMsg(String.Format("{0} has been blacklisted.",bySpace[1]),incoming.user);
|
||||||
|
} else {
|
||||||
|
send.PrivMsg("Already blacklisted or is an admin user",incoming.user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
connection.Write(send);
|
||||||
|
return;
|
||||||
|
case "r.greylist":
|
||||||
|
if (bySpace.Length < 2) {
|
||||||
|
send.PrivMsg(theRaven.helpText,incoming.target);
|
||||||
|
} else {
|
||||||
|
if (theRaven.whitelist.Contains(bySpace[1])) {
|
||||||
|
theRaven.whitelist.Remove(bySpace[1]);
|
||||||
|
send.PrivMsg("User cleared from whitelist",incoming.user);
|
||||||
|
} else if (theRaven.blacklist.Contains(bySpace[1])) {
|
||||||
|
theRaven.blacklist.Remove(bySpace[1]);
|
||||||
|
send.PrivMsg("User cleared from blacklist",incoming.user);
|
||||||
|
} else {
|
||||||
|
send.PrivMsg("No action needed",incoming.user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
connection.Write(send);
|
||||||
|
return;
|
||||||
|
case "r.adminhelp":
|
||||||
|
send.PrivMsg("r.adminhelp, r.cf to crowfacts, r.us to unsubscribe, r.whitelist to make user bot admin, r.blacklist to block user, r.greylist to pull user off *list, r.say [channel] [message], r.join a channel, r.part a channel, r.quit to quit",incoming.user);
|
||||||
|
connection.Write(send);
|
||||||
|
return;
|
||||||
|
// Commenting because I believe this to be unneeded
|
||||||
|
// TODO Reevaluate
|
||||||
|
/* case "r.ident":
|
||||||
|
theRaven.IdentifySelfToServer();
|
||||||
|
return;*/
|
||||||
|
case "r.mailerreset":
|
||||||
|
theRaven.MailerCount = new Dictionary<String,int>();
|
||||||
|
send.PrivMsg("Mailer counts have been reset",incoming.user);
|
||||||
|
connection.Write(send);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This function returns a search string for the user to browse.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name=searchBase> this is the base of the search URL</param>
|
||||||
|
/// <param name=request> This is the string requested to be recombined.</param>
|
||||||
|
/// <param name=junction>This is the character to use to combine the search arguments</param>
|
||||||
|
public static String FormatSearch(String searchBase,String request,String junction) {
|
||||||
|
StringBuilder formattedString = new StringBuilder(searchBase);
|
||||||
|
String[] elements = request.Split(' ');
|
||||||
|
if (elements.Length < 2) return helpString;
|
||||||
|
formattedString.Append(elements[1]); //First element is the command
|
||||||
|
for (int i = 2; i < elements.Length; i++) {
|
||||||
|
formattedString.Append(junction);
|
||||||
|
formattedString.Append(elements[i]);
|
||||||
|
}
|
||||||
|
return formattedString.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
65
ravenconfigure.csharp
Normal file
65
ravenconfigure.csharp
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace AniNIX.TheRaven {
|
||||||
|
|
||||||
|
public static class RavenConfigure {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new list from the line-delimited entries in a file
|
||||||
|
/// </summary>
|
||||||
|
/// <param name=filename> the file to read </param>
|
||||||
|
/// <returns> A List of Strings containing the lines.</returns>
|
||||||
|
public static List<String> ReadLineDelimitedFile(String filename) {
|
||||||
|
String line = null;
|
||||||
|
int count = 0;
|
||||||
|
//Read all the file to join.
|
||||||
|
ReportMessage.Log(Verbosity.Verbose,String.Format("Reading {0}",filename)); //Path.GetFileName(filename)));
|
||||||
|
List<String> newEntries = new List<String>();
|
||||||
|
StreamReader fileReader = new StreamReader(filename);
|
||||||
|
line = fileReader.ReadLine();
|
||||||
|
while (line != null) {
|
||||||
|
if (line.Length < 1) {
|
||||||
|
line = fileReader.ReadLine();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
line = line.Trim();
|
||||||
|
if (line[0] == '#') {
|
||||||
|
line = fileReader.ReadLine();
|
||||||
|
continue;
|
||||||
|
} //Skip lines starting with a #
|
||||||
|
String[] byHash = line.Split('#'); //Ignore everything after a #
|
||||||
|
newEntries.Add(byHash[0]);
|
||||||
|
count++;
|
||||||
|
ReportMessage.Log(Verbosity.VeryVerbose,String.Format("Added entry {0} from {1}",line,Path.GetFileName(filename)));
|
||||||
|
line = fileReader.ReadLine();
|
||||||
|
}
|
||||||
|
fileReader.Close();
|
||||||
|
ReportMessage.Log(Verbosity.VeryVerbose,String.Format("Found {0} newEntries.",newEntries.Count));
|
||||||
|
return newEntries;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the String[] of lines in a file -- use this for random performance
|
||||||
|
/// </summary>
|
||||||
|
/// <param name=filename> the file to read </param>
|
||||||
|
/// <returns> A String[] </returns>
|
||||||
|
public static String[] ReadLineDelimitedFileToArr(String filename) {
|
||||||
|
return RavenConfigure.ReadLineDelimitedFile(filename).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read the first line from a file -- this is useful for allowing configuration of single strings.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name=filename> the file to read </param>
|
||||||
|
/// <returns>The first line as a String</returns>
|
||||||
|
public static String ReadFirstLineFromFile(String filename) {
|
||||||
|
StreamReader fileReader = new StreamReader(filename);
|
||||||
|
String readString = fileReader.ReadLine();
|
||||||
|
fileReader.Close();
|
||||||
|
return readString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
58
ravenexecute.csharp
Normal file
58
ravenexecute.csharp
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace AniNIX.TheRaven {
|
||||||
|
|
||||||
|
public static class RavenExecute {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This method allows TheRaven to execute a command on the OS.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name=command>The command string to run as the string argument to "bash -c 'command'"</param>
|
||||||
|
/// <param name=input>The effective replacement for the command's stdin</param
|
||||||
|
/// <return>The stdout of the command</return>
|
||||||
|
/// </summary>
|
||||||
|
public static String Command(String command, String input) {
|
||||||
|
//Sanitize inputs.
|
||||||
|
if (command.Contains("\'")) {
|
||||||
|
throw new Exception("Command strings cannot include \'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
//Create process.
|
||||||
|
Process proc = new Process();
|
||||||
|
proc.StartInfo.CreateNoWindow = true;
|
||||||
|
proc.StartInfo.FileName = "/bin/bash";
|
||||||
|
proc.StartInfo.Arguments = String.Format("-c \'{0}\'",command);
|
||||||
|
proc.StartInfo.UseShellExecute=false;
|
||||||
|
|
||||||
|
//Redirect input
|
||||||
|
proc.StartInfo.RedirectStandardOutput=true;
|
||||||
|
proc.StartInfo.RedirectStandardInput=true;
|
||||||
|
|
||||||
|
//Start process
|
||||||
|
proc.Start();
|
||||||
|
|
||||||
|
//Add input and read output.
|
||||||
|
proc.StandardInput.Write(input);
|
||||||
|
proc.StandardInput.Close();
|
||||||
|
proc.WaitForExit();
|
||||||
|
if (proc.ExitCode != 0) {
|
||||||
|
throw new Exception(String.Format("Failed to exit command with return code {0}",proc.ExitCode));
|
||||||
|
}
|
||||||
|
String stdoutString = proc.StandardOutput.ReadToEnd();
|
||||||
|
|
||||||
|
//Close up and return
|
||||||
|
proc.Close();
|
||||||
|
return stdoutString;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Add polymorphism to allow no stdin
|
||||||
|
public static String Command(String command) {
|
||||||
|
return Command(command,null);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
45
reportmessage.csharp
Normal file
45
reportmessage.csharp
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace AniNIX.TheRaven {
|
||||||
|
|
||||||
|
public enum Verbosity {
|
||||||
|
Always = -2,
|
||||||
|
Error,
|
||||||
|
Quiet = 0,
|
||||||
|
Verbose,
|
||||||
|
VeryVerbose,
|
||||||
|
Explicit,
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ReportMessage {
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Log a new message for the user.
|
||||||
|
/// </summary>
|
||||||
|
public static void Log(Verbosity level,String message) {
|
||||||
|
|
||||||
|
if (level == Verbosity.Error) {
|
||||||
|
Console.Error.WriteLine(message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Raven.verbosity == Verbosity.Quiet) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (level == Verbosity.Always
|
||||||
|
|| (Raven.verbosity == Verbosity.Verbose && level == Verbosity.Verbose)
|
||||||
|
|| (Raven.verbosity == Verbosity.VeryVerbose && (level == Verbosity.Verbose || level == Verbosity.VeryVerbose))
|
||||||
|
|| (Raven.verbosity == Verbosity.Explicit && (level == Verbosity.Verbose || level == Verbosity.VeryVerbose || level == Verbosity.Explicit))
|
||||||
|
) {
|
||||||
|
Console.WriteLine(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Log(String message) {
|
||||||
|
Log(Verbosity.VeryVerbose,message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
timedoutexception.csharp
Normal file
12
timedoutexception.csharp
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace AniNIX.TheRaven {
|
||||||
|
public class RavenTimedOutException : System.Exception {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new RavenTimedOutException to identify this event
|
||||||
|
/// </summary>
|
||||||
|
public RavenTimedOutException(String message) : base(message) { }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user