packages feed

glirc 2.24 → 2.25

raw patch · 21 files changed

+273/−67 lines, 21 filesdep ~asyncdep ~freedep ~hookup

Dependency ranges changed: async, free, hookup, kan-extensions, lens, time, vty

Files

ChangeLog.md view
@@ -1,5 +1,12 @@ # Revision history for glirc2 +## 2.25+* `/ignore` can list ignores and supports full wildcard masks+* Updated C extension API+* Improved OTR extension to avoid interacting with ZNC replays+* Added `protocol-family` configuration option. Set to `inet` or+  `inet6` to force a particular IP protocol.+ ## 2.24  * `/query` now takes a message parameter and tab-completes
README.md view
@@ -16,8 +16,7 @@ up-to-date:  ```-$ cabal update # if you're using cabal-$ stack update # if you're using stack+$ cabal update ```  To install the latest version from Hackage using cabal-install:@@ -33,16 +32,6 @@ $ cabal build ``` -Building with stack--```-$ stack init --resolver=lts-8 --solver-$ stack build-```--Note that if you're using a *nightly* resolver that you'll need to work around a-bug in stack by manually installing `alex` and `happy`.- Client Features =============== @@ -119,6 +108,7 @@     sasl-password: "somepass"     socks-host:    "socks5.example.com"     socks-port:    8080 -- defaults to 1080+    log-dir:       "/home/myuser/ircLogs"    * name: "example"     hostname:      "example.com"
glirc.cabal view
@@ -1,5 +1,5 @@ name:                glirc-version:             2.24+version:             2.25 synopsis:            Console IRC client description:         Console IRC client                      .@@ -89,6 +89,7 @@                        Client.Image.StatusLine                        Client.Image.Textbox                        Client.Log+                       Client.Mask                        Client.Message                        Client.Network.Async                        Client.Network.Connect@@ -104,6 +105,7 @@                        Client.View.ChannelInfo                        Client.View.Digraphs                        Client.View.Help+                       Client.View.IgnoreList                        Client.View.KeyMap                        Client.View.MaskList                        Client.View.Mentions@@ -133,13 +135,13 @@                        containers           >=0.5.7  && <0.6,                        directory            >=1.2.6  && <1.4,                        filepath             >=1.4.1  && <1.5,-                       free                 >=4.12   && <4.13,+                       free                 >=4.12   && <5.1,                        gitrev               >=1.2    && <1.4,                        hashable             >=1.2.4  && <1.3,-                       hookup               >=0.1    && <0.2,+                       hookup               >=0.2    && <0.3,                        irc-core             >=2.3    && <2.4,-                       kan-extensions       >=5.0    && <5.1,-                       lens                 >=4.14   && <4.16,+                       kan-extensions       >=5.0    && <5.2,+                       lens                 >=4.14   && <4.17,                        network              >=2.6.2  && <2.7,                        process              >=1.4.2  && <1.7,                        regex-tdfa           >=1.2    && <1.3,@@ -149,12 +151,12 @@                        stm                  >=2.4    && <2.5,                        template-haskell     >=2.11   && <2.13,                        text                 >=1.2.2  && <1.3,-                       time                 >=1.6    && <1.9,+                       time                 >=1.6    && <1.10,                        transformers         >=0.5.2  && <0.6,                        unix                 >=2.7    && <2.8,                        unordered-containers >=0.2.7  && <0.3,                        vector               >=0.11   && <0.13,-                       vty                  >=5.11.1 && <5.18+                       vty                  >=5.11.1 && <5.20  test-suite test   type:                exitcode-stdio-1.0
include/glirc-api.h view
@@ -40,8 +40,7 @@ };  struct glirc_command {-        const struct glirc_string *params;-        size_t params_n;+        struct glirc_string command; };  typedef void *start_type         (struct glirc *G, const char *path);
src/Client/CApi.hs view
@@ -167,11 +167,11 @@ -- | Notify an extension of a client command with the given parameters. commandExtension ::   Ptr ()          {- ^ client state stableptr -} ->-  [Text]          {- ^ parameters             -} ->+  Text            {- ^ command                -} ->   ActiveExtension {- ^ extension to command   -} ->   IO ()-commandExtension stab params ae = evalNestedIO $-  do cmd <- withCommand params+commandExtension stab command ae = evalNestedIO $+  do cmd <- withCommand command      let f = fgnCommand (aeFgn ae)      liftIO $ unless (f == nullFunPtr)             $ runProcessCommand f stab (aeSession ae) cmd@@ -209,12 +209,11 @@      nest1 $ with $ FgnChat net' tgt' msg'  withCommand ::-  [Text] {- ^ parameters -} ->+  Text {- ^ command -} ->   NestedIO (Ptr FgnCmd)-withCommand params =-  do prms          <- traverse withText params-     (prmN,prmPtr) <- nest2 $ withArrayLen prms-     nest1 $ with $ FgnCmd prmPtr (fromIntegral prmN)+withCommand command =+  do cmd <- withText command+     nest1 $ with $ FgnCmd cmd  withTag :: TagEntry -> NestedIO (FgnStringLen, FgnStringLen) withTag (TagEntry k v) =
src/Client/CApi/Types.hsc view
@@ -223,20 +223,16 @@  -- | @struct glirc_command@ data FgnCmd = FgnCmd-  { fcParams  :: Ptr FgnStringLen -- ^ array-  , fcParamN  :: CSize            -- ^ array length+  { fcCommand :: FgnStringLen   }  instance Storable FgnCmd where   alignment _ = #alignment struct glirc_command   sizeOf    _ = #size      struct glirc_command   peek p      = FgnCmd-            <$> (#peek struct glirc_command, params  ) p-            <*> (#peek struct glirc_command, params_n) p+            <$> (#peek struct glirc_command, command) p -  poke p FgnCmd{..} =-             do (#poke struct glirc_command, params  ) p fcParams-                (#poke struct glirc_command, params_n) p fcParamN+  poke p FgnCmd{..} = (#poke struct glirc_command, command) p fcCommand  ------------------------------------------------------------------------ 
src/Client/Commands.hs view
@@ -33,6 +33,7 @@ import           Client.Commands.Recognizer import           Client.Commands.WordCompletion import           Client.Configuration+import           Client.Mask import           Client.Message import           Client.State import           Client.State.Channel@@ -627,9 +628,29 @@    , Command       (pure "ignore")-      (remainingArg "nicks")-      "Toggle the soft-ignore on each of the space-delimited given nicknames.\n"-    $ ClientCommand cmdIgnore simpleClientTab+      (remainingArg "masks")+      "\^BParameters:\^B\n\+      \\n\+      \    masks: List of masks\n\+      \\n\+      \\^BDescription:\^B\n\+      \\n\+      \    Toggle the soft-ignore on each of the space-delimited given\n\+      \    nicknames. Ignores can use \^B*\^B (many) and \^B?\^B (one) wildcards.\n\+      \    Masks can be of the form: nick[[!user]@host]\n\+      \    Masks use a case-insensitive comparison.\n\+      \\n\+      \    If no masks are specified the current ignore list is displayed.\n\+      \\n\+      \\^BExamples:\^B\n\+      \\n\+      \    /ignore\n\+      \    /ignore nick1 nick2 nick3\n\+      \    /ignore nick@host\n\+      \    /ignore nick!user@host\n\+      \    /ignore *@host\n\+      \    /ignore *!baduser@*\n"+    $ ClientCommand cmdIgnore tabIgnore    , Command       (pure "grep")@@ -1890,15 +1911,28 @@  cmdIgnore :: ClientCommand String cmdIgnore st rest =-  case mkId . Text.pack <$> words rest of-    [] -> commandFailureMsg "bad arguments" st-    xs -> commandSuccess-            $ over clientIgnores updateIgnores st+  case mkId <$> Text.words (Text.pack rest) of+    [] -> commandSuccess (changeSubfocus FocusIgnoreList st)+    xs -> commandSuccess st2       where+        (newIgnores, st1) = (clientIgnores <%~ updateIgnores) st+        st2 = set clientIgnoreMask (buildMask (toList newIgnores)) st1+         updateIgnores :: HashSet Identifier -> HashSet Identifier         updateIgnores s = foldl' updateIgnore s xs+         updateIgnore s x = over (contains x) not s +-- | Complete the nickname at the current cursor position using the+-- userlist for the currently focused channel (if any)+tabIgnore :: Bool {- ^ reversed -} -> ClientCommand String+tabIgnore isReversed st _ =+  simpleTabCompletion mode hint completions isReversed st+  where+    hint          = activeNicks st+    completions   = currentCompletionList st ++ views clientIgnores toList st+    mode          = currentNickCompletionMode st+ -- | Implementation of @/reload@ -- -- Attempt to reload the configuration file@@ -2054,7 +2088,7 @@     -- the window line when that action was significant enough to     -- be considered a hint for tab completion.     summaryActor :: IrcSummary -> Maybe Identifier-    summaryActor (ChatSummary who) = Just who+    summaryActor (ChatSummary who) = Just $! userNick who     summaryActor _                 = Nothing  -- | Use the *!*@host masks of users for channel lists when setting list modes@@ -2114,13 +2148,13 @@     mode          = currentNickCompletionMode st  cmdExtension :: ClientCommand (String, String)-cmdExtension st (name,params) =+cmdExtension st (name,command) =   case find (\ae -> aeName ae == Text.pack name)             (view (clientExtensions . esActive) st) of         Nothing -> commandFailureMsg "unknown extension" st         Just ae ->           do (st',_) <- clientPark st $ \ptr ->-                          commandExtension ptr (Text.pack <$> words params) ae+                          commandExtension ptr (Text.pack command) ae              commandSuccess st'  -- | Implementation of @/exec@ command.
src/Client/Configuration.hs view
@@ -93,7 +93,7 @@   , _configMacros          :: Recognizer Macro -- ^ command macros   , _configExtensions      :: [FilePath] -- ^ paths to shared library   , _configUrlOpener       :: Maybe FilePath -- ^ paths to url opening executable-  , _configIgnores         :: HashSet Identifier -- ^ initial ignore list+  , _configIgnores         :: [Text] -- ^ initial ignore mask list   , _configActivityBar     :: Bool -- ^ initially visibility of the activity bar   , _configBellOnMention   :: Bool -- ^ notify terminal on mention   , _configHideMeta        :: Bool -- ^ default setting for hidemeta on new windows@@ -273,7 +273,7 @@                                "Extra words to highlight in chat messages"      _configNickPadding     <- sec' NoPadding "nick-padding" nickPaddingSpec                                "Amount of space to reserve for nicknames in chat messages"-     _configIgnores         <- sec' mempty "ignores" identifierSetSpec+     _configIgnores         <- sec' [] "ignores" valuesSpec                                "Set of nicknames to ignore on startup"      _configActivityBar     <- sec' False  "activity-bar" yesOrNoSpec                                "Show channel names and message counts for activity on\
src/Client/Configuration/ServerSettings.hs view
@@ -47,6 +47,7 @@   , ssAutoconnect   , ssNickCompletion   , ssLogDir+  , ssProtocolFamily    -- * Load function   , loadDefaultServerSettings@@ -68,7 +69,7 @@ import           Data.Text (Text) import qualified Data.Text as Text import           Irc.Identifier (Identifier, mkId)-import           Network.Socket (HostName, PortNumber)+import           Network.Socket (HostName, PortNumber, Family(..)) import           System.Environment  -- | Static server-level settings@@ -100,6 +101,7 @@   , _ssAutoconnect      :: Bool -- ^ Connect to this network on server startup   , _ssNickCompletion   :: WordCompletionMode -- ^ Nick completion mode for this server   , _ssLogDir           :: Maybe FilePath -- ^ Directory to save logs of chat+  , _ssProtocolFamily   :: Maybe Family -- ^ Protocol family to connect with   }   deriving Show @@ -148,6 +150,7 @@        , _ssAutoconnect      = False        , _ssNickCompletion   = defaultNickWordCompleteMode        , _ssLogDir           = Nothing+       , _ssProtocolFamily   = Nothing        }  serverSpec :: ValueSpecs (ServerSettings -> ServerSettings)@@ -249,7 +252,17 @@        , opt "log-dir" ssLogDir stringSpec         "Path to log file directory for this server"++      , opt "protocol-family" ssProtocolFamily protocolFamilySpec+        "IP protocol family to use for this connection"       ]+++-- | Specification for IP protocol family.+protocolFamilySpec :: ValueSpecs Family+protocolFamilySpec =+      AF_INET   <$ atomSpec "inet"+  <!> AF_INET6  <$ atomSpec "inet6"   nicksSpec :: ValueSpecs (NonEmpty Text)
src/Client/Image/PackedImage.hs view
@@ -45,8 +45,8 @@   = HorizText'       !Attr -- don't unpack, these get reused from the palette       {-# UNPACK #-} !S.Text-      {-# UNPACK #-} !Int-      {-# UNPACK #-} !Int+      {-# UNPACK #-} !Int -- terminal width+      {-# UNPACK #-} !Int -- codepoint count       !Image'   | EmptyImage'   deriving (Show)@@ -91,6 +91,7 @@            Nothing -> 0            Just ix -> ix +-- | Width in terms of terminal columns imageWidth :: Image' -> Int imageWidth = go 0   where
src/Client/Image/StatusLine.hs view
@@ -338,6 +338,7 @@     FocusKeyMap   -> Just $ string (view palLabel pal) "keymap"     FocusHelp mb  -> Just $ string (view palLabel pal) "help" <>                             opt mb+    FocusIgnoreList -> Just $ string (view palLabel pal) "ignores"     FocusRtsStats -> Just $ string (view palLabel pal) "rtsstats"     FocusMasks m  -> Just $ mconcat       [ string (view palLabel pal) "masks"
src/Client/Image/Textbox.hs view
@@ -144,7 +144,7 @@   String               {- ^ input text   -} ->   Image'               {- ^ output image -} renderLine st pal myNick nicks macros focused ('/':xs) =-  char defAttr '/' <> string attr cmd <> continue rest+  char defAttr '/' <> string attr cleanCmd <> continue rest   where     specAttr spec =       case parse st spec rest of@@ -152,6 +152,8 @@         Just{}  -> view palCommandReady pal      (cmd, rest) = break isSpace xs+    cleanCmd = map cleanChar cmd+     allCommands = (Left <$> macros) <> (Right <$> commands)     (attr, continue)       = case recognize (Text.pack cmd) allCommands of
+ src/Client/Mask.hs view
@@ -0,0 +1,102 @@+{-# Language OverloadedStrings #-}+module Client.Mask+  ( Mask+  , matchMask+  , buildMask+  ) where++import Irc.UserInfo+import Irc.Identifier+import Data.Text (Text)+import qualified Data.Text as Text+import Text.Regex.TDFA+import Text.Regex.TDFA.String (compile)+import Data.List++newtype Mask = Mask Regex++-- | Compile a list of masks down to a single, reuseable 'Mask' value+-- suitable for being used with 'matchMask'.+--+-- Masks can match zero-to-many arbitrary characters with @*@.+--+-- Masks can match one arbitrary character with @?@.+--+-- Literal @*@ @?@ and @|@ can be matched with a preceding @\@.+--+-- Missing host or username components of a mask will automatically+-- be treated as wildcards.+buildMask ::+  [Identifier] {- ^ masks -} ->+  Mask+buildMask patterns =+  case componentsToMask (map (translate . parseMaskComponents . idTextNorm) patterns) of+    Left e -> error e+    Right m -> m++-- | Determine if a given 'Mask' matches a given 'UserInfo'+matchMask :: Mask -> UserInfo -> Bool+matchMask (Mask re) userInfo =+  matchTest re (Text.unpack (normalized (renderUserInfo userInfo)))++normalized :: Text -> Text+normalized = idTextNorm . mkId++-- | Parse a mask into the nick, user, and hostname components+-- while replacing omitted components with @"*"@.+parseMaskComponents :: Text -> String+parseMaskComponents str = Text.unpack nick ++ "!" ++ user ++ "@" ++ host+  where+    (nickuser,rawhost) = Text.break (=='@') str+    (nick    ,rawuser) = Text.break (=='!') nickuser++    user = defaultWild rawuser+    host = defaultWild rawhost++    defaultWild x =+      case Text.uncons x of+        Nothing     -> "*"+        Just (_, y) -> Text.unpack y++componentsToMask :: [String] -> Either String Mask+componentsToMask xs =+  Mask <$> compile defaultCompOpt { multiline     = False }+                   defaultExecOpt { captureGroups = False }+                   ("^(" ++ intercalate "|" xs ++ ")$")++-- | Translate from the languge of masks to the language of+-- regular expressions.+--+-- Masks support the @*@ (many) and @?@ (one) wildcards. Wildcards+-- and @\@ can be escaped by preceding them with a @\@. All other+-- uses of @\@ are treated as matching the literal backslash.+translate :: String -> String+translate [] = []+translate ('\\' : '*'  : xs) = '\\' : '*'  : translate xs+translate ('\\' : '?'  : xs) = '\\' : '?'  : translate xs+translate ('\\' : '\\' : xs) = '\\' : '\\' : translate xs+translate ('*'         : xs) = '.'  : '*'  : translate xs+translate ('?'         : xs) = '.'  : '?'  : translate xs+translate (x           : xs)+  | isMetaChar x = '\\' : x : translate xs+  | otherwise    =        x : translate xs++-- | returns True iff the charactr is a regular expression meta character:+-- @^$\\.|*?+()[]{}@+isMetaChar :: Char -> Bool+isMetaChar c = case c of+  '^'  -> True+  '\\' -> True+  '.'  -> True+  '|'  -> True+  '*'  -> True+  '?'  -> True+  '+'  -> True+  '('  -> True+  ')'  -> True+  '['  -> True+  ']'  -> True+  '{'  -> True+  '}'  -> True+  '$'  -> True+  _    -> False
src/Client/Message.hs view
@@ -61,7 +61,7 @@   | PartSummary {-# UNPACK #-} !Identifier   | NickSummary {-# UNPACK #-} !Identifier {-# UNPACK #-} !Identifier   | ReplySummary {-# UNPACK #-} !ReplyCode-  | ChatSummary {-# UNPACK #-} !Identifier+  | ChatSummary {-# UNPACK #-} !UserInfo   | CtcpSummary {-# UNPACK #-} !Identifier   | NoSummary   deriving (Eq, Show)@@ -87,10 +87,10 @@     Part who _ _    -> PartSummary (userNick who)     Quit who _      -> QuitSummary (userNick who)     Nick who who'   -> NickSummary (userNick who) who'-    Privmsg who _ _ -> ChatSummary (userNick who)-    Notice who _ _  -> ChatSummary (userNick who)-    Ctcp who _ "ACTION" _ -> ChatSummary (userNick who)+    Privmsg who _ _ -> ChatSummary who+    Notice who _ _  -> ChatSummary who+    Ctcp who _ "ACTION" _ -> ChatSummary who     Ctcp who _ _ _ -> CtcpSummary (userNick who)-    CtcpNotice who _ _ _ -> ChatSummary (userNick who)+    CtcpNotice who _ _ _ -> ChatSummary who     Reply code _    -> ReplySummary code     _               -> NoSummary
src/Client/Network/Connect.hs view
@@ -33,6 +33,11 @@                     (view ssTlsServerCert args)                     (view ssTlsCiphers    args) +      family =+        case view ssProtocolFamily args of+          Nothing -> defaultFamily+          Just pf -> pf+       useSecure =         case view ssTls args of           UseInsecure    -> Nothing@@ -45,7 +50,8 @@                           (view ssSocksPort args)    in ConnectionParams-    { cpHost  = view ssHostName args+    { cpFamily = family+    , cpHost  = view ssHostName args     , cpPort  = ircPort args     , cpTls   = useSecure     , cpSocks = proxySettings
src/Client/State.hs view
@@ -33,6 +33,7 @@   , clientSubfocus   , clientNetworkMap   , clientIgnores+  , clientIgnoreMask   , clientConnection   , clientBell   , clientExtensions@@ -111,6 +112,7 @@ import           Client.Image.Message import           Client.Image.Palette import           Client.Log+import           Client.Mask import           Client.Message import           Client.Network.Async import           Client.State.Channel@@ -181,7 +183,8 @@    , _clientBell              :: !Bool                     -- ^ sound a bell next draw -  , _clientIgnores           :: !(HashSet Identifier)     -- ^ ignored nicknames+  , _clientIgnores           :: !(HashSet Identifier)     -- ^ ignored masks+  , _clientIgnoreMask        :: Mask                      -- ^ precomputed ignore regular expression (lazy)    , _clientExtensions        :: !ExtensionState           -- ^ state of loaded extensions   , _clientLogQueue          :: ![LogLine]                -- ^ log lines ready to write@@ -240,11 +243,13 @@    withExtensionState $ \exts -> -  do events         <- atomically newTQueue+  do events <- atomically newTQueue+     let ignoreIds = map mkId (view configIgnores cfg)      k ClientState         { _clientWindows           = _Empty # ()         , _clientNetworkMap        = _Empty # ()-        , _clientIgnores           = view configIgnores cfg+        , _clientIgnores           = HashSet.fromList ignoreIds+        , _clientIgnoreMask        = buildMask ignoreIds         , _clientConnections       = _Empty # ()         , _clientTextBox           = Edit.defaultEditBox         , _clientTextBoxOffset     = 0@@ -404,17 +409,17 @@     _                    -> Nothing   where     checkUser !who-      | identIgnored (userNick who) st = Just (userNick who)-      | otherwise                      = Nothing+      | identIgnored who st = Just (userNick who)+      | otherwise           = Nothing    -- | Predicate for nicknames to determine if messages should be ignored. identIgnored ::-  Identifier  {- ^ nickname     -} ->+  UserInfo    {- ^ target user  -} ->   ClientState {- ^ client state -} ->   Bool        {- ^ is ignored   -}-identIgnored who st = HashSet.member who (view clientIgnores st)+identIgnored who st = matchMask (view clientIgnoreMask st) who   -- | Record a message in the windows corresponding to the given target
src/Client/State/Focus.hs view
@@ -54,6 +54,7 @@   | FocusKeyMap      -- ^ Show key bindings   | FocusHelp (Maybe Text) -- ^ Show help window with optional command   | FocusRtsStats    -- ^ Show GHC RTS statistics+  | FocusIgnoreList    -- ^ Show ignored masks   deriving (Eq,Show)  -- | Unfocused first, followed by focuses sorted by network.
src/Client/View.hs view
@@ -19,6 +19,7 @@ import           Client.View.ChannelInfo import           Client.View.Digraphs import           Client.View.Help+import           Client.View.IgnoreList import           Client.View.KeyMap import           Client.View.MaskList import           Client.View.Mentions@@ -49,6 +50,7 @@     (_, FocusKeyMap) -> keyMapLines st     (_, FocusHelp mb) -> helpImageLines st mb pal     (_, FocusRtsStats) -> rtsStatsLines (view clientRtsStats st) pal+    (_, FocusIgnoreList) -> ignoreListLines (view clientIgnores st) pal     _ -> chatMessageImages focus w st   where     pal = clientPalette st
+ src/Client/View/IgnoreList.hs view
@@ -0,0 +1,44 @@+{-# Language OverloadedStrings #-}+{-|+Module      : Client.View.IgnoreList+Description : Line renderers for ignore mask list view+Copyright   : (c) Eric Mertens, 2016+License     : ISC+Maintainer  : emertens@gmail.com++This module renders the lines used to list the ignore masks.+-}+module Client.View.IgnoreList+  ( ignoreListLines+  ) where++import           Client.Image.PackedImage+import           Client.Image.Palette+import           Client.Image.Message+import           Graphics.Vty.Attributes+import           Irc.Identifier+import           Data.HashSet (HashSet)+import           Data.Foldable+import           Data.Semigroup+import           Control.Lens++-- | Render the lines used in a channel mask list.+ignoreListLines ::+  HashSet Identifier {- ^ ignore masks -} ->+  Palette            {- ^ palette      -} ->+  [Image']+ignoreListLines ignores pal =+  summaryLine ignores pal :+  [ text' defAttr (cleanText (idText mask))+  | mask <- toList ignores ]+++-- | Render a summary describing the number of ignore masks.+summaryLine ::+  HashSet Identifier {- ^ ignore masks -} ->+  Palette            {- ^ palette      -} ->+  Image'+summaryLine ignores pal+  | null ignores = text' (view palError pal) "Ignore list empty"+  | otherwise    = text' (view palLabel pal) "Ignore entries: "+                <> string defAttr (show (length ignores))
src/Client/View/Messages.hs view
@@ -31,6 +31,7 @@ import           Data.Semigroup import           Irc.Identifier import           Irc.Message+import           Irc.UserInfo   chatMessageImages :: Focus -> Int -> ClientState -> [Image']@@ -169,5 +170,5 @@         {- ^ Image, incoming identifier, outgoing identifier if changed -} metadataWindowLine st wl =   case view wlSummary wl of-    ChatSummary who -> (ignoreImage, who, Nothing) <$ guard (identIgnored who st)+    ChatSummary who -> (ignoreImage, userNick who, Nothing) <$ guard (identIgnored who st)     summary         -> metadataImg summary
src/Client/View/UrlSelection.hs view
@@ -31,6 +31,7 @@ import           Data.Text (Text) import           Graphics.Vty.Attributes import           Irc.Identifier+import           Irc.UserInfo (userNick) import           Text.Read (readMaybe)  @@ -76,7 +77,7 @@     QuitSummary who   -> Just who     PartSummary who   -> Just who     NickSummary who _ -> Just who-    ChatSummary who   -> Just who+    ChatSummary who   -> Just (userNick who)     CtcpSummary who   -> Just who     ReplySummary {}   -> Nothing     NoSummary         -> Nothing