Add lenses to most API types

This commit is contained in:
Joscha 2020-04-09 10:39:59 +00:00
parent 8ec2d582b0
commit 9a476d9371
5 changed files with 191 additions and 73 deletions

View file

@ -3,6 +3,7 @@
## upcoming ## upcoming
- add `Haboli.Euphoria.Botrulez` module - add `Haboli.Euphoria.Botrulez` module
- add `Haboli.Euphoria.Command` module and submodules - add `Haboli.Euphoria.Command` module and submodules
- add `Haboli.Euphoria.Lens` and add lenses to a few types
- add `Haboli.Euphoria.Listing` module - add `Haboli.Euphoria.Listing` module
- add `Haboli.Euphoria.Util` module and move `respondingToPing` there - add `Haboli.Euphoria.Util` module and move `respondingToPing` there
- add example bot (`Haboli.Euphoria.ExampleBot`) - add example bot (`Haboli.Euphoria.ExampleBot`)

View file

@ -4,7 +4,7 @@ cabal-version: 1.12
-- --
-- see: https://github.com/sol/hpack -- see: https://github.com/sol/hpack
-- --
-- hash: ae5e8a6e8abe1ff515797a54dfa9acd185df7699102b31190d569ccfe3a693f8 -- hash: 35dc75bb1fd8534c4476115b165d0e969a579affaa05a419abe6f7e3ab749082
name: haboli name: haboli
version: 0.3.1.0 version: 0.3.1.0
@ -37,6 +37,7 @@ library
Haboli.Euphoria.Command.Megaparsec Haboli.Euphoria.Command.Megaparsec
Haboli.Euphoria.Command.Simple Haboli.Euphoria.Command.Simple
Haboli.Euphoria.ExampleBot Haboli.Euphoria.ExampleBot
Haboli.Euphoria.Lens
Haboli.Euphoria.Listing Haboli.Euphoria.Listing
Haboli.Euphoria.Util Haboli.Euphoria.Util
other-modules: other-modules:
@ -48,8 +49,11 @@ library
, base >=4.7 && <5 , base >=4.7 && <5
, containers , containers
, megaparsec , megaparsec
, microlens
, microlens-th
, network , network
, stm , stm
, template-haskell
, text , text
, time , time
, transformers , transformers

View file

@ -18,8 +18,11 @@ dependencies:
- aeson - aeson
- containers - containers
- megaparsec - megaparsec
- microlens
- microlens-th
- network - network
- stm - stm
- template-haskell
- text - text
- time - time
- transformers - transformers

View file

