simpler newmont

This commit is contained in:
Neale Pickett 2012-11-16 12:46:16 -07:00
parent ed125f385b
commit 8dcfc5deb7
3 changed files with 80 additions and 99 deletions

98
README
View File

@ -1,22 +1,18 @@
bot
===
This is a simple C program to assist writing an IRC bot in whatever language(s)
you choose. It is based on the Unix principle that one program should do one
thing.
This framework gives you plenty of opportunity to shoot yourself in the foot.
If you're not a seasoned programmer, please stick to a language like Python or
Lua for your handler. Leave the arcane trivia of proper Bourne Shell quoting
rules to the seasoned experts who've learned the hard way how to do it.
This is a simple C program to assist writing an IRC bot in whatever
language(s) you choose. It is based on the Unix principle that one
program should do one thing.
Getting Started Quickly
=======================
The example handler script, called `newmont`, will get you started right away.
It will set its nickname to `newmont`, join the channel `#newmont`, and respond
to any channel message containing the substring "strawberry".
The example handler script, called `newmont`, will get you started right
away. It will set its nickname to `newmont`, join the channel
`#newmont`, and respond to any channel message containing the substring
"strawberry".
Start it like so:
@ -32,66 +28,74 @@ There are three pieces involved.
tcpclient
---------
`tcpclient' is a program that connects to a TCP port, sets file descriptors 6
and 7 to be the input and output channels to that connection, and hands off
control to whatever program you specify (in the example above, the `./bot`
program with argument `./newmont`). There also exist a `udpclient`,
`sslclient`, and probably others. The advantage to this method is that
your network client doesn't have to care about the transport mechanism:
TCP, UDP, TCP6, SSL, or whatever else.
`tcpclient' is a program that connects to a TCP port, sets file
descriptors 6 and 7 to be the input and output channels to that
connection, and hands off control to whatever program you specify (in
the example above, the `./bot` program with argument `./newmont`).
There also exist a `udpclient`, `sslclient`, and probably others. The
advantage to this method is that your network client doesn't have to
care about the transport mechanism: TCP, UDP, TCP6, SSL, or whatever
else.
bot
---
`bot` reads one line at a time from fd6 (or 0 as a fallback), parses it up,
forks, sets some environment variables, and runs the "handler" program provided
as the first argument. Whatever that program prints to stdout is sent back to
the server, verbatim. As a convenience, it automatically responds to PING
messages from the server. It can also rate-limit messages to the server, so
your bot doesn't flood itself off IRC. Lastly, it can monitor a directory and
send the contents of any new file to the server, deleting the file after. This
allows you to write to IRC from a cron job, git post-update hook, or whatever
else you dream up.
`bot` reads one line at a time from fd6 (or 0 as a fallback), parses it
up, forks, sets some environment variables, and runs the "handler"
program provided as the first argument. Whatever that program prints to
stdout is sent back to the server, verbatim. As a convenience, it
automatically responds to PING messages from the server. It can also
rate-limit messages to the server, so your bot doesn't flood itself off
IRC. Lastly, it can monitor a directory and send the contents of any
new file to the server, deleting the file after. This allows you to
write to IRC from a cron job, git post-update hook, or whatever else you
dream up.
`bot` sets the following environment variables:
prefix IRC line prefix: you probably don't care about this
prefix you probably don't care about this
command IRC command or numeric
sender nickname to send "private" replies to this message
forum nickname to send "public" replies to this message
text text of this message, like what's sent to the channel
text command text, like what's sent to the channel
Any additional parameters of the message, like with the MODE command, are
passed in as arguments to the handler.
Any additional parameters of the message, like with the MODE command,
are passed in as arguments to the handler.
handler
-------
The handler is launched once for each incoming message. It should decide
what to do based on the environment variables and argv, possibly writing
something to stdout, and then it should exit.
The handler is launched once for each incoming message. It should
decide what to do based on the environment variables and argv, possibly
writing something to stdout, and then it should exit.
Handlers are launched in parallel to each other. IRC is an asynchronous
protocol, and while messages do tend to arrive in a particular order, don't
count on it, especially with this framework.
protocol, and while messages do tend to arrive in a particular order,
don't count on it, especially with this framework.
`newmont` is a very simple handler script to reply to any PRIVMSG with the
substring "strawberry", in the (public) forum it was sent.
`newmont` is a very simple handler script to reply to any PRIVMSG with
the substring "strawberry", in the (public) forum it was sent.
Caution
=======
Your handler is getting input provided by a potentially malicious adversary.
If you're not careful, you could create a remote exploit: a path through your
handler script that allows anyone on IRC to do whatever they want on your
local computer.
Your handler is getting input provided by a potentially malicious
adversary. If you're not careful, you could create a remote exploit: a
path through your handler script that allows anyone on IRC to do
whatever they want on your local computer.
You can write handlers in bourne shell: it's really easy. It's equally as
easy to accidentally allow remote control. There's nothing I can do in the
code I provide to prevent you from really hurting yourself, all I can do is
warn you.
You can write handlers in bourne shell: it's really easy. It's equally
as easy to accidentally allow remote control. There's nothing I can do
in my code I provide to prevent you from creating a remote exploit, all
I can do is warn you.
If you're not confident in your mastery of bourne shell quoting rules,
you should build off of the provided lua example, which will make it
much more difficult to accidentally create an exploit. Or create a new
handler in Python, Ruby, etc.
@ -100,5 +104,3 @@ Author
Neale Pickett <neale@woozle.org>

