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
|
||||
===
|
||||
|
||||
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
11
bot.c
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue