Thanks to visit codestin.com
Credit goes to github.com

Skip to content

A library for writing Discord bots in Haskell

License

saevarb/haskord

Repository files navigation

haskord

A library for writing Discord bots in Haskell.

NOTE: The library is not considered stable. This means:

  • The module hierarchy is a mess
  • The API is still in flux and will change
  • Large parts of the Discord HTTP API have not been implemented yet
  • This readme is probably outdated.

I like exploring new concepts in Haskell, and as such this library is also the result of experimentation with some type level programming concepts. Warning: over-engineering past this point.

Features:

  • Powerful, simple-to-use plugin API with some type-level programming sprinkled on top
  • Automatically sandboxed plugins
    • Plugins that run too long can be killed
    • Plugins that crash can’t affect the main thread
  • Curses style TUI
  • State management
    • Persist data permanently to an sqlite database using persistent
    • Ephemeral state that persists only while bot is running

(The list above probably contains features that have not been implemented yet)

Very early TUI screenshot of the bot doing nothing

Early bot TUI

Getting started

With that said.. The project is not on hackage or stackage yet, so to use it you will need to add it as an extra dependency in your stack.yaml. This should be something like:

# .. elided ..
packages:
  - location: https://github.com/saevarb/haskord
    extra-dep: true
    subdirs:
      - haskord-lib
# .. elided ..

Then add haskord as a dependency to either your .cabal file or your package.yaml.

Now create a config.yaml with the same contents as sample.yaml.

and finally, write a bot:

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}
module Main where

import Control.Monad (void)
import qualified Data.Text as T

import Haskord
import Haskord.Types


reversePlugin
    :: DispatchPlugin    -- we want to handle a dispatch message
       "Reverse plugin"  -- the plugin's name
       'MESSAGE_CREATE   -- the dispatch event is MESSAGE_CREATE
       ()                -- No state
reversePlugin =
    -- simplePlugin assumes you don't want any state, the handler function
    -- only takes the message payload as parameter
    simplePlugin $ \(MessageCreatePayload Message {..}) -> do
        let split = T.words content
        case split of
            ("!reverse":rest) ->
                void $ sendMessage channelId . msgText . T.reverse $ T.unwords rest
            _ -> return ()
        return ()

counterPlugin
    :: DispatchPlugin    -- we want to handle a dispatch message
       "Counter plugin"  -- the plugin's name
       'MESSAGE_CREATE   -- the dispatch event is MESSAGE_CREATE
       Int               -- We have one integer as state
counterPlugin =
    -- For a plugin that uses state, we define `pInitializer` to give us the initial state
    Plugin
    { pInitializer = return 0
    , pHandler        = counterHandler
    }
  where
    -- Note that this handler takes `TVar Int` because our plugin's state is `Int`
    counterHandler :: TVar Int -> DispatchPayload 'MESSAGE_CREATE -> BotM ()
    counterHandler stateVar (MessageCreatePayload Message {..}) = do
        let split = T.words content
        case split of
            ("!count":_) -> do
                counter <- liftIO $ atomically $ do
                    modifyTVar' stateVar succ
                    readTVar stateVar
                void $ sendMessage channelId . msgText $ "Counter: " <> T.pack (show counter)
            _ -> return ()
        return ()

main :: IO ()
main =
    -- config.yaml should be in the working directory
    runBotWithSettings "config.yaml" $
      defaultSettings
      -- We need to "homogenize" the plugins since they have different types, so we call `wrapPlugin`
      -- on each, individually. `map wrapPlugin [counterPlugin, reversePlugin]` does not work.
      # withPlugins
        [ wrapPlugin counterPlugin
        , wrapPlugin reversePlugin
        ]

See the examples subdirectory.

Known issues

  • Resuming may work, but probably not.
  • After updating the logging stuff and updating the renderer for it, it doesn’t work properly
  • It only builds on linux or other systems that can use the vty library because of brick
  • Large parts of Discord HTTP API yet to be implemented

About

A library for writing Discord bots in Haskell

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published