@ -1,4 +1,5 @@
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
-- | This module attempts to map the structure of the ephoria API to types. -- | This module attempts to map the structure of the ephoria API to types.
@ -6,41 +7,101 @@ module Haboli.Euphoria.Api
( ToJSONObject(..) ( ToJSONObject(..)
-- * Basic types -- * Basic types
, AuthOption(..) , AuthOption(..)
, Message(..)
, PersonalAccountView(..)
, SessionView(..)
, Snowflake , Snowflake
, UserType(..) , UserType(..)
, UserId(..) , UserId(..)
, userTypeL
, userSnowflakeL
, SessionView(..)
, svIdL
, svNickL
, svServerIdL
, svServerEraL
, svSessionIdL
, svIsStaffL
, svIsManagerL
, svClientAddressL
, svRealClientAddressL
, Message(..)
, msgIdL
, msgParentL
, msgPreviousEditIdL
, msgTimeL
, msgSenderL
, msgContentL
, msgEncryptionKeyIdL
, msgEditedL
, msgDeletedL
, msgTruncatedL
, PersonalAccountView(..)
, pavIdL
, pavNameL
, pavEmailL
-- * Asynchronous events -- * Asynchronous events
-- ** bounce-event -- ** bounce-event
, BounceEvent(..) , BounceEvent(..)
, bounceReasonL
, bounceAuthOptionL
-- ** disconnect-event -- ** disconnect-event
, DisconnectEvent(..) , DisconnectEvent(..)
, disconnectReasonL
-- ** hello-event -- ** hello-event
, HelloEvent(..) , HelloEvent(..)
, helloAccountL
, helloSessionViewL
, helloAccountHasAccessL
, helloAccountEmailVerifiedL
, helloRoomIsPrivateL
, helloVersionL
-- ** join-event -- ** join-event
, JoinEvent(..) , JoinEvent(..)
, joinSessionL
-- ** login-event -- ** login-event
, LoginEvent(..) , LoginEvent(..)
, loginAccountIdL
-- ** logout-event -- ** logout-event
, LogoutEvent(..) , LogoutEvent(..)
-- ** network-event -- ** network-event
, NetworkEvent(..) , NetworkEvent(..)
, networkTypeL
, networkServerIdL
, networkServerEraL
-- ** nick-event -- ** nick-event
, NickEvent(..) , NickEvent(..)
, nickSessionIdL
, nickIdL
, nickFromL
, nickToL
-- ** edit-message-event -- ** edit-message-event
, EditMessageEvent(..) , EditMessageEvent(..)
, editMessageMessageL
, editMessageEditIdL
-- ** part-event -- ** part-event
, PartEvent(..) , PartEvent(..)
, partSessionL
-- ** ping-event -- ** ping-event
, PingEvent(..) , PingEvent(..)
, pingTimeL
, pingNextL
-- ** pm-initiate-event -- ** pm-initiate-event
, PmInitiateEvent(..) , PmInitiateEvent(..)
, pmInitiateFromL
, pmInitiateFromNickL
, pmInitiateFromRoomL
, pmInitiatePmIdL
-- ** send-event -- ** send-event
, SendEvent(..) , SendEvent(..)
, sendMessageL
-- ** snapshot-event -- ** snapshot-event
, SnapshotEvent(..) , SnapshotEvent(..)
, snapshotIdentityL
, snapshotSessionIdL
, snapshotVersionL
, snapshotListingL
, snapshotLogL
, snapshotNickL
, snapshotPmWithNickL
, snapshotPmWithUserIdL
-- * Session commands -- * Session commands
-- ** auth -- ** auth
, AuthCommand(..) , AuthCommand(..)
@ -77,6 +138,8 @@ import qualified Data.Text as T
import Data.Time import Data.Time
import Data.Time.Clock.POSIX import Data.Time.Clock.POSIX
import Haboli.Euphoria.Lens
-- | A class for all types that can be converted into an -- | A class for all types that can be converted into an
-- 'Data.Aeson.Types.Object'. Similar to 'ToJSON', but more restrictive. -- 'Data.Aeson.Types.Object'. Similar to 'ToJSON', but more restrictive.
class ToJSONObject a where class ToJSONObject a where
@ -110,75 +173,6 @@ instance FromJSON AuthOption where
parseJSON (String _) = fail "invalid value" parseJSON (String _) = fail "invalid value"
parseJSON v = typeMismatch "String" v parseJSON v = typeMismatch "String" v
-- | A 'Message' is a node in a rooms log. It corresponds to a chat message, or
-- a post, or any broadcasted event in a room that should appear in the log. See
-- <https://api.euphoria.io/#message>.
data Message = Message
{ msgId :: Snowflake
, msgParent :: Maybe Snowflake
, msgPreviousEditId :: Maybe Snowflake
, msgTime :: UTCTime
, msgSender :: SessionView
, msgContent :: T.Text
, msgEncryptionKeyId :: Maybe T.Text
, msgEdited :: Maybe UTCTime
, msgDeleted :: Maybe UTCTime
, msgTruncated :: Bool
} deriving (Show)
instance FromJSON Message where
parseJSON v = parseJSON v >>= \o -> Message
<$> o .: "id"
<*> o .:? "parent"
<*> o .:? "previous_edit_id"
<*> (posixSecondsToUTCTime <$> o .: "time")
<*> o .: "sender"
<*> o .: "content"
<*> o .:? "encryption_key_id"
<*> o .:? "edited"
<*> (fmap posixSecondsToUTCTime <$> o .:? "deleted")
<*> o .:? "truncated" .!= False
-- | A 'PersonalAccountView' contains information about an euphoria account. See
-- <https://api.euphoria.io/#personalaccountview>.
data PersonalAccountView = PersonalAccountView
{ pavId :: Snowflake
, pavName :: T.Text
, pavEmail :: T.Text
} deriving (Show)
instance FromJSON PersonalAccountView where
parseJSON v = parseJSON v >>= \o -> PersonalAccountView
<$> o .: "id"
<*> o .: "name"
<*> o .: "email"
-- | A 'SessionView' describes a session and its identity. See
-- <https://api.euphoria.io/#sessionview>.
data SessionView = SessionView
{ svId :: UserId
, svNick :: T.Text
, svServerId :: T.Text
, svServerEra :: T.Text
, svSessionId :: T.Text
, svIsStaff :: Bool
, svIsManager :: Bool
, svClientAddress :: Maybe T.Text
, svRealClientAddress :: Maybe T.Text
} deriving (Show)
instance FromJSON SessionView where
parseJSON v = parseJSON v >>= \o -> SessionView
<$> o .: "id"
<*> o .: "name"
<*> o .: "server_id"
<*> o .: "server_era"
<*> o .: "session_id"
<*> o .:? "is_staff" .!= False
<*> o .:? "is_manager" .!= False
<*> o .:? "client_address"
<*> o .:? "real_client_address"
-- | A snowflake is a 13-character string, usually used as a unique identifier -- | 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 -- for some type of object. It is the base-36 encoding of an unsigned, 64-bit
-- integer. See <https://api.euphoria.io/#snowflake>. -- integer. See <https://api.euphoria.io/#snowflake>.
@ -206,6 +200,8 @@ data UserId = UserId
, userSnowflake :: Snowflake , userSnowflake :: Snowflake
} deriving (Show, Eq, Ord) } deriving (Show, Eq, Ord)
makeLensesL ''UserId
instance ToJSON UserId where instance ToJSON UserId where
toJSON uid = toJSON uid =
let prefix = case userType uid of let prefix = case userType uid of
@ -223,6 +219,81 @@ instance FromJSON UserId where
("bot", snowflake) -> pure $ UserId Bot $ T.drop 1 snowflake ("bot", snowflake) -> pure $ UserId Bot $ T.drop 1 snowflake
_ -> fail "invalid user id label" _ -> fail "invalid user id label"
-- | A 'SessionView' describes a session and its identity. See
-- <https://api.euphoria.io/#sessionview>.
data SessionView = SessionView
{ svId :: UserId
, svNick :: T.Text
, svServerId :: T.Text
, svServerEra :: T.Text
, svSessionId :: T.Text
, svIsStaff :: Bool
, svIsManager :: Bool
, svClientAddress :: Maybe T.Text
, svRealClientAddress :: Maybe T.Text
} deriving (Show)
makeLensesL ''SessionView
instance FromJSON SessionView where
parseJSON v = parseJSON v >>= \o -> SessionView
<$> o .: "id"
<*> o .: "name"
<*> o .: "server_id"
<*> o .: "server_era"
<*> o .: "session_id"
<*> o .:? "is_staff" .!= False
<*> o .:? "is_manager" .!= False
<*> o .:? "client_address"
<*> o .:? "real_client_address"
-- | A 'Message' is a node in a rooms log. It corresponds to a chat message, or
-- a post, or any broadcasted event in a room that should appear in the log. See
-- <https://api.euphoria.io/#message>.
data Message = Message
{ msgId :: Snowflake
, msgParent :: Maybe Snowflake
, msgPreviousEditId :: Maybe Snowflake
, msgTime :: UTCTime
, msgSender :: SessionView
, msgContent :: T.Text
, msgEncryptionKeyId :: Maybe T.Text
, msgEdited :: Maybe UTCTime
, msgDeleted :: Maybe UTCTime
, msgTruncated :: Bool
} deriving (Show)
makeLensesL ''Message
instance FromJSON Message where
parseJSON v = parseJSON v >>= \o -> Message
<$> o .: "id"
<*> o .:? "parent"
<*> o .:? "previous_edit_id"
<*> (posixSecondsToUTCTime <$> o .: "time")
<*> o .: "sender"
<*> o .: "content"
<*> o .:? "encryption_key_id"
<*> o .:? "edited"
<*> (fmap posixSecondsToUTCTime <$> o .:? "deleted")
<*> o .:? "truncated" .!= False
-- | A 'PersonalAccountView' contains information about an euphoria account. See
-- <https://api.euphoria.io/#personalaccountview>.
data PersonalAccountView = PersonalAccountView
{ pavId :: Snowflake
, pavName :: T.Text
, pavEmail :: T.Text
} deriving (Show)
makeLensesL ''PersonalAccountView
instance FromJSON PersonalAccountView where
parseJSON v = parseJSON v >>= \o -> PersonalAccountView
<$> o .: "id"
<*> o .: "name"
<*> o .: "email"
{- Asynchronous events -} {- Asynchronous events -}
{- bounce-event -} {- bounce-event -}
@ -233,6 +304,8 @@ data BounceEvent = BounceEvent
, bounceAuthOption :: [AuthOption] , bounceAuthOption :: [AuthOption]
} deriving (Show) } deriving (Show)
makeLensesL ''BounceEvent
instance FromJSON BounceEvent where instance FromJSON BounceEvent where
parseJSON = fromPacket "bounce-event" $ \o -> BounceEvent parseJSON = fromPacket "bounce-event" $ \o -> BounceEvent
<$> o .:? "reason" <$> o .:? "reason"
@ -245,6 +318,8 @@ newtype DisconnectEvent = DisconnectEvent
{ disconnectReason :: T.Text { disconnectReason :: T.Text
} deriving (Show) } deriving (Show)
makeLensesL ''DisconnectEvent
instance FromJSON DisconnectEvent where instance FromJSON DisconnectEvent where
parseJSON = fromPacket "disconnect-evnet" $ \o -> DisconnectEvent parseJSON = fromPacket "disconnect-evnet" $ \o -> DisconnectEvent
<$> o .: "reason" <$> o .: "reason"
@ -261,6 +336,8 @@ data HelloEvent = HelloEvent
, helloVersion :: T.Text , helloVersion :: T.Text
} deriving (Show) } deriving (Show)
makeLensesL ''HelloEvent
instance FromJSON HelloEvent where instance FromJSON HelloEvent where
parseJSON = fromPacket "hello-event" $ \o -> HelloEvent parseJSON = fromPacket "hello-event" $ \o -> HelloEvent
<$> o .:? "account" <$> o .:? "account"
@ -277,6 +354,8 @@ newtype JoinEvent = JoinEvent
{ joinSession :: SessionView { joinSession :: SessionView
} deriving (Show) } deriving (Show)
makeLensesL ''JoinEvent
instance FromJSON JoinEvent where instance FromJSON JoinEvent where
parseJSON = fromPacket "join-event" $ \o -> JoinEvent parseJSON = fromPacket "join-event" $ \o -> JoinEvent
<$> parseJSON (Object o) <$> parseJSON (Object o)
@ -288,6 +367,8 @@ newtype LoginEvent = LoginEvent
{ loginAccountId :: Snowflake { loginAccountId :: Snowflake
} deriving (Show) } deriving (Show)
makeLensesL ''LoginEvent
instance FromJSON LoginEvent where instance FromJSON LoginEvent where
parseJSON = fromPacket "login-event" $ \o -> LoginEvent parseJSON = fromPacket "login-event" $ \o -> LoginEvent
<$> o .: "acount_id" <$> o .: "acount_id"
@ -310,6 +391,8 @@ data NetworkEvent = NetworkEvent
, networkServerEra :: T.Text , networkServerEra :: T.Text
} deriving (Show) } deriving (Show)
makeLensesL ''NetworkEvent
instance FromJSON NetworkEvent where instance FromJSON NetworkEvent where
parseJSON = fromPacket "network-event" $ \o -> NetworkEvent parseJSON = fromPacket "network-event" $ \o -> NetworkEvent
<$> o .: "type" <$> o .: "type"
@ -326,6 +409,8 @@ data NickEvent = NickEvent
, nickTo :: T.Text , nickTo :: T.Text
} deriving (Show) } deriving (Show)
makeLensesL ''NickEvent
instance FromJSON NickEvent where instance FromJSON NickEvent where
parseJSON = fromPacket "nick-event" $ \o -> NickEvent parseJSON = fromPacket "nick-event" $ \o -> NickEvent
<$> o .: "session_id" <$> o .: "session_id"
@ -341,6 +426,8 @@ data EditMessageEvent = EditMessageEvent
, editMessageEditId :: Snowflake , editMessageEditId :: Snowflake
} deriving (Show) } deriving (Show)
makeLensesL ''EditMessageEvent
instance FromJSON EditMessageEvent where instance FromJSON EditMessageEvent where
parseJSON = fromPacket "EditMessageEvent" $ \o -> EditMessageEvent parseJSON = fromPacket "EditMessageEvent" $ \o -> EditMessageEvent
<$> parseJSON (Object o) <$> parseJSON (Object o)
@ -353,6 +440,8 @@ newtype PartEvent = PartEvent
{ partSession :: SessionView { partSession :: SessionView
} deriving (Show) } deriving (Show)
makeLensesL ''PartEvent
instance FromJSON PartEvent where instance FromJSON PartEvent where
parseJSON = fromPacket "part-event" $ \o -> PartEvent parseJSON = fromPacket "part-event" $ \o -> PartEvent
<$> parseJSON (Object o) <$> parseJSON (Object o)
@ -365,6 +454,8 @@ data PingEvent = PingEvent
, pingNext :: UTCTime , pingNext :: UTCTime
} deriving (Show) } deriving (Show)
makeLensesL ''PingEvent
instance FromJSON PingEvent where instance FromJSON PingEvent where
parseJSON = fromPacket "ping-event" $ \o -> PingEvent parseJSON = fromPacket "ping-event" $ \o -> PingEvent
<$> (posixSecondsToUTCTime <$> o .: "time") <$> (posixSecondsToUTCTime <$> o .: "time")
@ -380,6 +471,8 @@ data PmInitiateEvent = PmInitiateEvent
, pmInitiatePmId :: Snowflake , pmInitiatePmId :: Snowflake
} deriving (Show) } deriving (Show)
makeLensesL ''PmInitiateEvent
instance FromJSON PmInitiateEvent where instance FromJSON PmInitiateEvent where
parseJSON = fromPacket "pm-initiate-event" $ \o -> PmInitiateEvent parseJSON = fromPacket "pm-initiate-event" $ \o -> PmInitiateEvent
<$> o .: "from" <$> o .: "from"
@ -394,6 +487,8 @@ newtype SendEvent = SendEvent
{ sendMessage :: Message { sendMessage :: Message
} deriving (Show) } deriving (Show)
makeLensesL ''SendEvent
instance FromJSON SendEvent where instance FromJSON SendEvent where
parseJSON = fromPacket "send-event" $ \o -> SendEvent parseJSON = fromPacket "send-event" $ \o -> SendEvent
<$> parseJSON (Object o) <$> parseJSON (Object o)
@ -412,6 +507,8 @@ data SnapshotEvent = SnapshotEvent
, snapshotPmWithUserId :: Maybe UserId , snapshotPmWithUserId :: Maybe UserId
} deriving (Show) } deriving (Show)
makeLensesL ''SnapshotEvent
instance FromJSON SnapshotEvent where instance FromJSON SnapshotEvent where
parseJSON = fromPacket "snapshot-event" $ \o -> SnapshotEvent parseJSON = fromPacket "snapshot-event" $ \o -> SnapshotEvent
<$> o .: "identity" <$> o .: "identity"

View file

@ -0,0 +1,13 @@
module Haboli.Euphoria.Lens
( makeLensesL
) where
import Language.Haskell.TH
import Lens.Micro.TH
import Lens.Micro
rename :: Name -> [Name] -> Name -> [DefName]
rename _ _ name = [TopName $ mkName $ nameBase name ++ "L"]
makeLensesL :: Name -> DecsQ
makeLensesL = makeLensesWith $ lensRules & lensField .~ rename