summaryrefslogtreecommitdiff
path: root/ToxChat.hs
blob: fba5d33b5054adbabf3d8fdead9bf795b1f796f3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
{-# LANGUAGE CPP                   #-}
{-# LANGUAGE PartialTypeSignatures #-}
module ToxChat
    ( module Chat
    , module ToxChat
    ) where

import Control.Concurrent.STM
import Control.Monad
import Data.Char
import Data.Dependent.Sum
import Data.Function
import qualified Data.Map     as Map
         ;import Data.Map     (Map)
import Data.Maybe
import qualified Data.Text    as T
         ;import Data.Text    (Text)
import Debug.Trace

#ifdef THREAD_DEBUG
import Control.Concurrent.Lifted.Instrument
#else
import Control.Concurrent.Lifted
import GHC.Conc                  (labelThread)
#endif

import Chat
import Data.Tox.Msg
import DebugTag
import DPut
import MUC
import Network.Tox.AggregateSession

forkUntilSignaled :: String -> STM (IO ()) -> IO (IO ())
forkUntilSignaled lbl action = do
    quitSignal <- newTVarIO False
    t <- forkIO $ do
        fix $ \loop -> join $ atomically
            $ orElse (do readTVar quitSignal >>= check
                         return $ return ())
                     (fmap (>> loop) $ action)
    labelThread t lbl
    return $ atomically (writeTVar quitSignal True)

data RoomData k = RoomData
    { room           :: Maybe (Room k)
    , pendingInvites :: [(AggregateSession,Int,Maybe Text,Invite)]
    }

chatevents :: (Ord conkey, Show conkey) => TVar (Map Text (RoomData conkey)) -> STM (IO ())
chatevents rsvar = do
    rs <- readTVar rsvar
    if Map.null rs
      then retry
      else do
        ios <- flip Map.traverseWithKey rs $ \rkey r -> do
            ps <- maybe (return mempty) roomPending $ room r
            trace ("roomPending " ++ show ps) $ return ()
            case Map.toList ps of
                (k,t):ts -> do
                    roomCommit (fromJust $ room r) k t
                    modifyTVar' rsvar
                        $ Map.adjust (\d -> d { pendingInvites = take 1 (pendingInvites d)})
                                     rkey
                    return $ do
                        dput XJabber $ "toxchat-committed " ++ show (rkey,length ts,k,t)
                        case membershipEffect $ chatMessage t of
                            MembershipEffect Outside Inside -> do
                                forM_ (pendingInvites r) $ \(c,i,jid,inv) -> do
                                    -- TODO b <- checkCompatible me them c
                                    dput XJabber $ "Replying to invite " ++ show inv
                                    dispatchMessage c (Just i)
                                        $ Pkt INVITE_GROUPCHAT ==> inv { invite = AcceptedInvite }
                            _ -> return ()
                _ -> retry
        return $ foldl1 (>>) ios

data InviteCache m = InviteCache
    { rememberInvite :: AggregateSession -> Int {- session ID -} -> Maybe Text {- origin -} -> Invite -> m ()
    , lookupInvite   :: Text {- chatroom JID -} -> m (Maybe Invite)
    }

forkToxChat :: MUC -> IO (IO (), InviteCache IO)
forkToxChat muc = do
    (chan, rs) <- atomically $ do
        c <- dupTChan (mucChan muc)
        rs <- newTVar Map.empty -- TODO: This seems to redundantly duplicate MUC{mucRooms}.
        return (c,rs)
    quit <- forkUntilSignaled "toxchat" $ orElse (chatevents rs) $ do
        e <- readTChan chan
        case e of
            MUCCreate room jid nick r -> modifyTVar' rs $
                Map.alter (\d -> Just $ RoomData (Just r) (maybe [] pendingInvites d))
                          (T.toLower room)
        return $ case e of
            MUCCreate room jid nick _ ->
                dput XJabber $ unwords $ map T.unpack
                    [ "MUCCreate", room, jid, nick ]
    return $ (,) quit InviteCache
        { rememberInvite = \c i jid inv -> do
            dput XJabber $ "remember invite " ++ show (T.pack $ show $ inviteChatID inv, i, jid )
            atomically $ do
              modifyTVar' rs $ Map.alter (\d -> Just $ RoomData (room =<< (d:: Maybe (RoomData _)))
                                                                ( ((c,i,jid,inv) :) $ maybe [] pendingInvites d))
                                       (T.pack $ map toLower $ show $ inviteChatID inv)
        , lookupInvite = \_ -> return Nothing
        }