Initial commit
This commit is contained in:
commit
7e3bdb76d0
12 changed files with 429 additions and 0 deletions
5
src/EuphApi.hs
Normal file
5
src/EuphApi.hs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
module EuphApi
|
||||
( module EuphApi.Types
|
||||
) where
|
||||
|
||||
import EuphApi.Types
|
||||
1
src/EuphApi/Controller.hs
Normal file
1
src/EuphApi/Controller.hs
Normal file
|
|
@ -0,0 +1 @@
|
|||
module EuphApi.Controller where
|
||||
134
src/EuphApi/Threads.hs
Normal file
134
src/EuphApi/Threads.hs
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
{-# LANGUAGE ExistentialQuantification #-}
|
||||
|
||||
-- | Setup consisting of a few threads to send and receive packets to and from
|
||||
-- the euphoria api using a websocket connection.
|
||||
|
||||
module EuphApi.Threads (
|
||||
-- * Events and replies
|
||||
Failure(..)
|
||||
-- * Functions for using the api
|
||||
, send
|
||||
, reply
|
||||
) where
|
||||
|
||||
import Control.Concurrent
|
||||
import Control.Exception
|
||||
import Data.Aeson
|
||||
import Data.Text
|
||||
import qualified EuphApi.Types as E
|
||||
import qualified Network.WebSockets as WS
|
||||
|
||||
-- Some useful type aliases
|
||||
type PacketID = Text
|
||||
|
||||
{-
|
||||
- Events and replies
|
||||
-}
|
||||
|
||||
-- | The ways in which getting a reply from the server can fail.
|
||||
data Failure = FailDisconnect -- ^ Disconnected from the server while waiting for the reply.
|
||||
| FailParse -- ^ Could not parse the server's reply correctly.
|
||||
|
||||
-- send-reply
|
||||
data SendReply = SendReply
|
||||
{ sendReplyMessage :: E.Message
|
||||
} deriving (Show)
|
||||
|
||||
instance FromJSON SendReply where
|
||||
parseJSON v = SendReply <$> parseJSON v
|
||||
|
||||
{-
|
||||
- API functions
|
||||
-}
|
||||
|
||||
send :: SendChan -> Text -> IO (Either Failure E.Message)
|
||||
send = undefined
|
||||
|
||||
reply :: SendChan -> PacketID -> Text -> IO (Either Failure E.Message)
|
||||
reply = undefined
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{-
|
||||
data Packet = Packet
|
||||
{ packetID :: Maybe PacketID
|
||||
, packetType :: Text
|
||||
, packetContent :: Content
|
||||
, packetThrottled :: Maybe Text
|
||||
}
|
||||
-}
|
||||
|
||||
type SendChan = Chan Send
|
||||
-- Contents of sendChan
|
||||
data Send = SPacket Text --Value -- packet type, content
|
||||
| SDisconnect
|
||||
|
||||
type RecvChan = Chan Recv
|
||||
-- Contents of recvChan
|
||||
data Recv = RConnectionClosed -- Ws connection closed
|
||||
-- | RPacket ByteString -- Packet received from the ws connection
|
||||
-- | forall c . (FromJSON c) => RReply PacketID (MVar (Response c)) -- Request for a reply with a certain ID
|
||||
|
||||
{-
|
||||
sendPacket :: Connection -> Packet -> IO ()
|
||||
sendPacket = undefined
|
||||
|
||||
recvPacket :: Connection -> IO Packet
|
||||
recvPacket = undefined
|
||||
|
||||
sendThread :: SendChan -> RecvChan -> Connection -> IO ()
|
||||
sendThread s r c = do
|
||||
return ()
|
||||
|
||||
type EventChan = Chan Event
|
||||
-- Contents of eventChan
|
||||
data Event = EPlaceholder
|
||||
|
||||
fetchMessage :: RecvChan -> Connection -> IO ()
|
||||
fetchMessage recv con = do
|
||||
message <- receiveData con
|
||||
writeChan recv (RPacket message)
|
||||
fetchMessage recv con
|
||||
|
||||
fetchThread :: RecvChan -> Connection -> IO ()
|
||||
fetchThread recv con = fetchMessage recv con `catch` handleException
|
||||
where
|
||||
handleException (CloseRequest _ _) = writeChan recv RConnectionClosed
|
||||
handleException ConnectionClosed = writeChan recv RConnectionClosed
|
||||
handleException _ = fetchThread recv con
|
||||
|
||||
sendMessage :: SendChan -> RecvChan -> Connection -> IO ()
|
||||
sendMessage send recv con = do
|
||||
message <- readChan send
|
||||
return ()
|
||||
|
||||
sendThread :: SendChan -> RecvChan -> Connection -> IO ()
|
||||
sendThread = undefined
|
||||
-}
|
||||
139
src/EuphApi/Types.hs
Normal file
139
src/EuphApi/Types.hs
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-# LANGUAGE RecordWildCards #-}
|
||||
|
||||
-- | This module implements parts of the Euphoria API at
|
||||
-- <http://api.euphoria.io/#overview>.
|
||||
--
|
||||
-- Currently, accounts are not implemented.
|
||||
-- This means that all account, room host and staff commands are not implemented.
|
||||
|
||||
module EuphApi.Types
|
||||
( Snowflake
|
||||
, SessionID
|
||||
, UserID(..)
|
||||
, UserType(..)
|
||||
, Message(..)
|
||||
, SessionView(..)
|
||||
) where
|
||||
|
||||
import Data.Aeson
|
||||
import Data.Function
|
||||
import qualified Data.Text as T
|
||||
import Data.Time
|
||||
|
||||
-- | Represents <http://api.euphoria.io/#snowflake>.
|
||||
--
|
||||
-- A 'Snowflake' is a 13-character string, usually used as a unique identifier for some type of object.
|
||||
-- It is the base-36 encoding of an unsigned, 64-bit integer.
|
||||
type Snowflake = T.Text
|
||||
|
||||
-- | ID of a session, unique across all sessions globally.
|
||||
type SessionID = T.Text
|
||||
|
||||
-- | Represents <http://api.euphoria.io/#userid>.
|
||||
--
|
||||
-- A 'UserID' identifies a user.
|
||||
-- The type of session, 'UserType', can be retrieved via 'userType'.
|
||||
data UserID = UserID
|
||||
{ userType :: UserType
|
||||
, userSnowflake :: Snowflake
|
||||
} deriving (Show, Eq)
|
||||
|
||||
instance FromJSON UserID where
|
||||
parseJSON = withText "UserID" $ \t ->
|
||||
let (tp, sf) = T.breakOn ":" t
|
||||
userType = findUserType tp
|
||||
userSnowflake = T.drop 1 sf
|
||||
in return $ if userType == Other
|
||||
then UserID {userSnowflake=t, ..}
|
||||
else UserID {..}
|
||||
where
|
||||
findUserType txt
|
||||
| txt == "account" = Account
|
||||
| txt == "bot" = Bot
|
||||
| txt == "agent" = Agent
|
||||
| otherwise = Other
|
||||
|
||||
|
||||
-- | Whether a user is logged in, out, or a bot.
|
||||
--
|
||||
-- See <http://api.euphoria.io/#userid> for more info.
|
||||
data UserType = Agent
|
||||
| Account
|
||||
| Bot
|
||||
| Other
|
||||
deriving (Show, Eq)
|
||||
|
||||
-- | Represents <http://api.euphoria.io/#message>.
|
||||
--
|
||||
-- A 'Message' is a node in a Room’s Log.
|
||||
-- It corresponds to a chat message, or a post, or any broadcasted event in a room that should appear in the log.
|
||||
--
|
||||
-- The fields @previous_edit_id@ and @encryption_key_id@ are not implemented.
|
||||
data Message = Message
|
||||
{ msgID :: Snowflake
|
||||
-- ^ The id of the message (unique within a room)
|
||||
, msgParent :: Maybe Snowflake
|
||||
-- ^ The id of the message's parent, or Nothing if top-level
|
||||
, msgTime :: UTCTime
|
||||
-- ^ The unix timestamp of when the message was posted
|
||||
, msgSender :: SessionView
|
||||
-- ^ The view of the sender's session
|
||||
, msgContent :: String
|
||||
-- ^ The content of the message (client-defined)
|
||||
, msgEdited :: Maybe UTCTime
|
||||
-- ^ The unix timestamp of when the message was last edited
|
||||
, msgDeleted :: Maybe UTCTime
|
||||
-- ^ The unix timestamp of when the message was deleted
|
||||
, msgTruncated :: Bool
|
||||
-- ^ If true, then the full content of this message is not included.
|
||||
|
||||
-- , msgPreviousEditId :: MaybeSnowflake -- not implemented
|
||||
-- , msgEncryptionKeyID :: String -- not implemented
|
||||
} deriving (Show)
|
||||
|
||||
instance Eq Message where
|
||||
(==) = (==) `on` msgID
|
||||
|
||||
instance Ord Message where
|
||||
compare = compare `on` msgID
|
||||
|
||||
instance FromJSON Message where
|
||||
parseJSON = withObject "Message" $ \o -> do
|
||||
msgID <- o .: "id"
|
||||
msgParent <- o .:? "parent"
|
||||
msgTime <- o .: "time"
|
||||
msgSender <- o .: "sender"
|
||||
msgContent <- o .: "content"
|
||||
msgEdited <- o .:? "edited"
|
||||
msgDeleted <- o .:? "deleted"
|
||||
msgTruncated <- o .:? "truncated" .!= False
|
||||
return $ Message {..}
|
||||
|
||||
-- | Represents <http://api.euphoria.io/#sessionview>.
|
||||
--
|
||||
-- A 'SessionView' describes a session and its identity.
|
||||
--
|
||||
-- The fields @client_address@ and @real_client_address@ are not implemented.
|
||||
data SessionView = SessionView
|
||||
{ sessID :: UserID
|
||||
, sessName :: String
|
||||
, sessServerID :: String
|
||||
, sessServerEra :: String
|
||||
, sessSessionID :: SessionID
|
||||
, isStaff :: Bool
|
||||
, isManager :: Bool
|
||||
-- , sessClientAddress :: String -- not implemented
|
||||
-- , sessRealClientAddress :: String -- not implemented
|
||||
} deriving (Show)
|
||||
|
||||
instance FromJSON SessionView where
|
||||
parseJSON = withObject "SessionView" $ \o -> do
|
||||
sessID <- o .: "id"
|
||||
sessName <- o .: "name"
|
||||
sessServerID <- o .: "server_id"
|
||||
sessServerEra <- o .: "server_era"
|
||||
sessSessionID <- o .: "session_id"
|
||||
isStaff <- o .:? "is_staff" .!= False
|
||||
isManager <- o .:? "is_manager" .!= False
|
||||
return $ SessionView {..}
|
||||
Loading…
Add table
Add a link
Reference in a new issue