Skip to content

Commit

Permalink
deduplicate channel lists
Browse files Browse the repository at this point in the history
  • Loading branch information
kmein committed Jan 14, 2025
1 parent 9111e01 commit 38db9fd
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 45 deletions.
16 changes: 9 additions & 7 deletions src/Brockman/Bot.hs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import Control.Concurrent.Chan
import Data.ByteString (ByteString)
import Data.Conduit
import Data.Conduit.List (sourceList)
import Data.Foldable (toList)
import Data.Maybe
import Data.Set (Set)
import qualified Data.Text as T
import Data.Text.Encoding (encodeUtf8)
import qualified Network.IRC.Conduit as IRC
Expand All @@ -30,30 +32,30 @@ withIrcConnection BrockmanConfig {configIrc} listen speak = do
host = ircHost configIrc
tls = ircTls configIrc == Just True

handshake :: Nick -> [Channel] -> ConduitM () IRC.IrcMessage IO ()
handshake :: Nick -> Set Channel -> ConduitM () IRC.IrcMessage IO ()
handshake nick channels = do
notice nick ("handshake, joining " <> show channels)
yield $ IRC.Nick $ encode nick
yield $ IRC.RawMsg $ "USER " <> encode nick <> " * 0 :" <> encode nick
mapM_ (yield . IRC.Join . encode) channels
mapM_ (yield . IRC.Join . encode) (toList channels)

deafen :: Nick -> ConduitM () IRC.IrcMessage IO ()
deafen nick = yield $ IRC.Mode (encode nick) False [] ["+D"] -- deafen to PRIVMSGs
deafen nick = yield $ IRC.Mode (encode nick) False [] ["+D"] -- deafen to PRIVMSGs in channels (query still works)

-- maybe join channels separated by comma

broadcastNotice :: (Monad m) => [Channel] -> T.Text -> ConduitT i (IRC.Message ByteString) m ()
broadcastNotice :: (Monad m) => Set Channel -> T.Text -> ConduitT i (IRC.Message ByteString) m ()
broadcastNotice channels message =
sourceList
[ IRC.Notice (encode channel) $ Right $ encodeUtf8 message
| channel <- channels
| channel <- toList channels
]

broadcast :: (Monad m) => [Channel] -> [T.Text] -> ConduitT i (IRC.Message ByteString) m ()
broadcast :: (Monad m) => Set Channel -> [T.Text] -> ConduitT i (IRC.Message ByteString) m ()
broadcast channels messages =
sourceList
[ IRC.Privmsg (encode channel) $ Right $ encodeIRCMessage message
| channel <- channels,
| channel <- toList channels,
message <- messages
]

Expand Down
21 changes: 11 additions & 10 deletions src/Brockman/Bot/Controller.hs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import qualified Data.ByteString as B
import Data.Conduit
import qualified Data.Map.Strict as M
import Data.Maybe
import qualified Data.Set as Set
import qualified Data.Text as T
import qualified Network.IRC.Conduit as IRC

