Difference between revisions of "Roll your own IRC bot/Source"

From HaskellWiki
Jump to: navigation, search
m
(Replaced content with "This page once contained a complete copy of the source code for the bot described in Roll your own IRC bot, but it fell out of date with the article (and became uncomp...")
 
Line 1: Line 1:
The complete IRC bot source from the [[Roll your own IRC bot]] tutorial.
 
  +
This page once contained a complete copy of the source code for the bot described in [[Roll your own IRC bot]], but it fell out of date with the article (and became uncompilable). That page already has source for the bot anyway, so there's not much point in keeping this separate one.
Free for any use.
 
 
<haskell>
 
--
 
-- A simple, clean IRC bot in Haskell
 
--
 
-- $ ghc -O --make -o bot bot.hs
 
-- $ ./bot
 
-- or
 
-- $ runhaskell bot.hs
 
-- or
 
-- $ echo main | ghci bot.hs
 
-- or
 
-- $ echo main | hugs -98 bot.hs
 
-- or
 
-- $ runhugs -98 bot.hs
 
--
 
 
import Data.List
 
import Network
 
import System.IO
 
import System.Time
 
import System.Exit
 
import Control.Monad.Reader
 
-- import Control.Exception -- for base-3, with base-4 use Control.OldException
 
import Control.OldException
 
import Text.Printf
 
import Prelude hiding (catch)
 
 
server = "irc.freenode.org"
 
port = 6667
 
chan = "#tutbot-testing"
 
nick = "tutbot"
 
 
--
 
-- The 'Net' monad, a wrapper over IO, carrying the bot's immutable state.
 
-- A socket and the bot's start time.
 
--
 
type Net = ReaderT Bot IO
 
data Bot = Bot { socket :: Handle, starttime :: ClockTime }
 
 
--
 
-- Set up actions to run on start and end, and run the main loop
 
--
 
main :: IO ()
 
main = bracket connect disconnect loop
 
where
 
disconnect = hClose . socket
 
loop st = catch (runReaderT run st) (const $ return ())
 
 
--
 
-- Connect to the server and return the initial bot state
 
--
 
connect :: IO Bot
 
connect = notify $ do
 
t <- getClockTime
 
h <- connectTo server (PortNumber (fromIntegral port))
 
hSetBuffering h NoBuffering
 
return (Bot h t)
 
where
 
notify a = bracket_
 
(printf "Connecting to %s ... " server >> hFlush stdout)
 
(putStrLn "done.")
 
a
 
 
--
 
-- We're in the Net monad now, so we've connected successfully
 
-- Join a channel, and start processing commands
 
--
 
run :: Net ()
 
run = do
 
write "NICK" nick
 
write "USER" (nick++" 0 * :tutorial bot")
 
write "JOIN" chan
 
asks socket >>= listen
 
 
--
 
-- Process each line from the server
 
--
 
listen :: Handle -> Net ()
 
listen h = forever $ do
 
s <- init `fmap` io (hGetLine h)
 
io (putStrLn s)
 
if ping s then pong s else eval (clean s)
 
where
 
forever a = a >> forever a
 
clean = drop 1 . dropWhile (/= ':') . drop 1
 
ping x = "PING :" `isPrefixOf` x
 
pong x = write "PONG" (':' : drop 6 x)
 
 
--
 
-- Dispatch a command
 
--
 
eval :: String -> Net ()
 
eval "!uptime" = uptime >>= privmsg
 
eval "!quit" = write "QUIT" ":Exiting" >> io (exitWith ExitSuccess)
 
eval x | "!id " `isPrefixOf` x = privmsg (drop 4 x)
 
eval _ = return () -- ignore everything else
 
 
--
 
-- Send a privmsg to the current chan + server
 
--
 
privmsg :: String -> Net ()
 
privmsg s = write "PRIVMSG" (chan ++ " :" ++ s)
 
 
--
 
-- Send a message out to the server we're currently connected to
 
--
 
write :: String -> String -> Net ()
 
write s t = do
 
h <- asks socket
 
io $ hPrintf h "%s %s\r\n" s t
 
io $ printf "> %s %s\n" s t
 
 
--
 
-- Calculate and pretty print the uptime
 
--
 
uptime :: Net String
 
uptime = do
 
now <- io getClockTime
 
zero <- asks starttime
 
return . pretty $ diffClockTimes now zero
 
 
--
 
-- Pretty print the date in '1d 9h 9m 17s' format
 
--
 
pretty :: TimeDiff -> String
 
pretty td = join . intersperse " " . filter (not . null) . map f $
 
[(years ,"y") ,(months `mod` 12,"m")
 
,(days `mod` 28,"d") ,(hours `mod` 24,"h")
 
,(mins `mod` 60,"m") ,(secs `mod` 60,"s")]
 
where
 
secs = abs $ tdSec td ; mins = secs `div` 60
 
hours = mins `div` 60 ; days = hours `div` 24
 
months = days `div` 28 ; years = months `div` 12
 
f (i,s) | i == 0 = []
 
| otherwise = show i ++ s
 
 
--
 
-- Convenience.
 
--
 
io :: IO a -> Net a
 
io = liftIO
 
</haskell>
 

Latest revision as of 00:57, 17 June 2015

This page once contained a complete copy of the source code for the bot described in Roll your own IRC bot, but it fell out of date with the article (and became uncompilable). That page already has source for the bot anyway, so there's not much point in keeping this separate one.