11
bot.c
View File

@ -22,6 +22,7 @@
#define max(a,b) ((a)>(b)?(a):(b))
bool running = true;
char *handler = NULL;
char *msgdir = NULL;
struct timeval output_interval = {0};
@ -295,6 +296,9 @@ void
handle_input()
{
handle_file(stdin, dispatch);
if (feof(stdin)) {
running = false;
}
}
void
@ -462,11 +466,14 @@ main(int argc, char *argv[])
signal(SIGCHLD, sigchld);
// Let handler know we're starting up
dispatch("INIT");
dispatch("_INIT_");
while (1) {
while (running) {
loop();
}
// Let handler know we're shutting down
dispatch("_END_");
return 0;
}

View File

@ -1,70 +1,42 @@
#! /usr/bin/lua
--
-- Set global variables from environment
-- A very simple bot which will join IRC, join #newmont, and
-- respond to any messages with "strawberry" in them. It also
-- has some naïve nickname collision avoidance.
--
-- This is a good place to start if you're not going to write
-- your handler in lua. If you *do* want to use lua, you should
-- take a look at bot.lua instead.
--
prefix = os.getenv("prefix")
forum = os.getenv("forum")
sender = os.getenv("sender")
command = os.getenv("command")
text = os.getenv("text")
--
-- Write text to stderr (for debugging)
--
function log(text)
io.stderr:write(text .. "\n")
end
io.stderr:write(">>> [" .. command .. "] " ..
(sender or "-") .. "/" ..
(forum or "-") .. " " ..
(text or "") .. "\n")
--
-- Send a raw IRC command to the server
--
function raw(text)
log("< " .. text)
print(text)
end
--
-- Send a message to the forum; if we've sent 4 lines
-- already, start sending directly to the sender, to
-- avoid spamming channels.
--
msgs_sent = 0
msg_recip = forum
function msg(text)
msgs_sent = msgs_sent + 1
if ((msgs_sent == 5) and (forum ~= sender)) then
raw("PRIVMSG " .. forum .. " :Sending the rest in private")
msg_recip = sender
end
raw("PRIVMSG " .. msg_recip .. " :" .. text)
end
--
--
-- Main program
--
--
-- Log what we got
log(" > " .. (prefix or "") .. " " .. (sender or "-") .. "/" .. (forum or "-") .. " [" .. command .. "] :" .. (text or ""))
-- Our action depends on what the command is
if (command == "INIT") then
-- Our behavior depends on what the command is
if (command == "_INIT_") then
-- bot sends this when it first starts up, so we can log in
raw("NICK nemont")
raw("USER newmont newmont newmont :Sample bot")
print("NICK nemont")
print("USER newmont newmont newmont :Sample bot")
elseif (command == "433") then
-- Couldn't get the nickname we asked for
raw("NICK bot_" .. (os.time() % 500))
print("NICK bot_" .. (os.time() % 500))
elseif (command == "001") then
-- IRC server sends this after successful login
raw("JOIN #newmont")
print("JOIN #newmont")
elseif (command == "PRIVMSG") then
-- Somebody said something!
if (text:find("strawberry")) then
msg("Strawberries are delicious.")
print("PRIVMSG " .. forum .. " :Strawberries are delicious.")
elseif (text:find("die")) then
print("QUIT :goodbye")
end
end