Commit 4dd60126 authored by Mart Lubbers's avatar Mart Lubbers

Initial merge clean-irc

parent 82d0a735
definition module IRCBot
from Data.Error import :: MaybeErrorString, :: MaybeError
from Data.Maybe import :: Maybe
from IRC import :: IRCMessage
/*
* Spawn an IRC Bot
*
* param: Hostname and port
* param: Startup commands that are sent initially. For example:
* [NICK "clooglebot" Nothing
* ,USER "cloogle" "0" "Cloogle bot"
* ,JOIN [("#cloogle",Nothing)]]
* param: Shutdown commands. For example
* [QUIT (Just "Bye")]
* param: Processing function
* param: command received by the server
* param: State
* param: World
* return: Maybe a response, the updated state and the updated world
* If the response is nothing the connection is closed
* All items in the list are sent back
* param: World
* return: Maybe an error, the state and the new world
*/
bot :: (String, Int) [IRCMessage] [IRCMessage] .a (IRCMessage -> (.a -> *(*World -> *(Maybe [IRCMessage], .a, *World)))) !*World -> *(Maybe String, .a, !*World)
implementation module IRCBot
import StdEnv
import Data.Either
import Data.Func
import Data.Maybe
import Data.Tuple
import Text
import IRC
import TCPServer.Connection
bot :: (String, Int) [IRCMessage] [IRCMessage] .a (IRCMessage -> (.a -> *(*World -> *(Maybe [IRCMessage], .a, *World)))) !*World -> *(Maybe String, .a, !*World)
bot (host, port) start end state bot w = appSnd3 snd $ connect host port
{ emptyConnection
& onConnect = onConnect
, onData = onData
} ("", state) w
where
onConnect s w = (Just (concat (map toString start)), connectionResponse s, w)
onData d (acc, s) w = case split "\r\n" (acc +++ d) of
[m,rest:xs]
= case parseIRCMessage $ m +++ "\r\n" of
// Do something with the error
(Left err) = (Nothing, {connectionResponse ("", s) & stop=True}, w)// (Error $ "IRC Parsing error: " +++ join "\n" err, chan, state, w)
(Right msg)
# acc = join "\r\n" [rest:xs]
# (mircc, s, w) = bot msg s w
| isNothing mircc = (Just (concat (map toString end)), {connectionResponse (acc, s) & stop=True}, w)
# tosendthis = concat (map toString (fromJust mircc))
# (tosend, cr, w) = onData "" (acc, s) w
= (Just (maybe tosendthis ((+++) tosendthis) tosend), cr, w)
[m] = (Nothing, connectionResponse (m, s), w)
MIT License
Copyright (c) 2017 Cloogle
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
CLM:=clm
override CLMFLAGS+=-nt
CLMLIBS:=\
-IL Platform\
-IL TCPIP\
-I ./libcloogle\
-I ./clean-selectloop/libraries
BINARIES:=IRC IRCBot cloogleirc #test
all: $(BINARIES)
%: %.icl $(wildcard */*.[id]cl *.[id]cl)
$(CLM) $(CLMLIBS) $(CLMFLAGS) $(basename $<) -o $@
clean:
$(RM) -r $(BINARIES) Clean\ System\ Files
# clean-irc
IRC library for Clean
## Example
As a proof of concept, a cloogle bot is created. Run `./cloogle-irc --help` for
more info. `run.sh` contains a script suitable for running it continously.
## Todo
- Check the commands one more time on syntax
module cloogleirc
import StdEnv
import Control.Applicative
import Control.Monad => qualified join
import Data.Either
import Data.Error
import Data.Func
import Data.Functor
import Data.List
import qualified Data.Map as DM
import Data.Maybe
import Data.Tuple
import Internet.HTTP
import System.CommandLine
import System.Time
import Text
import Text.Encodings.UrlEncoding
import Text.GenJSON
import Cloogle.API
import IRC
import IRCBot
shorten :: String *World -> (String, *World)
shorten s w
# s = if (startsWith "http://" s) s (if (startsWith "https://" s) s ("http://" + s))
# data = "type=regular&url="+urlEncode s+"&token=a"
# (mer, w) = doHTTPRequest
{ newHTTPRequest
& req_method = HTTP_POST
, req_path = "/"
, server_name = "cloo.gl"
, server_port = 80
, req_headers = 'DM'.fromList
[("Content-Type", "application/x-www-form-urlencoded")
,("Content-Length", toString $ size data)
,("Accept", "*/*")]
, req_data = data} 10000 w
| isError mer = ("request failed: " + fromError mer, w)
# resp = fromOk mer
= (resp.rsp_data, w)
cloogle :: String *World -> (String, *World)
cloogle data w
# (mer, w) = doHTTPRequestFollowRedirects
{ newHTTPRequest
& req_path = "/api.php"
, req_query = "?str=" + urlEncode data
, req_headers = 'DM'.fromList [("User-Agent", "cloogle-irc")]
, server_name = "cloogle.org"
, server_port = 80} 10000 10 w
| isError mer = ("request failed: " + fromError mer, w)
# resp = fromOk mer
= case fromJSON $ fromString resp.HTTPResponse.rsp_data of
Nothing = ("couldn't parse json", w)
Just {return=127} = ("No results for " + data, w)
Just clr = ("Results for " + data + " -- https://cloogle.org/#" +
replaceSubString "+" "%20" (urlEncode data) + "\n" +
processResults clr, w)
where
processResults :: Response -> String
processResults resp
| resp.return > 127 = "Failed: return code: " + toString resp.return + ", " + resp.msg
= join "\n" $ map processResult $ take 3 resp.data
processResult :: Result -> String
processResult (FunctionResult (br, {func}))
= "Function in " +++ br.library +++ ": " +++ br.modul +++ "\n" +++ func
processResult (TypeResult (br, {type}))
= "Type in " +++ br.library +++ ": " +++ br.modul +++ "\n" +++ limitResults type
processResult (ClassResult (br, {class_name,class_funs}))
= "Class in " +++ br.library +++ ": " +++ br.modul +++ "\n" +++ class_name +++ " with "
+++ toString (length class_funs) +++ " class functions"
processResult (ModuleResult (br, _))
= "Module in " +++ br.library +++ ": " +++ br.modul
processResult (SyntaxResult (br, re))
= "Clean syntax: " +++ re.syntax_title +++ "\n"
+++ concat (intersperse "; " re.syntax_code)
processResult (ABCInstructionResult (br, re))
= "ABC instruction: " +++ re.abc_instruction
processResult (ProblemResult pr)
= "Common problem: " +++ pr.problem_title
+++ "; see https://gitlab.science.ru.nl/cloogle/common-problems/blob/master/" +++ pr.problem_key +++ ".md"
limitResults :: String -> String
limitResults s
# lines = split "\n" s
| length lines > 4 = limitResults (join "\n" (take 3 lines) + "\n...")
= join "\n" (map maxWidth lines)
maxWidth :: String -> String
maxWidth s = if (size s > 80) (subString 0 77 s + "...") s
:: BotSettings =
{ bs_nick :: String
, bs_nickserv :: Maybe String
, bs_autojoin :: [String]
, bs_port :: Int
, bs_server :: String
, bs_strftime :: String
}
Start :: *World -> (Maybe String, *World)
Start w
# ([cmd:args], w) = getCommandLine w
# (io, w) = stdio w
# bs = parseCLI cmd args
| isError bs
# io = io <<< fromError bs <<< "\n"
= (Nothing, snd $ fclose io w)
# (Ok bs) = bs
# (_, w) = fclose io w
# (merr, _, w) = bot (bs.bs_server, bs.bs_port) (startup bs) shutdown () (process bs.bs_strftime) w
= (merr, w)
where
parseCLI :: String [String] -> MaybeErrorString BotSettings
parseCLI _ [] = Ok
{ bs_nick = "clooglebot"
, bs_nickserv = Nothing
, bs_autojoin = []
, bs_port = 6667
, bs_server = "irc.freenode.net"
, bs_strftime = "%s"
}
parseCLI cmd [a:as]
| a == "-f" || a == "--strftime"
= arg1 "--strftime" as \a c->{c & bs_strftime=a}
| a == "-n" || a == "--nick"
= arg1 "--nick" as \a c->{c & bs_nick=a}
| a == "-ns" || a == "--nickserv"
= arg1 "--nickserv" as \a c->{c & bs_nickserv=Just a}
| a == "-a" || a == "--autojoin"
= arg1 "--autojoin" as \a c->{c & bs_autojoin=c.bs_autojoin ++ [a]}
| a == "-p" || a == "--port"
= arg1 "--port" as \a c->{c & bs_port=toInt a}
| a == "-s" || a == "--server"
= arg1 "--server" as \a c->{c & bs_server=a}
| a == "-h" || a == "--help" = Error $ join "\n" $
[ "Usage: " + cmd + " [OPTS]"
, "Options:"
, "\t--strftime/-f FORMAT strftime format used in the output. default: %s\n"
, "\t--nick/-n NICKNAME Use the given nickname instead of clooglebot"
, "\t--nickserv/-ns PW Identify via the given password with NickServ"
, "\t--port/-p PORT Use the given port instead of port 6667"
, "\t--server/-s SERVER Use the given server instead of irc.freenode.net"
, "\t--autojoin/-a CHANNEL Add CHANNEL to the autojoin list. This command "
, "\t can be called multiple times. Beware that #"
, "\t has to be escaped in most shells"
]
= Error $ "Unknown option: " +++ a
where
arg1 name [] _ = Error $ name +++ " requires an argument"
arg1 name [a:as] f = parseCLI cmd as >>= Ok o f a
nickserv pw = PRIVMSG (CSepList ["NickServ"]) $ "IDENTIFY " +++ pw
toPrefix c = {irc_prefix=Nothing,irc_command=Right c}
startup bs = map toPrefix $
[ NICK bs.bs_nick Nothing
, USER "cloogle" "cloogle" "cloogle" "Cloogle bot"
]++ maybe [] (pure o nickserv) bs.bs_nickserv
++ if (isEmpty bs.bs_autojoin) []
[JOIN (CSepList bs.bs_autojoin) Nothing]
shutdown = map toPrefix [QUIT $ Just "Bye"]
process :: String !IRCMessage () !*World -> (Maybe [IRCMessage], (), !*World)
process strf im _ w
# (io ,w) = stdio w
# (io, w) = log strf " (r): " im (io, w)
# (_, w) = fclose io w
= case im.irc_command of
Left numr = (Just [], (), w)
Right cmd = case process` im.irc_prefix cmd w of
(Nothing, w) = (Nothing, (), w)
(Just cs, w)
# msgs = map toPrefix cs
= (Just msgs, (), w)
log :: String String IRCMessage (!*File, !*World) -> (!*File, !*World)
log strf pref m (io, w)
#! (t, w) = localTime w
= (io <<< strfTime strf t <<< pref <<< toString m <<< "\n", w)
process` :: (Maybe (Either IRCUser String)) IRCCommand *World -> (Maybe [IRCCommand], *World)
process` (Just (Left user)) (PRIVMSG t m) w
| m == "!restart" = (Nothing, w)
| m.[0] == '!'
# (msgs, w) = realProcess (split " " $ m % (1, size m)) w
= (Just $ map reply msgs, w)
| m % (0,4) == "\001PING" = (Just [reply m], w)
= (Just [], w)
where
reply = case (\(CSepList [t:_]) -> t.[0]) t of
'#' -> PRIVMSG t
_ -> NOTICE user.irc_nick
process` _ (PING t mt) w = (Just [PONG t mt], w)
process` _ _ w = (Just [], w)
realProcess :: [String] *World -> ([String], *World)
realProcess ["help",x:xs] w = ((case x of
"help" =
[ "Usage: !help [ARG]"
, "Show this help, or the specific help of the argument"]
"ping" =
[ "Usage: !ping [ARG [ARG ...]]"
, "Ping the bot, it will pong the arguments back"]
"shorten" =
[ "Usage: !shorten URL [URL [URL ...]]"
, "Shorten the given urls with the cloo.gl url shortener"]
"query" =
[ "Usage: !query QUERY"
, "Query QUERY in cloogle and return the results"]
"restart" =
[ "Usage: !restart"
, "Restart the bot"]
x = ["Unknown command: " +++ x]
), w)
realProcess ["help"] w = (
["Type !help cmd for command specific help"
,"available commands: help, ping, shorten, query, restart"], w)
realProcess ["ping":xs] w = (["pong " +++ join " " xs], w)
realProcess ["shorten":xs] w = case xs of
[] = (["shorten requires at least one argument"], w)
xs = mapSt shorten xs w
realProcess ["query":xs] w = case xs of
[] = (["query requires one or more arguments"], w)
xs = appFst (split "\n") $ cloogle (join " " xs) w
realProcess ["restart":_] w = (["restart takes no arguments"], w)
realProcess [c:_] w = ([join " " [
"Unknown cmd: ", c, ", type !help to get help"]], w)
#!/bin/sh
set -e
if [ "$#" -eq 0 ];
then
echo "Usage: $0 DIR" >&2
exit 1;
fi
TARGET="$(realpath "$1")"
echo "Removing and repopulating $TARGET" >&2
if [ -d "$TARGET" ]; then
mv "$TARGET" "$TARGET.$(date +%F)" || rm -fr "$TARGET"
fi
rm -fr "$TARGET"
mkdir -p "$TARGET"
echo "Downloading and installing clean nightly" >&2
curl -sSL ftp://ftp.cs.ru.nl/pub/Clean/builds/linux-x64/clean-bundle-complete-linux-x64-latest.tgz \
| tar --gunzip --strip-components=1 --extract --directory="$TARGET"
echo "export CLEAN_HOME=$TARGET; export PATH=$TARGET/bin:\$PATH;"
#!/bin/sh
while true
do
git pull origin master
git submodule init
git submodule update
make -B
./cloogleirc "$@"
sleep 5s
done
definition module GenIRC
import StdGeneric
from Data.Either import :: Either
from Data.Maybe import :: Maybe
from Text.Parsers.Simple.Core import :: Error
from IRC import :: IRCCommand, :: CSepList
generic gIRCParse a :: [String] -> (Either Error a, [String])
generic gIRCPrint a :: a -> [String]
derive gIRCParse IRCCommand, String, Int, Maybe, (,), [], CSepList
derive gIRCPrint IRCCommand, String, Int, Maybe, (,), [], CSepList
implementation module GenIRC
import StdEnv
import StdGeneric
import Data.Either
import Data.Func
import Data.Functor
import Data.Maybe
import Data.Tuple
import Text
import IRC
pOne [] = (Left "Expected an argument", [])
pOne [a:as] = (Right a, as)
generic gIRCParse a :: [String] -> (Either Error a, [String])
gIRCParse{|UNIT|} a = (Right UNIT, a)
gIRCParse{|String|} as = pOne as
gIRCParse{|Int|} as = appFst (fmap toInt) $ pOne as
gIRCParse{|EITHER|} lp rp as = case lp as of
(Right a, rest) = (Right $ LEFT a, rest)
(Left e1, _) = case rp as of
(Right a, rest) = (Right $ RIGHT a, rest)
(Left e2, _) = (Left $ e2, [])
gIRCParse{|OBJECT|} p as = appFst (fmap OBJECT) $ p as
gIRCParse{|CONS of d|} p []
= (Left $ concat ["Expected a cmd constructor: ", d.gcd_name], [])
gIRCParse{|CONS of d|} p [a:as]
| a <> d.gcd_name = (Left $ concat [
"Wrong constructor. expected: ", d.gcd_name, ", got: ", a], [])
= case p as of
(Right a, rest) = (Right $ CONS a, rest)
(Left e, _) = (Left e, [])
gIRCParse{|PAIR|} pl pr as = case pl as of
(Right a1, rest) = case pr rest of
(Right a2, rest) = (Right $ PAIR a1 a2, rest)
(Left e, _) = (Left e, [])
(Left e, _) = (Left e, [])
gIRCParse{|[]|} pl as = case pl as of
(Right e, rest) = case gIRCParse{|*->*|} pl rest of
(Right es, rest) = (Right [e:es], rest)
(Left e, _) = (Left e, [])
(Left e, _) = (Right [], as)
gIRCParse{|Maybe|} pm as
= appFst (either (const $ Right Nothing) $ Right o Just) $ pm as
gIRCParse{|CSepList|} as = appFst (fmap $ CSepList o split ",") $ pOne as
derive gIRCParse (,), IRCCommand
derive gIRCPrint (,), IRCCommand
generic gIRCPrint a :: a -> [String]
gIRCPrint{|UNIT|} _ = []
gIRCPrint{|String|} s = if (indexOf " " s == -1) [s] [":"+++s]
gIRCPrint{|Int|} i = [toString i]
gIRCPrint{|EITHER|} lp rp (LEFT i) = lp i
gIRCPrint{|EITHER|} lp rp (RIGHT i) = rp i
gIRCPrint{|OBJECT|} lp (OBJECT p) = lp p
gIRCPrint{|PAIR|} lp rp (PAIR l r) = lp l ++ rp r
gIRCPrint{|CONS of d|} pc (CONS c) = [d.gcd_name:pc c]
gIRCPrint{|[]|} pl x = flatten $ map pl x
gIRCPrint{|Maybe|} pl m = gIRCPrint{|*->*|} pl $ maybeToList m
gIRCPrint{|CSepList|} (CSepList as) = [join "," as]
definition module IRC
from StdOverloaded import class fromInt, class toInt, class toString, class fromString
from Data.Either import :: Either
from Data.Maybe import :: Maybe
from Text.Parsers.Simple.Core import :: Error
:: IRCMessage =
{ irc_prefix :: Maybe (Either IRCUser String)
, irc_command :: Either IRCNumReply IRCCommand}
:: IRCNumReply =
{ irc_reply :: IRCReplies
, irc_recipient :: String
, irc_message :: String
}
:: IRCUser =
{ irc_nick :: String
, irc_user :: Maybe String
, irc_host :: Maybe String
}
parseIRCMessage :: String -> Either [Error] IRCMessage
instance toString IRCCommand, IRCReplies, IRCErrors, IRCMessage, IRCUser, IRCNumReply
instance fromInt IRCReplies, IRCErrors
instance toInt IRCReplies, IRCErrors
:: CSepList = CSepList [String]
:: IRCCommand
= ADMIN (Maybe String)
| AWAY String
| CONNECT String (Maybe (Int, Maybe String))
| DIE
| ERROR String
| INFO (Maybe String)
| INVITE String String
| ISON [String]
| JOIN CSepList (Maybe String)
| KICK String String (Maybe String)
| KILL String String
| LINKS (Maybe (Maybe String, String))
| LIST (Maybe (CSepList, Maybe String))
| LUSERS (Maybe (String, Maybe String))
| MODE String String (Maybe String) (Maybe String) (Maybe String)
| MOTD (Maybe String)
| NAMES CSepList
| NICK String (Maybe String)
| NJOIN
| NOTICE String String
| OPER String String
| PART CSepList
| PASS String
| PING String (Maybe String)
| PONG String (Maybe String)
| PRIVMSG CSepList String
| QUIT (Maybe String)
| REHASH
| RESTART
| SERVER
| SERVICE String String String String
| SERVLIST (Maybe (String, Maybe String))
| SQUERY String String
| SQUIRT
| SQUIT String String
| STATS (Maybe (String, Maybe String))
| SUMMON String (Maybe (String, Maybe String))
| TIME (Maybe String)
| TOPIC String (Maybe String)
| TRACE (Maybe String)
| USER String String String String
| USERHOST CSepList
| USERS (Maybe String)
| VERSION (Maybe String)
| WALLOPS String
| WHO (Maybe String)
| WHOIS (Maybe String) String
| WHOWAS String (Maybe (String, Maybe String))
:: IRCReplies = RPL_WELCOME | RPL_YOURHOST | RPL_CREATED | RPL_MYINFO |
RPL_BOUNCE | RPL_TRACELINK | RPL_TRACECONNECTING | RPL_TRACEHANDSHAKE |
RPL_TRACEUNKNOWN | RPL_TRACEOPERATOR | RPL_TRACEUSER | RPL_TRACESERVER |
RPL_TRACESERVICE | RPL_TRACENEWTYPE | RPL_TRACECLASS | RPL_TRACERECONNECT |
RPL_STATSLINKINFO | RPL_STATSCOMMANDS | RPL_ENDOFSTATS | RPL_UMODEIS |
RPL_SERVLIST | RPL_SERVLISTEND | RPL_STATSUPTIME | RPL_STATSOLINE |
RPL_LUSERCLIENT | RPL_LUSEROP | RPL_LUSERUNKNOWN | RPL_LUSERCHANNELS |
RPL_LUSERME | RPL_ADMINME | RPL_ADMINLOC1 | RPL_ADMINLOC2 | RPL_ADMINEMAIL |
RPL_TRACELOG | RPL_TRACEEND | RPL_TRYAGAIN | RPL_AWAY | RPL_USERHOST |
RPL_ISON | RPL_UNAWAY | RPL_NOWAWAY | RPL_WHOISUSER | RPL_WHOISSERVER |
RPL_WHOISOPERATOR | RPL_WHOWASUSER | RPL_ENDOFWHO | RPL_WHOISIDLE |
RPL_ENDOFWHOIS | RPL_WHOISCHANNELS | RPL_LISTSTART | RPL_LIST |
RPL_LISTEND | RPL_CHANNELMODEIS | RPL_UNIQOPIS | RPL_NOTOPIC | RPL_TOPIC |
RPL_INVITING | RPL_SUMMONING | RPL_INVITELIST | RPL_ENDOFINVITELIST |
RPL_EXCEPTLIST | RPL_ENDOFEXCEPTLIST | RPL_VERSION | RPL_WHOREPLY |
RPL_NAMREPLY | RPL_LINKS | RPL_ENDOFLINKS | RPL_ENDOFNAMES | RPL_BANLIST |
RPL_ENDOFBANLIST | RPL_ENDOFWHOWAS | RPL_INFO | RPL_MOTD | RPL_ENDOFINFO |
RPL_MOTDSTART | RPL_ENDOFMOTD | RPL_YOUREOPER | RPL_REHASHING |
RPL_YOURESERVICE | RPL_TIME | RPL_USERSSTART | RPL_USERS | RPL_ENDOFUSERS |
RPL_NOUSERS | RPL_UNKNOWN
:: IRCErrors = ERR_NOSUCHNICK | ERR_NOSUCHSERVER | ERR_NOSUCHCHANNEL |
ERR_CANNOTSENDTOCHAN | ERR_TOOMANYCHANNELS | ERR_WASNOSUCHNICK |
ERR_TOOMANYTARGETS | ERR_NOSUCHSERVICE | ERR_NOORIGIN | ERR_NORECIPIENT |
ERR_NOTEXTTOSEND | ERR_NOTOPLEVEL | ERR_WILDTOPLEVEL | ERR_BADMASK |
ERR_UNKNOWNCOMMAND | ERR_NOMOTD | ERR_NOADMININFO | ERR_FILEERROR |
ERR_NONICKNAMEGIVEN | ERR_ERRONEUSNICKNAME | ERR_NICKNAMEINUSE |
ERR_NICKCOLLISION | ERR_UNAVAILRESOURCE | ERR_USERNOTINCHANNEL |
ERR_NOTONCHANNEL | ERR_USERONCHANNEL | ERR_NOLOGIN | ERR_SUMMONDISABLED |
ERR_USERSDISABLED | ERR_NOTREGISTERED | ERR_NEEDMOREPARAMS |
ERR_ALREADYREGISTRED | ERR_NOPERMFORHOST | ERR_PASSWDMISMATCH |
ERR_YOUREBANNEDCREEP | ERR_YOUWILLBEBANNED | ERR_KEYSET |
ERR_CHANNELISFULL | ERR_UNKNOWNMODE | ERR_INVITEONLYCHAN |
ERR_BANNEDFROMCHAN | ERR_BADCHANNELKEY | ERR_BADCHANMASK |
ERR_NOCHANMODES | ERR_BANLISTFULL | ERR_NOPRIVILEGES |
ERR_CHANOPRIVSNEEDED | ERR_CANTKILLSERVER | ERR_RESTRICTED |