packages feed

marvin 0.1.5 → 0.2.0

raw patch · 10 files changed

+196/−16 lines, 10 filesdep +conduitdep +irc-conduitPVP ok

version bump matches the API change (PVP)

Dependencies added: conduit, irc-conduit

API changes (from Hackage documentation)

- Marvin.Types: Script :: (Handlers a_au4M) -> ScriptId -> Config -> a_au4M -> Script a_au4M
+ Marvin.Adapter.IRC: data IRCAdapter
+ Marvin.Adapter.IRC: data IRCChannel
+ Marvin.Adapter.IRC: instance Marvin.Internal.Types.IsAdapter Marvin.Adapter.IRC.IRCAdapter
+ Marvin.Adapter.Telegram.Poll: class HasFirstName s a | s -> a
+ Marvin.Adapter.Telegram.Poll: class HasId_ s a | s -> a
+ Marvin.Adapter.Telegram.Poll: class HasLastName s a | s -> a
+ Marvin.Adapter.Telegram.Poll: class HasType_ s a | s -> a
+ Marvin.Adapter.Telegram.Poll: class HasUsername s a | s -> a
+ Marvin.Adapter.Telegram.Poll: firstName :: HasFirstName s a => Lens' s a
+ Marvin.Adapter.Telegram.Poll: id_ :: HasId_ s a => Lens' s a
+ Marvin.Adapter.Telegram.Poll: lastName :: HasLastName s a => Lens' s a
+ Marvin.Adapter.Telegram.Poll: type_ :: HasType_ s a => Lens' s a
+ Marvin.Adapter.Telegram.Poll: username :: HasUsername s a => Lens' s a
+ Marvin.Adapter.Telegram.Push: class HasFirstName s a | s -> a
+ Marvin.Adapter.Telegram.Push: class HasId_ s a | s -> a
+ Marvin.Adapter.Telegram.Push: class HasLastName s a | s -> a
+ Marvin.Adapter.Telegram.Push: class HasType_ s a | s -> a
+ Marvin.Adapter.Telegram.Push: class HasUsername s a | s -> a
+ Marvin.Adapter.Telegram.Push: firstName :: HasFirstName s a => Lens' s a
+ Marvin.Adapter.Telegram.Push: id_ :: HasId_ s a => Lens' s a
+ Marvin.Adapter.Telegram.Push: lastName :: HasLastName s a => Lens' s a
+ Marvin.Adapter.Telegram.Push: type_ :: HasType_ s a => Lens' s a
+ Marvin.Adapter.Telegram.Push: username :: HasUsername s a => Lens' s a
+ Marvin.Types: actions :: HasActions s a => Lens' s a
+ Marvin.Types: adapter :: HasAdapter s a => Lens' s a
+ Marvin.Types: class HasActions s a | s -> a
+ Marvin.Types: class HasAdapter s a | s -> a
+ Marvin.Types: class HasPayload s a | s -> a
+ Marvin.Types: class HasScriptId s a | s -> a
+ Marvin.Types: data BotActionState a_atqV d_atqW
+ Marvin.Types: payload :: HasPayload s a => Lens' s a
+ Marvin.Types: scriptId :: HasScriptId s a => Lens' s a
- Marvin: data Script a_au4M
+ Marvin: data Script a_auft
- Marvin.Types: data Script a_au4M
+ Marvin.Types: data Script a_auft

Files

