Question: How should I do to use a state variable in bot loop?
Thank you for your making about this library.
I have a question. I'm creating a game dealer bot which can deal some games on discord bot. And, I chose this library. But, I can't understand how to use some state variables.
For example, I made a command join . Users who want to join the game should send this command, and bot updates players :: [Users] variable.
How should I do?
Good question, it's not very intuitive how to get state into this library. Here's an example I drafted that I should add to the existing examples.
We need to use a reference type (MVar a, IORef a, or something), and use IO functions to read and write to the shared variable. Pass this reference into discordOnEvent to have access to it.
{-# LANGUAGE OverloadedStrings #-} -- allows "strings" to be Data.Text
import Control.Monad (when, forM_)
import Control.Concurrent (threadDelay)
import Data.Char (toLower)
import qualified Data.Set as S
import qualified Data.Text as T
import qualified Data.Text.IO as TIO
import Data.IORef
import Discord
import Discord.Types
import qualified Discord.Requests as R
data State = State { users :: S.Set UserId }
stateExample :: IO ()
stateExample = do
state <- newIORef (State (S.empty)) -- create state
tok <- TIO.readFile "./examples/auth-token.secret"
t <- runDiscord $ def { discordToken = tok
, discordOnEnd = putStrLn "Ended"
, discordOnEvent = eventHandler state -- pass state here!
, discordForkThreadForEvents = False
-- Optional, but lets us use IORef instead of a
-- concurrency handler like MVar.
}
threadDelay (1 `div` 10 * 10^6)
TIO.putStrLn t
-- handler now knows state
eventHandler :: IORef State -> DiscordHandle -> Event -> IO ()
eventHandler state dis event = case event of
MessageCreate m -> when (not (fromBot m) && isJoin m) $ do
_ <- restCall dis (R.CreateMessage (messageChannel m) "Joining!")
-- add user to state
modifyIORef state (addUser m)
-- print it or something
s <- readIORef state
print $ S.toList (users s)
pure ()
_ -> pure ()
-- in a real program, this function has ALL of the state logic, and will
-- be VERY large and complicated. Here we just add the userid to the
-- existing state
addUser :: Message -> State -> State
addUser m s = State {
users = S.union (users s)
(S.fromList [userId (messageAuthor m)] )
}
fromBot :: Message -> Bool
fromBot m = userIsBot (messageAuthor m)
isJoin :: Message -> Bool
isJoin = ("join" `T.isPrefixOf`) . T.map toLower . messageText
Thank you! I've done what I want to do. I think it should be written into documents.
Added an example https://github.com/aquarial/discord-haskell/blob/master/examples/state-counter.hs
Documentation is a good idea, need to figure out what kind. Github wiki or markdown or other.
Just wanted to say that this thread and example was very helpful, thank you!