Expand All @@ -42,7 +43,7 @@ controllerThread configMVar = do
case configController initialConfig of
Nothing -> pure ()
Just initialControllerConfig@ControllerConfig {controllerNick} ->
let initialControllerChannels = configChannel initialConfig : fromMaybe [] (controllerExtraChannels initialControllerConfig)
let initialControllerChannels = Set.insert (configChannel initialConfig) $ fromMaybe Set.empty (controllerExtraChannels initialControllerConfig)
listen chan =
forever $
await >>= \case
Expand All @@ -62,9 +63,9 @@ controllerThread configMVar = do
Just ["help"] -> writeChan chan $ Help $ decode channel
Just ["add", decode -> nick, decodeUtf8 -> url]
| "http" `T.isPrefixOf` url && isValidIrcNick nick ->
writeChan chan $
Add nick url $
if decode channel == configChannel initialConfig then Nothing else Just $ decode channel
writeChan chan $
Add nick url $
if decode channel == configChannel initialConfig then Nothing else Just $ decode channel
Just _ -> writeChan chan $ Help $ decode channel
_ -> pure ()
-- 376 is RPL_ENDOFMOTD
Expand All @@ -83,7 +84,7 @@ controllerThread configMVar = do
case command of
Help channel -> do
broadcast
[channel]
(Set.singleton channel)
[ "help — send this helpful message",
"add NICK FEED_URL — add a new bot to all channels I am in",
"dump — upload the current config/state somewhere you can see it",
Expand All @@ -100,9 +101,9 @@ controllerThread configMVar = do
]
Add nick url extraChannel ->
case M.lookup nick configBots of
Just BotConfig {botFeed} -> broadcast [fromMaybe configChannel extraChannel] [T.pack (show nick) <> " is already serving " <> botFeed]
Just BotConfig {botFeed} -> broadcast (Set.singleton $ fromMaybe configChannel extraChannel) [T.pack (show nick) <> " is already serving " <> botFeed]
Nothing -> do
liftIO $ update configMVar $ configBotsL . at nick ?~ BotConfig {botFeed = url, botDelay = Nothing, botExtraChannels = (: []) <$> extraChannel}
liftIO $ update configMVar $ configBotsL . at nick ?~ BotConfig {botFeed = url, botDelay = Nothing, botExtraChannels = Set.singleton <$> extraChannel}
_ <- liftIO $ forkIO $ eloop $ reporterThread configMVar nick
pure ()
Pinged serverName -> do
Expand All @@ -118,13 +119,13 @@ controllerThread configMVar = do
Subscribe user nick -> do
liftIO $ update configMVar $ configBotsL . at nick . mapped . botExtraChannelsL %~ insert user
notice nick $ show user <> " has subscribed"
broadcast [user] ["subscribed to " <> T.pack (show nick)]
broadcast (Set.singleton user) ["subscribed to " <> T.pack (show nick)]
Unsubscribe user nick -> do
liftIO $ update configMVar $ configBotsL . at nick . mapped . botExtraChannelsL %~ delete user
notice nick $ show user <> " has unsubscribed"
broadcast [user] ["unsubscribed from " <> T.pack (show nick)]
broadcast (Set.singleton user) ["unsubscribed from " <> T.pack (show nick)]
Dump channel ->
broadcast [channel] . (: []) =<< case configPastebin config of
broadcast (Set.singleton channel) . (: []) =<< case configPastebin config of
Just endpoint -> liftIO $ pasteJson endpoint config
Nothing -> pure "No pastebin set"
MOTD -> do
Expand Down
15 changes: 7 additions & 8 deletions src/Brockman/Bot/Reporter.hs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import qualified Data.Cache.LRU as LRU
import Data.Conduit
import qualified Data.Map.Strict as M
import Data.Maybe (fromMaybe, maybeToList)
import qualified Data.Set as Set
import qualified Data.Text as T (Text, pack, unpack, unwords, words)
import Data.Time.Clock (getCurrentTime)
import Network.HTTP.Client (HttpException (HttpExceptionRequest), HttpExceptionContent (ConnectionFailure, StatusCodeException))
Expand Down Expand Up @@ -60,7 +61,7 @@ reporterThread configMVar nick = do
config@BrockmanConfig {configChannel, configShortener, configShowEntryDate} <- readMVar configMVar
withIrcConnection config listen $ \chan -> do
withCurrentBotConfig nick configMVar $ \initialBotConfig -> do
handshake nick $ configChannel : fromMaybe [] (botExtraChannels initialBotConfig)
handshake nick $ Set.insert configChannel (fromMaybe Set.empty (botExtraChannels initialBotConfig))
deafen nick
_ <- liftIO $ forkIO $ feedThread nick configMVar True Nothing chan
forever $
Expand All @@ -85,24 +86,22 @@ reporterThread configMVar nick = do
Messaged user message ->
debug nick ("got a message from " <> show user <> ": " <> show message)
InfoRequested channel -> do
broadcast [channel] $
broadcast (Set.singleton channel) $
pure $
case view (configBotsL . at nick) currentConfig of
Just BotConfig {botFeed, botExtraChannels, botDelay} -> do
T.unwords $ [botFeed, T.pack (show (configChannel : fromMaybe [] botExtraChannels))] ++ maybeToList (T.pack . show <$> botDelay)
T.unwords $ [botFeed, T.pack (show (Set.insert configChannel $ fromMaybe Set.empty botExtraChannels))] ++ maybeToList (T.pack . show <$> botDelay)
_ -> "huh?"

Tick channel tick -> do
liftIO $ update configMVar $ configBotsL . at nick . mapped . botDelayL .~ tick
notice nick ("change tick speed to " <> show tick)
channelsForNick <- botChannels nick <$> liftIO (readMVar configMVar)
broadcastNotice (channel:channelsForNick) $ T.pack (show nick) <> " @ " <> T.pack (maybe "auto" ((<> " seconds") . show) tick)

