packages feed

rtorrent-rpc-0.3.0.0: Network/RTorrent/Peer.hs

{-# LANGUAGE TypeOperators, TypeFamilies, OverloadedStrings #-}

{-|
Module      : Peer
Copyright   : (c) Kai Lindholm, 2014, 2025
License     : MIT
Maintainer  : megantti@gmail.com
Stability   : experimental

For more info on actions, see "Network.RTorrent.Action".
-}

module Network.RTorrent.Peer (
    PeerId (..)
  , PeerInfo (..)
  , PeerAction

  , getPeerPartial
  , allPeers
  , getTorrentPeers

  -- * Control peers
  , banPeer
  , disconnectPeer

  -- * Functions for single variables
  , getPeerHash
  , getPeerIp

  , getPeerClientVersion
  , getPeerUpRate
  , getPeerDownRate
  , getPeerUpTotal
  , getPeerDownTotal
  , getPeerEncrypted
  , getPeerCompletedPercent
  , getPeerPort
) where

import Control.Applicative

import qualified Data.Map as M
import qualified Data.Vector as V
import qualified Data.Text as T

import Network.RTorrent.Action.Internals
import Network.RTorrent.Torrent
import Network.RTorrent.Command
import Network.RTorrent.Value

data PeerId = PeerId !TorrentId !T.Text
    deriving Show

instance RpcType PeerId where
    toValue (PeerId (TorrentId tid) i) = ValueString $ tid <> ":p" <> i
    fromValue v = uncurry PeerId <$> (parse =<< fromValue v)
      where
        parse :: (Monad m, MonadFail m) => T.Text -> m (TorrentId, T.Text)
        parse str = do
            [hash, s] <- return $ T.splitOn ":p" str
            return (TorrentId hash, s)

data PeerInfo = PeerInfo {
    peerClientVersion :: !T.Text
  , peerIp :: !T.Text
  , peerUpRate :: !Int
  , peerDownRate :: !Int
  , peerUpTotal :: !Int
  , peerDownTotal :: !Int
  , peerEncrypted :: !Bool
  , peerCompletedPercent :: !Int
  , peerPort :: !Int
  , peerId :: !PeerId
} deriving Show

getPeerHash :: PeerId -> PeerAction T.Text
getPeerHash = simpleAction "p.id" []

getPeerIp :: PeerId -> PeerAction T.Text
getPeerIp = simpleAction "p.address" []

getPeerClientVersion :: PeerId -> PeerAction T.Text
getPeerClientVersion = simpleAction "p.client_version" []

getPeerUpRate :: PeerId -> PeerAction Int
getPeerUpRate = simpleAction "p.up_rate" []

getPeerDownRate :: PeerId -> PeerAction Int
getPeerDownRate = simpleAction "p.down_rate" []

getPeerUpTotal :: PeerId -> PeerAction Int
getPeerUpTotal = simpleAction "p.up_total" []

getPeerDownTotal :: PeerId -> PeerAction Int
getPeerDownTotal = simpleAction "p.down_total" []

getPeerEncrypted :: PeerId -> PeerAction Bool
getPeerEncrypted = simpleAction "p.is_encrypted" []

getPeerCompletedPercent :: PeerId -> PeerAction Int
getPeerCompletedPercent = simpleAction "p.completed_percent" []

getPeerPort :: PeerId -> PeerAction Int
getPeerPort = simpleAction "p.port" []

-- | Get a partial peer. @PeerId@ can be gotten by running @allPeers@.
getPeerPartial :: PeerId -> PeerAction (PeerId -> PeerInfo)
getPeerPartial = runActionB $ PeerInfo
         <$> b getPeerClientVersion
         <*> b getPeerIp
         <*> b getPeerUpRate
         <*> b getPeerDownRate
         <*> b getPeerUpTotal
         <*> b getPeerDownTotal
         <*> b getPeerEncrypted
         <*> b getPeerCompletedPercent
         <*> b getPeerPort
  where
    b = ActionB

disconnectPeer :: PeerId -> PeerAction Int
disconnectPeer = simpleAction "p.disconnect" []

banPeer :: PeerId -> PeerAction Int
banPeer = simpleAction "p.banned.set" [PInt 1]

type PeerAction = Action PeerId

getTorrentPeers :: TorrentId -> TorrentAction (V.Vector PeerInfo)
getTorrentPeers = fmap (V.map contract) . allPeers getPeerPartial
  where
    contract (x :*: f) = f x

-- | Run the peer action on all peers that a torrent has.
allPeers :: (PeerId -> PeerAction a) -> TorrentId -> TorrentAction (V.Vector (PeerId :*: a))
allPeers p = fmap addId . (getTorrentId <+> allToMulti (allP (getPeerHash <+> p)))
  where
    addId (hash :*: peers) =
        V.map (\(phash :*: f) -> PeerId hash phash :*: f) peers
    allP :: (PeerId -> PeerAction a) -> AllAction PeerId a
    allP = AllAction (PeerId (TorrentId "") "") "p.multicall" (V.fromList [PString ""])