mirror of https://github.com/nealey/irc-bot
simpler newmont
This commit is contained in:
parent
ed125f385b
commit
8dcfc5deb7
98
README
98
README
|
@ -1,22 +1,18 @@
|
||||||
bot
|
bot
|
||||||
===
|
===
|
||||||
|
|
||||||
This is a simple C program to assist writing an IRC bot in whatever language(s)
|
This is a simple C program to assist writing an IRC bot in whatever
|
||||||
you choose. It is based on the Unix principle that one program should do one
|
language(s) you choose. It is based on the Unix principle that one
|
||||||
thing.
|
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.
|
|
||||||
|
|
||||||
|
|
||||||
Getting Started Quickly
|
Getting Started Quickly
|
||||||
=======================
|
=======================
|
||||||
|
|
||||||
The example handler script, called `newmont`, will get you started right away.
|
The example handler script, called `newmont`, will get you started right
|
||||||
It will set its nickname to `newmont`, join the channel `#newmont`, and respond
|
away. It will set its nickname to `newmont`, join the channel
|
||||||
to any channel message containing the substring "strawberry".
|
`#newmont`, and respond to any channel message containing the substring
|
||||||
|
"strawberry".
|
||||||
|
|
||||||
Start it like so:
|
Start it like so:
|
||||||
|
|
||||||
|
@ -32,66 +28,74 @@ There are three pieces involved.
|
||||||
tcpclient
|
tcpclient
|
||||||
---------
|
---------
|
||||||
|
|
||||||
`tcpclient' is a program that connects to a TCP port, sets file descriptors 6
|
`tcpclient' is a program that connects to a TCP port, sets file
|
||||||
and 7 to be the input and output channels to that connection, and hands off
|
descriptors 6 and 7 to be the input and output channels to that
|
||||||
control to whatever program you specify (in the example above, the `./bot`
|
connection, and hands off control to whatever program you specify (in
|
||||||
program with argument `./newmont`). There also exist a `udpclient`,
|
the example above, the `./bot` program with argument `./newmont`).
|
||||||
`sslclient`, and probably others. The advantage to this method is that
|
There also exist a `udpclient`, `sslclient`, and probably others. The
|
||||||
your network client doesn't have to care about the transport mechanism:
|
advantage to this method is that your network client doesn't have to
|
||||||
TCP, UDP, TCP6, SSL, or whatever else.
|
care about the transport mechanism: TCP, UDP, TCP6, SSL, or whatever
|
||||||
|
else.
|
||||||
|
|
||||||
|
|
||||||
bot
|
bot
|
||||||
---
|
---
|
||||||
|
|
||||||
`bot` reads one line at a time from fd6 (or 0 as a fallback), parses it up,
|
`bot` reads one line at a time from fd6 (or 0 as a fallback), parses it
|
||||||
forks, sets some environment variables, and runs the "handler" program provided
|
up, forks, sets some environment variables, and runs the "handler"
|
||||||
as the first argument. Whatever that program prints to stdout is sent back to
|
program provided as the first argument. Whatever that program prints to
|
||||||
the server, verbatim. As a convenience, it automatically responds to PING
|
stdout is sent back to the server, verbatim. As a convenience, it
|
||||||
messages from the server. It can also rate-limit messages to the server, so
|
automatically responds to PING messages from the server. It can also
|
||||||
your bot doesn't flood itself off IRC. Lastly, it can monitor a directory and
|
rate-limit messages to the server, so your bot doesn't flood itself off
|
||||||
send the contents of any new file to the server, deleting the file after. This
|
IRC. Lastly, it can monitor a directory and send the contents of any
|
||||||
allows you to write to IRC from a cron job, git post-update hook, or whatever
|
new file to the server, deleting the file after. This allows you to
|
||||||
else you dream up.
|
write to IRC from a cron job, git post-update hook, or whatever else you
|
||||||
|
dream up.
|
||||||
|
|
||||||
`bot` sets the following environment variables:
|
`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
|
command IRC command or numeric
|
||||||
sender nickname to send "private" replies to this message
|
sender nickname to send "private" replies to this message
|
||||||
forum nickname to send "public" 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
|
Any additional parameters of the message, like with the MODE command,
|
||||||
passed in as arguments to the handler.
|
are passed in as arguments to the handler.
|
||||||
|
|
||||||
|
|
||||||
handler
|
handler
|
||||||
-------
|
-------
|
||||||
|
|
||||||
The handler is launched once for each incoming message. It should decide
|
The handler is launched once for each incoming message. It should
|
||||||
what to do based on the environment variables and argv, possibly writing
|
decide what to do based on the environment variables and argv, possibly
|
||||||
something to stdout, and then it should exit.
|
writing something to stdout, and then it should exit.
|
||||||
|
|
||||||
Handlers are launched in parallel to each other. IRC is an asynchronous
|
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
|
protocol, and while messages do tend to arrive in a particular order,
|
||||||
count on it, especially with this framework.
|
don't count on it, especially with this framework.
|
||||||
|
|
||||||
`newmont` is a very simple handler script to reply to any PRIVMSG with the
|
`newmont` is a very simple handler script to reply to any PRIVMSG with
|
||||||
substring "strawberry", in the (public) forum it was sent.
|
the substring "strawberry", in the (public) forum it was sent.
|
||||||
|
|
||||||
|
|
||||||
Caution
|
Caution
|
||||||
=======
|
=======
|
||||||
|
|
||||||
Your handler is getting input provided by a potentially malicious adversary.
|
Your handler is getting input provided by a potentially malicious
|
||||||
If you're not careful, you could create a remote exploit: a path through your
|
adversary. If you're not careful, you could create a remote exploit: a
|
||||||
handler script that allows anyone on IRC to do whatever they want on your
|
path through your handler script that allows anyone on IRC to do
|
||||||
local computer.
|
whatever they want on your local computer.
|
||||||
|
|
||||||
You can write handlers in bourne shell: it's really easy. It's equally as
|
You can write handlers in bourne shell: it's really easy. It's equally
|
||||||
easy to accidentally allow remote control. There's nothing I can do in the
|
as easy to accidentally allow remote control. There's nothing I can do
|
||||||
code I provide to prevent you from really hurting yourself, all I can do is
|
in my code I provide to prevent you from creating a remote exploit, all
|
||||||
warn you.
|
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>
|
Neale Pickett <neale@woozle.org>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
11
bot.c
11
bot.c
|
@ -22,6 +22,7 @@
|
||||||
|
|
||||||
#define max(a,b) ((a)>(b)?(a):(b))
|
#define max(a,b) ((a)>(b)?(a):(b))
|
||||||
|
|
||||||
|
bool running = true;
|
||||||
char *handler = NULL;
|
char *handler = NULL;
|
||||||
char *msgdir = NULL;
|
char *msgdir = NULL;
|
||||||
struct timeval output_interval = {0};
|
struct timeval output_interval = {0};
|
||||||
|
@ -295,6 +296,9 @@ void
|
||||||
handle_input()
|
handle_input()
|
||||||
{
|
{
|
||||||
handle_file(stdin, dispatch);
|
handle_file(stdin, dispatch);
|
||||||
|
if (feof(stdin)) {
|
||||||
|
running = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -462,11 +466,14 @@ main(int argc, char *argv[])
|
||||||
signal(SIGCHLD, sigchld);
|
signal(SIGCHLD, sigchld);
|
||||||
|
|
||||||
// Let handler know we're starting up
|
// Let handler know we're starting up
|
||||||
dispatch("INIT");
|
dispatch("_INIT_");
|
||||||
|
|
||||||
while (1) {
|
while (running) {
|
||||||
loop();
|
loop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Let handler know we're shutting down
|
||||||
|
dispatch("_END_");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,70 +1,42 @@
|
||||||
#! /usr/bin/lua
|
#! /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")
|
prefix = os.getenv("prefix")
|
||||||
forum = os.getenv("forum")
|
forum = os.getenv("forum")
|
||||||
sender = os.getenv("sender")
|
sender = os.getenv("sender")
|
||||||
command = os.getenv("command")
|
command = os.getenv("command")
|
||||||
text = os.getenv("text")
|
text = os.getenv("text")
|
||||||
|
|
||||||
--
|
io.stderr:write(">>> [" .. command .. "] " ..
|
||||||
-- Write text to stderr (for debugging)
|
(sender or "-") .. "/" ..
|
||||||
--
|
(forum or "-") .. " " ..
|
||||||
function log(text)
|
(text or "") .. "\n")
|
||||||
io.stderr:write(text .. "\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
--
|
-- Our behavior depends on what the command is
|
||||||
-- Send a raw IRC command to the server
|
if (command == "_INIT_") then
|
||||||
--
|
|
||||||
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
|
|
||||||
-- bot sends this when it first starts up, so we can log in
|
-- bot sends this when it first starts up, so we can log in
|
||||||
raw("NICK nemont")
|
print("NICK nemont")
|
||||||
raw("USER newmont newmont newmont :Sample bot")
|
print("USER newmont newmont newmont :Sample bot")
|
||||||
elseif (command == "433") then
|
elseif (command == "433") then
|
||||||
-- Couldn't get the nickname we asked for
|
-- Couldn't get the nickname we asked for
|
||||||
raw("NICK bot_" .. (os.time() % 500))
|
print("NICK bot_" .. (os.time() % 500))
|
||||||
elseif (command == "001") then
|
elseif (command == "001") then
|
||||||
-- IRC server sends this after successful login
|
-- IRC server sends this after successful login
|
||||||
raw("JOIN #newmont")
|
print("JOIN #newmont")
|
||||||
elseif (command == "PRIVMSG") then
|
elseif (command == "PRIVMSG") then
|
||||||
-- Somebody said something!
|
-- Somebody said something!
|
||||||
if (text:find("strawberry")) then
|
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
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue