    -- | Support for more(1) buffering
    module Lambdabot.Plugin.Core.More (morePlugin) where

    import Lambdabot.Bot
    import Lambdabot.Monad
    import Lambdabot.Plugin

    import Control.Monad.Trans

    type MoreState = GlobalPrivate () [String]
    type More = ModuleT MoreState LB

    -- the @more state is handled centrally
    morePlugin :: Module (GlobalPrivate () [String])
    morePlugin :: Module MoreState
    morePlugin = Module MoreState
    forall st. Module st
    newModule
        { moduleDefState = return $ mkGlobalPrivate 20 ()
        , moduleInit = registerOutputFilter moreFilter
            -- TODO: improve output filter system...
            -- currently, @more output will bypass any filters in the
            -- chain after 'moreFilter'

        , moduleCmds = return
            [ (command "more")
                { help = say "@more. Return more output from the bot buffer."
                , process = \String
    _ -> do
                    target <- Cmd (ModuleT MoreState LB) Nick
    forall (m :: * -> *). Monad m => Cmd m Nick
    getTarget
                    morestate <- readPS target
                    -- TODO: test theory that we can just "say" morestate;
                    --       it should end up going through the moreFilter as needed
                    case morestate of
                        Maybe [String]
    Nothing -> () -> Cmd (ModuleT MoreState LB) ()
    forall a. a -> Cmd (ModuleT MoreState LB) a
    forall (m :: * -> *) a. Monad m => a -> m a
    return ()
                        Just [String]
    ls -> ModuleT MoreState LB [String]
    -> Cmd (ModuleT MoreState LB) [String]
    forall (m :: * -> *) a. Monad m => m a -> Cmd m a
    forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
    (MonadTrans t, Monad m) =>
    m a -> t m a
    lift (OutputFilter MoreState
    moreFilter Nick
    target [String]
    ls)
                            Cmd (ModuleT MoreState LB) [String]
    -> ([String] -> Cmd (ModuleT MoreState LB) ())
    -> Cmd (ModuleT MoreState LB) ()
    forall a b.
    Cmd (ModuleT MoreState LB) a
    -> (a -> Cmd (ModuleT MoreState LB) b)
    -> Cmd (ModuleT MoreState LB) b
    forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
    >>= (String -> Cmd (ModuleT MoreState LB) ())
    -> [String] -> Cmd (ModuleT MoreState LB) ()
    forall (t :: * -> *) (m :: * -> *) a b.
    (Foldable t, Monad m) =>
    (a -> m b) -> t a -> m ()
    mapM_ (LB () -> Cmd (ModuleT MoreState LB) ()
    forall a. LB a -> Cmd (ModuleT MoreState LB) a
    forall (m :: * -> *) a. MonadLB m => LB a -> m a
    lb (LB () -> Cmd (ModuleT MoreState LB) ())
    -> (String -> LB ()) -> String -> Cmd (ModuleT MoreState LB) ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Nick -> String -> LB ()
    ircPrivmsg' Nick
    target)
                }
            ]
        }

    moreFilter :: Nick -> [String] -> More [String]
    moreFilter :: OutputFilter MoreState
    moreFilter Nick
    target [String]
    msglines = do
        let ([String]
    morelines, [String]
    thislines) = case Int -> [String] -> [String]
    forall a. Int -> [a] -> [a]
    drop (Int
    maxLinesInt -> Int -> Int
    forall a. Num a => a -> a -> a
    +Int
    2) [String]
    msglines of
              [] -> ([],[String]
    msglines)
              [String]
    _  -> (Int -> [String] -> [String]
    forall a. Int -> [a] -> [a]
    drop Int
    maxLines [String]
    msglines, Int -> [String] -> [String]
    forall a. Int -> [a] -> [a]
    take Int
    maxLines [String]
    msglines)
        Nick -> Maybe [String] -> ModuleT MoreState LB ()
    forall (m :: * -> *) g p.
    (MonadLBState m, LBState m ~ GlobalPrivate g p) =>
    Nick -> Maybe p -> m ()
    writePS Nick
    target (Maybe [String] -> ModuleT MoreState LB ())
    -> Maybe [String] -> ModuleT MoreState LB ()
    forall a b. (a -> b) -> a -> b
    $ if [String] -> Bool
    forall a. [a] -> Bool
    forall (t :: * -> *) a. Foldable t => t a -> Bool
    null [String]
    morelines then Maybe [String]
    forall a. Maybe a
    Nothing else [String] -> Maybe [String]
    forall a. a -> Maybe a
    Just [String]
    morelines
        [String] -> ModuleT MoreState LB [String]
    forall a. a -> ModuleT MoreState LB a
    forall (m :: * -> *) a. Monad m => a -> m a
    return ([String] -> ModuleT MoreState LB [String])
    -> [String] -> ModuleT MoreState LB [String]
    forall a b. (a -> b) -> a -> b
    $ [String]
    thislines [String] -> [String] -> [String]
    forall a. [a] -> [a] -> [a]
    ++ if [String] -> Bool
    forall a. [a] -> Bool
    forall (t :: * -> *) a. Foldable t => t a -> Bool
    null [String]
    morelines
                              then []
                              else [Char
    '['Char -> String -> String
    forall a. a -> [a] -> [a]
    :Int -> String -> String
    forall a. Show a => a -> String -> String
    shows ([String] -> Int
    forall a. [a] -> Int
    forall (t :: * -> *) a. Foldable t => t a -> Int
    length [String]
    morelines) String
    " @more lines]"]

        where maxLines :: Int
    maxLines = Int
    5 -- arbitrary, really