packages feed

apelsin-1.2: src/ClanFetcher.hs

module ClanFetcher(
	Clan(..), TagExpr, ClanID, matchTagExpr, prettyTagExpr, tagExprGet, getClanList, clanListFromCache
	, matchClanByID
) where

import Prelude as P
import Control.Applicative
import Control.Exception
import Control.Monad
import Data.ByteString.Char8 as B
import Data.ByteString.Lazy.Char8 as L
import Data.List as List
import Data.Maybe
import Data.Ord
import Network.Socket
import Network.HTTP
import Network.Stream
import Network.URI
import Network.Tremulous.NameInsensitive

import TremFormatting
import Constants
import Monad2

data TagExpr
	= TagPrefix !TI
	| TagSuffix !TI
	| TagInfix !TI
	| TagContained !TI !TI
	deriving Eq

instance Ord TagExpr where
	compare = comparing tagExprGet

tagExprGet :: TagExpr -> TI
tagExprGet x = case x of
	TagPrefix v		-> v
	TagSuffix v		-> v
	TagInfix v		-> v
	TagContained v _	-> v

type ClanID = Int
data Clan = Clan
	{ clanID	:: !ClanID
	, name		:: !TI
	, website
	, irc		:: !B.ByteString
	, websitealive	:: !Bool
	, tagexpr	:: !TagExpr
	, clanserver	:: !(Maybe SockAddr)
	}

matchClanByID :: [Clan] -> TI -> ClanID -> Bool
matchClanByID clans raw cid = case List.find (\x -> clanID x == cid) clans of
	Just a -> matchTagExpr (tagexpr a) raw
	Nothing -> False

mkTagExpr :: B.ByteString -> Maybe TagExpr
mkTagExpr str
	| Just (x, xs) <- B.uncons str = case x of
		'<' -> Just (TagPrefix (mk xs))
		'>' -> Just (TagSuffix (mk xs))
		'^' -> Just (TagInfix (mk xs))
		'%' -> let (a, b) = B.break (=='%') xs
				in Just (TagContained (mk a) (mk (B.drop 1 b)))
		_   -> Nothing
	| otherwise = Nothing

matchTagExpr :: TagExpr -> TI -> Bool
matchTagExpr expr raw = case expr of
	TagPrefix (TI _ xs)		-> xs `B.isPrefixOf` str
	TagSuffix (TI _ xs)		-> xs `B.isSuffixOf` str
	TagInfix (TI _ xs)		-> xs `B.isInfixOf` str
	TagContained (TI _ xs) (TI _ ys)-> xs `B.isPrefixOf` str && ys `B.isSuffixOf` str
	where str = cleanedCase raw

prettyTagExpr :: TagExpr -> B.ByteString
prettyTagExpr expr = case expr of
	TagPrefix bs	-> esc bs `B.append` wild
	TagSuffix bs	-> wild `B.append` esc bs
	TagInfix bs	-> B.concat [wild, esc bs, wild]
	TagContained a b-> B.concat [esc a, wild, esc b]
	where	wild = "<span color=\"#BBB\">*</span>"
		esc  = htmlEscapeBS . original


rawToClan :: L.ByteString -> IO (Maybe [Clan])
rawToClan = fmap sequence . mapM (f . B.split '\t' . lazyToStrict) . P.filter (not . L.null) . L.split '\n' where
	f (rclanID:rname:rexpr:website:rwebsitealive:irc:rserver:_)
		| Just tagexpr <- mkTagExpr rexpr
		, Just (clanID,_) <- B.readInt rclanID = do
			let	(ip, port1)	= B.break (==':') rserver
				port		= B.drop 1 port1
				websitealive	= rwebsitealive == "1"
			clanserver <- if B.null ip || B.null port
				then return Nothing
				else getDNS (B.unpack ip) (B.unpack port)
			return $ Just Clan { name = mkAlphaNum rname, .. }
	f _	= return Nothing

getClanList :: String -> IO (Maybe [Clan])
getClanList url = do
	cont <- get url
	case cont of
		Nothing -> return Nothing
		Just raw -> do
			file	<- cacheFile
			clans	<- rawToClan raw
			when (isJust clans) $
				L.writeFile file raw
			return clans


clanListFromCache :: IO [Clan]
clanListFromCache = handle err $ do
	file <- cacheFile
	fromMaybe [] <$> (rawToClan =<< L.readFile file)
	where
	err (_ :: IOException) = return []


lazyToStrict :: L.ByteString -> B.ByteString
lazyToStrict = B.concat . toChunks

cacheFile :: IO FilePath
cacheFile = inCacheDir "clans"


get :: HStream ty => String -> IO (Maybe ty)
get url = case parseURI url of
	Nothing -> return Nothing
	Just uri -> do
		resp <- ex $ simpleHTTP (mkRequest GET uri)
		return $ case resp of
			Right (Response (2,0,0) _  _ body)	-> Just body
			_					-> Nothing
	-- It doesn't catch everything apparently
	where ex = handle (\(_ :: IOException) -> return (Left ErrorReset))