broadcastNotice (Set.insert channel channelsForNick) $ T.pack (show nick) <> " @ " <> T.pack (maybe "auto" ((<> " seconds") . show) tick)
SetUrl channel url -> do
liftIO $ update configMVar $ configBotsL . at nick . mapped . botFeedL .~ url
notice nick $ "set url to " <> T.unpack url
channelsForNick <- botChannels nick <$> liftIO (readMVar configMVar)
broadcastNotice (channel:channelsForNick) $ T.pack (show nick) <> " -> " <> url
broadcastNotice (Set.insert channel channelsForNick) $ T.pack (show nick) <> " -> " <> url
Killed -> do
liftIO $ update configMVar $ configBotsL . at nick .~ Nothing
notice nick "killed"
Expand All @@ -125,7 +124,7 @@ reporterThread configMVar nick = do
liftIO $ writeChan chan $ Invited $ decode channel
Just (Right (IRC.Event _ _ (IRC.Kick channel nick' _)))
| nick == decode nick' ->
liftIO $ writeChan chan $ Kicked $ decode channel
liftIO $ writeChan chan $ Kicked $ decode channel
-- 376 is RPL_ENDOFMOTD
Just (Right (IRC.Event _ _ (IRC.Numeric 376 _))) ->
liftIO $ writeChan chan MOTD
Expand Down
2 changes: 1 addition & 1 deletion src/Brockman/Feed.hs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ deduplicate maybeLRU items
| Just lru <- maybeLRU,
Just capacity <- LRU.maxSize lru,
capacity >= genericLength items =
insertItems lru items
insertItems lru items
| otherwise = insertItems (LRU.newLRU (Just $ genericLength items * 2)) items
where
key = hash . itemLink
Expand Down
13 changes: 7 additions & 6 deletions src/Brockman/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import Data.Data (Data, constrFields, toConstr)
import Data.List (intercalate)
import Data.Map.Strict (Map, lookup, union)
import Data.Maybe (fromMaybe)
import Data.Set (Set)
import qualified Data.Set as Set
import Data.Text (Text)
import Data.Text.Encoding (decodeUtf8, encodeUtf8)
Expand Down Expand Up @@ -71,7 +72,7 @@ configBotsL = lens configBots (\config bots -> config {configBots = bots})
configControllerL :: Lens' BrockmanConfig (Maybe ControllerConfig)
configControllerL = lens configController (\config controller -> config {configController = controller})

controllerExtraChannelsL :: Lens' ControllerConfig (Maybe [Channel])
controllerExtraChannelsL :: Lens' ControllerConfig (Maybe (Set Channel))
controllerExtraChannelsL = lens controllerExtraChannels (\controller channels -> controller {controllerExtraChannels = channels})

botFeedL :: Lens' BotConfig URL
Expand All @@ -80,11 +81,11 @@ botFeedL = lens botFeed (\bot feed -> bot {botFeed = feed})
botDelayL :: Lens' BotConfig (Maybe Integer)
botDelayL = lens botDelay (\bot delay -> bot {botDelay = delay})

botExtraChannelsL :: Lens' BotConfig (Maybe [Channel])
botExtraChannelsL :: Lens' BotConfig (Maybe (Set Channel))
botExtraChannelsL = lens botExtraChannels (\bot channels -> bot {botExtraChannels = channels})

botChannels :: Nick -> BrockmanConfig -> [Channel]
botChannels nick config = (configChannel config :) $ fromMaybe [] $ botExtraChannels =<< Data.Map.Strict.lookup nick (configBots config)
botChannels :: Nick -> BrockmanConfig -> Set Channel
botChannels nick config = Set.insert (configChannel config) $ fromMaybe Set.empty $ botExtraChannels =<< Data.Map.Strict.lookup nick (configBots config)

mergeIrcConfig :: IrcConfig -> IrcConfig -> IrcConfig
mergeIrcConfig a b =
Expand Down Expand Up @@ -140,7 +141,7 @@ data BrockmanConfig = BrockmanConfig

data ControllerConfig = ControllerConfig
{ controllerNick :: Nick,
controllerExtraChannels :: Maybe [Channel]
controllerExtraChannels :: Maybe (Set Channel)
}
deriving (Data, Generic, Show, Typeable)

Expand All @@ -153,7 +154,7 @@ data IrcConfig = IrcConfig

data BotConfig = BotConfig
{ botFeed :: URL,
botExtraChannels :: Maybe [Channel],
botExtraChannels :: Maybe (Set Channel),
botDelay :: Maybe Integer
}
deriving (Data, Generic, Show, Typeable)
Expand Down
28 changes: 15 additions & 13 deletions src/Brockman/Util.hs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import Data.ByteString (ByteString)
import Data.ByteString.Char8 (all, uncons)
import Data.ByteString.Lazy (toStrict)
import Data.Char (isAsciiLower, isAsciiUpper)
import Data.List (delete, insert)
import Data.Set (Set)
import qualified Data.Set as Set
import Data.Text (Text, unpack)
import qualified Data.Text as T (words)
import Data.Text.Encoding (decodeUtf8With, encodeUtf8)
Expand Down Expand Up @@ -58,18 +59,19 @@ suicide = killThread =<< myThreadId
decodeUtf8 :: ByteString -> Text
decodeUtf8 = decodeUtf8With $ \_error _ -> Just '?'

insert :: (Ord a) => a -> Maybe [a] -> Maybe [a]
insert value list
| Just values <- list, value `elem` values = list
| otherwise = case Data.List.insert value <$> list of
Nothing -> Just [value]
Just xs -> Just xs

delete :: (Ord a) => a -> Maybe [a] -> Maybe [a]
delete value list = case Data.List.delete value <$> list of
Nothing -> Nothing
Just [] -> Nothing
Just xs -> Just xs
insert :: (Ord a) => a -> Maybe (Set a) -> Maybe (Set a)
insert value list =
Just $ case list of
Nothing -> Set.singleton value
Just values -> Set.insert value values

delete :: (Ord a) => a -> Maybe (Set a) -> Maybe (Set a)
delete value list =
case Set.delete value <$> list of
Just xs
| Set.null xs -> Nothing
| otherwise -> Just xs
Nothing -> Nothing

bsWords :: ByteString -> [ByteString]
bsWords = map encodeUtf8 . T.words . decodeUtf8
Expand Down

0 comments on commit 38db9fd

Please sign in to comment.