CHANGELOG.md view
@@ -1,3 +1,8 @@+# 0.2.0 - 25.02.2017++- Implemented an adapter for IRC+- Added manual lens class instances to expose the datatyes and instances using them.+ # 0.1.2, 0.1.3, 0.1.4, 0.1.5 - 17.02.2017  - Added manual version bounds to be compatible with stackage and the hackage build (sorry for the version spam)
README.md view
@@ -1,4 +1,4 @@-# Marvin, the paranoid bot (⍺ stage)+# Marvin, the paranoid bot (β stage)  [![Travis](https://travis-ci.org/JustusAdam/marvin.svg?branch=master)](https://travis-ci.org/JustusAdam/marvin) [![Hackage](https://img.shields.io/hackage/v/marvin.svg)](http://hackage.haskell.org/package/marvin)@@ -19,7 +19,10 @@ - [Repository](https://github.com/JustusAdam/marvin) - [Bugtracker](https://github.com/JustusAdam/marvin/issues) - [Documentation repository](https://github.com/JustusAdam/marvin-docs) and [bugtracker](https://github.com/JustusAdam/marvin-docs/issues)+- [Slack channel][slack-channel] ([signup instructions](#testing-and-talking)) +[slack-channel]: https://marvin-bot.slack.com+ ## A teaser  ```Haskell@@ -53,6 +56,12 @@          send $(isL "Hello #{username} welcome to the random channel!") ```++## Testing and Talking++There's a [slack channel][slack-channel] where you can ask questions or play around with a [test instance of marvin](https://github.com/JustusAdam/marvin/blob/master/test/integration/slack/Script1.hs).++It's currently invite-only, so [send me an email](mailto:dev@justus.science) if you would like to join.  ## Contributing 
marvin.cabal view
@@ -1,15 +1,18 @@ name:                marvin-version:             0.1.5-synopsis:            A modular chat bot-description:         A framework for writing portable chat bots. Inspired by hubot. The documentation is on readthedocs: <https://marvin.readthedocs.io>+version:             0.2.0+synopsis:            A framework for modular, portable chat bots.+description:         A framework for writing portable chat bots. Inspired by hubot. +                     . +                     The best way to get started with this library is with the documentation on readthedocs: <https://marvin.readthedocs.io> homepage:            https://marvin.readthedocs.io+bug-reports:         https://github.com/JustusAdam/marvin/issues license:             BSD3 license-file:        LICENSE-author:              JustusAdam+author:              Justus Adam maintainer:          dev@justus.science-copyright:           Copyright: (c) 2016 Justus Adam+copyright:           © 2016, 2017 Justus Adam category:            Development-stability:           Beta+stability:           experimental build-type:          Simple extra-source-files:  README.md                    , CHANGELOG.md@@ -33,6 +36,7 @@                      , Marvin.Util.HTTP                      , Marvin.Adapter                      , Marvin.Adapter.Shell+                     , Marvin.Adapter.IRC                      , Marvin.Adapter.Slack.RTM                      , Marvin.Adapter.Slack.EventsAPI                      , Marvin.Adapter.Telegram.Push@@ -79,6 +83,8 @@                      , http-client-tls >= 0.2 && < 0.4                      , optparse-applicative >= 0.11 && < 1                      , transformers >= 0.4+                     , conduit >= 1.2+                     , irc-conduit >= 0.2   default-language:    Haskell2010   default-extensions:  OverloadedStrings                      , TypeFamilies
+ src/Marvin/Adapter/IRC.hs view
@@ -0,0 +1,124 @@+{-|+Module      : $Header$+Description : Adapter for communicating with IRC.+Copyright   : (c) Justus Adam, 2017+License     : BSD3+Maintainer  : dev@justus.science+Stability   : experimental+Portability : POSIX++See caveats and potential issues with this adapter here <https://marvin.readthedocs.io/en/latest/adapters.html#irc>.+-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE Rank2Types     #-}+module Marvin.Adapter.IRC +    ( IRCAdapter, IRCChannel+    ) where+++import           Control.Concurrent.Async.Lifted+import           Control.Concurrent.Chan.Lifted+import           Control.Exception.Lifted+import           Control.Lens+import           Control.Monad+import           Control.Monad.IO.Class+import           Control.Monad.Logger+import           Data.ByteString                 (ByteString)+import           Data.Conduit+import           Data.Maybe+import qualified Data.Text                       as T+import qualified Data.Text.Encoding              as T+import qualified Data.Text.Lazy                  as L+import qualified Data.Text.Lazy.Encoding         as L+import           Data.Time.Clock                 (getCurrentTime)+import           Marvin.Adapter+import           Marvin.Interpolate.All+import           Marvin.Types                    as MT+import           Network.IRC.Conduit             as IRC+++type MarvinIRCMsg = IRC.Message L.Text+++-- Im not happy with this yet, we need to distinguish users and channels somehow+data IRCChannel+    = RealChannel { chanName :: L.Text }+    | Direct      { chanName :: L.Text }+++data IRCAdapter = IRCAdapter+    { msgOutChan :: Chan MarvinIRCMsg+    }+++producer :: Chan MarvinIRCMsg -> Producer IO IrcMessage+producer chan = forever $ do+    msg <- readChan chan+    yield $ T.encodeUtf8 . L.toStrict <$> msg+++consumer = awaitForever . writeChan+++processor :: Chan (Either ByteString IrcEvent) -> EventHandler IRCAdapter -> AdapterM IRCAdapter ()+processor inChan handler = do+    IRCAdapter{msgOutChan} <- getAdapter+    let handleOneMessage = readChan inChan >>= \case+            Left bs -> logInfoN $(isT "Undecodable message: #{T.decodeUtf8 bs}")+            Right rawEv -> do+                let ev = fmap (L.fromStrict . T.decodeUtf8) rawEv+                ts <- liftIO $ TimeStamp <$> getCurrentTime+                let (user, channel) = case _source ev of+                                        User nick -> (nick, Direct nick)+                                        Channel chan user -> (user, RealChannel chan)+                case _message ev of+                    Privmsg _ (Right msg) ->+                        runHandler $ CommandEvent user channel msg ts+                    Notice target (Right msg) -> do+                        botname <- getBotname+                        -- Check if bot is addressed+                        runHandler $ (if target == botname then CommandEvent else MessageEvent) user channel msg ts+                    Join channel' -> runHandler $ ChannelJoinEvent user (RealChannel channel') ts+                    Part channel' _ -> runHandler $ ChannelLeaveEvent user (RealChannel channel') ts+                    Kick channel' nick _ -> runHandler $ ChannelLeaveEvent nick (RealChannel channel') ts+                    Topic channel' t -> runHandler $ TopicChangeEvent user (RealChannel channel') t ts+                    Ping a b -> writeChan msgOutChan $ Pong $ fromMaybe a b+                    Invite chan _ -> writeChan msgOutChan $ Join chan+                    _ -> logDebugN $(isT "Unhadeled event #{rawEv}")+    forever $+        handleOneMessage `catch` (\e -> logErrorN $(isT "UserError: #{e :: ErrorCall}"))+  where+    runHandler = void . async . liftIO . handler++++instance IsAdapter IRCAdapter where+    -- | Stores the username+    type User IRCAdapter = L.Text+    -- | Stores channel name+    type Channel IRCAdapter = IRCChannel++    adapterId = "irc"+    messageChannel chan msg = do+        IRCAdapter{msgOutChan} <- getAdapter+        writeChan msgOutChan $ msgType $ Right msg+      where+        msgType = case chan of+                      Direct n -> Privmsg n+                      RealChannel c -> Notice c+    -- | Just returns the value again+    getUsername = return++    getChannelName = return . chanName+    resolveChannel = return . Just . RealChannel++    -- | Just returns the value again+    resolveUser = return . Just+    initAdapter = IRCAdapter <$> newChan+    runWithAdapter handler = do+        port <- fromMaybe 7000 <$> lookupFromAdapterConfig "port"+        host <- requireFromAdapterConfig "host"+        IRCAdapter{msgOutChan} <- getAdapter+        inChan <- newChan+        async $ processor inChan handler+        liftIO $ ircClient port host (return ()) (consumer inChan) (producer msgOutChan)
src/Marvin/Adapter/Slack/Types.hs view
@@ -42,7 +42,12 @@ deriveJSON defaultOptions { unwrapUnaryRecords = True } ''SlackUserId deriveJSON defaultOptions { unwrapUnaryRecords = True } ''SlackChannelId -+class HasTopic s a | s -> a where topic :: Lens' s a+class HasName s a | s -> a where name :: Lens' s a+class HasIdValue s a | s -> a where idValue :: Lens' s a+class HasUsername s a | s -> a where username :: Lens' s a+class HasNameResolver s a | s -> a where nameResolver :: Lens' s a+class HasInfoCache s a | s -> a where infoCache :: Lens' s a  declareFields [d|     data LimitedChannelInfo = LimitedChannelInfo
src/Marvin/Adapter/Telegram/Common.hs view
@@ -28,8 +28,8 @@ import           Control.Exception.Lifted  data APIResponse a-    = Success { description :: Maybe T.Text, result :: a}-    | Error { errorCode :: Int, errDescription :: T.Text}+    = Success { description :: Maybe T.Text, result :: a }+    | Error { errorCode :: Int, errDescription :: T.Text }   -- | The telegram adapter type for a particular update type. Either 'Push' or 'Poll'@@ -50,6 +50,12 @@     | ChannelChat  +class HasUsername s a | s -> a where username :: Lens' s a+class HasLastName s a | s -> a where lastName :: Lens' s a+class HasId_ s a | s -> a where id_ :: Lens' s a+class HasFirstName s a | s -> a where firstName :: Lens' s a+class HasType_ s a | s -> a where type_ :: Lens' s a+ -- | A user object as contained in the telegram update objects declareFields [d|     data TelegramUser = TelegramUser@@ -213,11 +219,9 @@     runWithAdapter = runnerImpl     getUsername = getUsernameImpl     getChannelName = getChannelNameImpl-    -- | Not supported in this adapter and always returns 'Nothing'     resolveChannel _ = do         logErrorN "Channel resolving not supported"         return Nothing-    -- | Not supported in this adapter and always returns 'Nothing'     resolveUser _ = do         logErrorN "User resolving not supported"         return Nothing
src/Marvin/Adapter/Telegram/Poll.hs view
@@ -6,6 +6,11 @@ Maintainer  : dev@justus.science Stability   : experimental Portability : POSIX+++=== Caveats:++'resolveUser' and 'resolveChannel' resolving are not yet supported in this adapter and always returns 'Nothing'. See <https://github.com/JustusAdam/marvin/issues/10 #10>. -} {-# LANGUAGE CPP #-} module Marvin.Adapter.Telegram.Poll@@ -13,6 +18,7 @@     , TelegramChat(..), ChatType(..)     , TelegramUser(..)     , MkTelegram+    , HasUsername(username), HasLastName(lastName), HasId_(id_), HasFirstName(firstName), HasType_(type_)     ) where  
src/Marvin/Adapter/Telegram/Push.hs view
@@ -6,6 +6,12 @@ Maintainer  : dev@justus.science Stability   : experimental Portability : POSIX+++=== Caveats:++'resolveUser' and 'resolveChannel' resolving are not yet supported in this adapter and always returns 'Nothing'. See <https://github.com/JustusAdam/marvin/issues/10 #10>.+ -} {-# LANGUAGE NamedFieldPuns #-} module Marvin.Adapter.Telegram.Push@@ -13,6 +19,7 @@     , TelegramChat(..), ChatType(..)     , TelegramUser(..)     , MkTelegram+    , HasUsername(username), HasLastName(lastName), HasId_(id_), HasFirstName(firstName), HasType_(type_)     ) where  
src/Marvin/Internal/Types.hs view
@@ -98,9 +98,20 @@ newtype AdapterId a = AdapterId { unwrapAdapterId :: T.Text } deriving (Show, Eq)  -class HasScriptId s a | s -> a where-    scriptId :: Lens' s a-+class HasScriptId s a | s -> a where scriptId :: Lens' s a+class HasConfig s a | s -> a where config :: Lens' s a+class HasAdapter s a | s -> a where adapter :: Lens' s a+class HasPayload s a | s -> a where payload :: Lens' s a+class HasTopicChangeIn s a | s -> a where topicChangeIn :: Lens' s a+class HasTopicChange s a | s -> a where topicChange :: Lens' s a+class HasResponds s a | s -> a where responds :: Lens' s a+class HasLeavesFrom s a | s -> a where leavesFrom :: Lens' s a+class HasLeaves s a | s -> a where leaves :: Lens' s a+class HasJoinsIn s a | s -> a where joinsIn :: Lens' s a+class HasJoins s a | s -> a where joins :: Lens' s a+class HasHears s a | s -> a where hears :: Lens' s a+class HasCustoms s a | s -> a where customs :: Lens' s a+class HasActions s a | s -> a where actions :: Lens' s a  -- | Read only data available to a handler when the bot reacts to an event. declareFields [d|
src/Marvin/Types.hs view
@@ -9,7 +9,7 @@ Portability : POSIX -} module Marvin.Types-    ( User(..), Channel(..), Message(..), Script(..)+    ( User(..), Channel(..), Message(..), Script     , ScriptId, mkScriptId, unwrapScriptId     , applicationScriptId, IsScript, getScriptId     , HasConfigAccess, TimeStamp(..)@@ -17,6 +17,9 @@     , User'(..), Channel'(..)     , Get(getLens)     , Event(..), RunnerM+    , BotActionState+    , HasScriptId(scriptId), HasAdapter(adapter), HasPayload(..)+    , HasActions(actions)     ) where