manderlbot-0.9.2/0040755000175000000620000000000010000342175012425 5ustar acidstaffmanderlbot-0.9.2/src/0040755000175000000620000000000010000342175013214 5ustar acidstaffmanderlbot-0.9.2/src/config.erl0100644000175000000620000002011207764117701015200 0ustar acidstaff%%%---------------------------------------------------------------------- %%% File : config.erl %%% Author : Dimitri Fontaine %%% Purpose : Read the manderlbot XML xonfig file %%% Created : 19 Feb 2002 by Dimitri Fontaine %%%---------------------------------------------------------------------- %%% %%% This file is part of Manderlbot. %%% %%% Manderlbot is free software; you can redistribute it and/or modify %%% it under the terms of the GNU General Public License as published by %%% the Free Software Foundation; either version 2 of the License, or %%% (at your option) any later version. %%% %%% Manderlbot is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %%% GNU General Public License for more details. %%% %%% See LICENSE for detailled license %%% %%% In addition, as a special exception, you have the permission to %%% link the code of this program with any library released under %%% the EPL license and distribute linked combinations including %%% the two. If you modify this file, you may extend this exception %%% to your version of the file, but you are not obligated to do %%% so. If you do not wish to do so, delete this exception %%% statement from your version. %%% %%%---------------------------------------------------------------------- -module(config). -author('dim@tuxfamily.org'). -include("config.hrl"). -include("xmerl.hrl"). -include("log.hrl"). -define(default_passwd, "h4ckd4w0rld"). -export([read/1]). -export([read_fortunes/1]). %%%---------------------------------------------------------------------- %%% Function: read/1 %%% Purpose: read the xml config file %%%---------------------------------------------------------------------- read(Filename) -> case xmerl_scan:file(Filename) of {ok, Root = #xmlElement{}} -> mdb_logger:debug("root: ~p~n~p~n", [Root#xmlElement.name, Root#xmlElement.content]), {ok, parse(Root, #config{})}; Error -> {error, Error} end. %%%---------------------------------------------------------------------- %%% Function: parse/2 %%% Purpose: parse the xmerl structure %%%---------------------------------------------------------------------- parse(Element = #xmlElement{parents = []}, #config{}) -> Name = getAttr(Element#xmlElement.attributes, name), Controler = build_list( getAttr(Element#xmlElement.attributes, controler), ", "), lists:foldl(fun parse/2, #config{name = Name, controler = Controler}, Element#xmlElement.content); %% parsing the Dict elements parse(Element = #xmlElement{name=dict}, Conf = #config{dict=Dict}) -> Server = getAttr(Element#xmlElement.attributes, host), Port = getAttr(Element#xmlElement.attributes, port), Default = getAttr(Element#xmlElement.attributes, default), {ok, [{integer,1,IPort}],1} = erl_scan:string(Port), lists:foldl(fun parse/2, Conf#config{dict = {Server, IPort, Default}}, Element#xmlElement.content); %% parsing the Server elements parse(Element = #xmlElement{name=server}, Conf = #config{servers=SList}) -> Server = getAttr(Element#xmlElement.attributes, host), Port = getAttr(Element#xmlElement.attributes, port), Passwd = getAttr(Element#xmlElement.attributes, password, ?default_passwd), {ok, [{integer,1,IPort}],1} = erl_scan:string(Port), lists:foldl(fun parse/2, Conf#config{servers = [#server{host=Server, port=IPort, passwd=Passwd }|SList]}, Element#xmlElement.content); %% Parsing the Channel element parse(Element = #xmlElement{name=channel}, Conf = #config{servers=[CurServ|SList]}) -> ChanList = CurServ#server.channels, Chan = getAttr(Element#xmlElement.attributes, name), Bot = getAttr(Element#xmlElement.attributes, botname), {ok, List, _Count} = regexp:gsub(getAttr(Element#xmlElement.attributes, behaviours), "\s+|\t+|\n+", ","), B = string:tokens(List, ","), lists:foldl(fun parse/2, Conf#config{servers = [CurServ#server{channels = [#channel{name=Chan, botname=Bot, behaviours=B} |ChanList]} |SList]}, Element#xmlElement.content); %% Parsing the behaviour element parse(Element = #xmlElement{name=behaviour}, Conf = #config{servers=[CurServ|SList], behaviours=BList}) -> [CurChan|ChanList] = CurServ#server.channels, Name = getAttr(Element#xmlElement.attributes, name), Action = getAttr(Element#xmlElement.attributes, action), From = getAttr(Element#xmlElement.attributes, from, '_'), To = getAttr(Element#xmlElement.attributes, to, '_'), Op = getAttr(Element#xmlElement.attributes, op, '_'), Option = getAttr(Element#xmlElement.attributes, option, '_'), Pattern = getAttr(Element#xmlElement.attributes, pattern, '_'), EFrom = getAttr(Element#xmlElement.attributes, exl_from, '_'), ETo = getAttr(Element#xmlElement.attributes, exl_to, '_'), EOp = getAttr(Element#xmlElement.attributes, exl_op, '_'), EOption = getAttr(Element#xmlElement.attributes, exl_option, '_'), EPattern = getAttr(Element#xmlElement.attributes, exl_pattern, '_'), Data = getText(Action, Element#xmlElement.content), lists:foldl(fun parse/2, Conf#config{behaviours = [#cfg_behaviour{name=Name, action=Action, from=From, to=To, op=Op, option=Option, pattern = Pattern, exl_from=EFrom, exl_to=ETo, exl_op=EOp, exl_option=EOption, exl_pattern = EPattern, data=Data} | BList]}, Element#xmlElement.content); %% Parsing other elements parse(Element = #xmlElement{}, Conf = #config{}) -> lists:foldl(fun parse/2, Conf, Element#xmlElement.content); %% Parsing non #xmlElement elements parse(Element, Conf = #config{}) -> Conf. %%%---------------------------------------------------------------------- %%% Function: getAttr/2 %%% Purpose: search the attibute list for the given one %%%---------------------------------------------------------------------- getAttr(Attr, Name) -> getAttr(Attr, Name, ""). getAttr([Attr = #xmlAttribute{name=Name}|Tail], Name, Default) -> case Attr#xmlAttribute.value of [] -> Default; A -> A end; getAttr([H|T], Name, Default) -> getAttr(T, Name, Default); getAttr([], Name, Default) -> Default. %%%---------------------------------------------------------------------- %%% Function: getText/2 %%% Purpose: get the text of the XML node %%%---------------------------------------------------------------------- getText("fortune", [Text = #xmlText{value=Value}|Tail]) -> %% The value is a file name, we have to read it as a fortune file read_fortunes(string:strip(Value, both)); getText(Action, [Text = #xmlText{value=Value}|Tail]) -> build_list(string:strip(Value, both)); getText(Action, _Other) -> "". %%%---------------------------------------------------------------------- %%% Function: build_list/1, build_list/2 %%% Purpose: Build a list from a string, using given separator. %%% Default separator is '%' %%%---------------------------------------------------------------------- build_list(String) -> build_list(String, "%"). build_list(String, Sep) -> string:tokens(String, Sep). %%%---------------------------------------------------------------------- %%% Function: getText/2 %%% Purpose: get the text of the XML node %%%---------------------------------------------------------------------- read_fortunes(Filename) -> case file:read_file(Filename) of {ok, Content} -> case lists:foldl( fun([$%|Tail], Acc) -> [[] | Acc]; (List, [[]|Acc]) -> [[List] | Acc]; (List, [Buffer | Acc]) -> [Buffer++[List] | Acc] end, [], string:tokens(binary_to_list(Content), "\r\n")) of [[]| Fortunes] -> %% This happens when first line of file is '%' Fortunes; Fortunes -> Fortunes end; {error, Reason} -> mdb_logger:error("Config: could not read fortune file ~s: ~s", [Filename, Reason]), [] end. manderlbot-0.9.2/src/config_srv.erl0100644000175000000620000002421007766050631016075 0ustar acidstaff%%%---------------------------------------------------------------------- %%% File : mdb_behaviours_srv.erl %%% Author : Dimitri Fontaine %%% Purpose : Manage the manderlbot config %%% Created : 2 Mar 2002 by Dimitri Fontaine %%%---------------------------------------------------------------------- %%% %%% This file is part of Manderlbot. %%% %%% Manderlbot is free software; you can redistribute it and/or modify %%% it under the terms of the GNU General Public License as published by %%% the Free Software Foundation; either version 2 of the License, or %%% (at your option) any later version. %%% %%% Manderlbot is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %%% GNU General Public License for more details. %%% %%% See LICENSE for detailled license %%% %%% In addition, as a special exception, you have the permission to %%% link the code of this program with any library released under %%% the EPL license and distribute linked combinations including %%% the two. If you modify this file, you may extend this exception %%% to your version of the file, but you are not obligated to do %%% so. If you do not wish to do so, delete this exception %%% statement from your version. %%% %%%---------------------------------------------------------------------- -module(config_srv). -author('fontaine@whitestar'). %%-compile(export_all). %%-export([Function/Arity, ...]). -behaviour(gen_server). %% External exports -export([start_link/1, getConf/0, getDictConf/0, readConf/0, reconf/3, getBList/2, getBehaviours/1]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -include("config.hrl"). -include("mdb.hrl"). -include("log.hrl"). %% We need a big timeout in order to be able to launch new bots. -define(timeout, 10000). %%%---------------------------------------------------------------------- %%% API %%%---------------------------------------------------------------------- start_link(ConfigFile) -> gen_server:start_link({local, ?MODULE}, ?MODULE, [ConfigFile], []). getConf() -> gen_server:call(?MODULE, {getConf}, ?timeout). getDictConf() -> gen_server:call(?MODULE, {getDictConf}, ?timeout). readConf() -> gen_server:call(?MODULE, {readConf}, ?timeout). reconf(Chan, BotName, ConfigFile) -> gen_server:call(?MODULE, {reconf, Chan, BotName, ConfigFile}, ?timeout). getBList(Channel, BotName) -> gen_server:call(?MODULE, {getlist, Channel, BotName}, ?timeout). getBehaviours(BNames) -> gen_server:call(?MODULE, {getBehaviours, BNames}, ?timeout). %%%---------------------------------------------------------------------- %%% Callback functions from gen_server %%%---------------------------------------------------------------------- %%---------------------------------------------------------------------- %% Func: init/1 %% Returns: {ok, State} | %% {ok, State, Timeout} | %% ignore | %% {stop, Reason} %%---------------------------------------------------------------------- init([ConfigFile]) -> {ok, {filename, ConfigFile}}. %%---------------------------------------------------------------------- %% Func: handle_call/3 %% Returns: {reply, Reply, State} | %% {reply, Reply, State, Timeout} | %% {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, Reply, State} | (terminate/2 is called) %% {stop, Reason, State} (terminate/2 is called) %%---------------------------------------------------------------------- handle_call({getConf}, From, Config) -> {reply, {ok, Config}, Config}; handle_call({getDictConf}, From, Config = #config{dict=Dict}) -> {reply, {ok, Dict}, Config}; handle_call({readConf}, From, {filename, ConfigFile}) -> case config:read(ConfigFile) of {ok, Config} -> %% Here we launch all the configured bots checkForNewChans(Config), {reply, ok, Config}; {error, Reason} -> {reply, {error, Reason}, ConfigFile} end; handle_call({reconf, Channel, BotName, ConfigFile}, From, Config) -> case config:read(ConfigFile) of {ok, NewConfig = #config{}} -> %% Don't forget to check for new chans to join checkForNewChans(NewConfig), {reply, {ok, getBehaviours(NewConfig, Channel, BotName)}, NewConfig}; Error -> {reply, Error, Config} end; handle_call({getlist, Channel, BotName}, From, Conf) -> {reply, {ok, getBehaviours(Conf, Channel, BotName)}, Conf}; handle_call({getBehaviours, notfound}, From, Conf) -> {reply, {ok, []}, Conf}; handle_call({getBehaviours, BNames}, From, Conf=#config{behaviours=BList}) -> mdb_logger:debug("BNames: ~p~n", [BNames]), {reply, {ok, lists:filter(fun(Behaviour=#behaviour{id=Id}) -> lists:member(Id, BNames) end, build_behaviours_list(BList, []))}, Conf}; handle_call(Request, From, State) -> Reply = ok, {reply, Reply, State}. %%---------------------------------------------------------------------- %% Func: handle_cast/2 %% Returns: {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, State} (terminate/2 is called) %%---------------------------------------------------------------------- handle_cast(Msg, State) -> {noreply, State}. %%---------------------------------------------------------------------- %% Func: handle_info/2 %% Returns: {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, State} (terminate/2 is called) %%---------------------------------------------------------------------- handle_info(Info, State) -> {noreply, State}. %%---------------------------------------------------------------------- %% Func: terminate/2 %% Purpose: Shutdown the server %% Returns: any (ignored by gen_server) %%---------------------------------------------------------------------- terminate(Reason, State) -> ok. %%---------------------------------------------------------------------- %% Func: code_change/3 %%---------------------------------------------------------------------- code_change(OldVsn, State, Extra) -> {ok, State}. %%%---------------------------------------------------------------------- %%% Internal functions %%%---------------------------------------------------------------------- %%---------------------------------------------------------------------- %% build_behaviours_list/2 %% Build a behaviour list from the behaviours found in the %% config file %%---------------------------------------------------------------------- build_behaviours_list([], Acc) -> Acc; build_behaviours_list([BC=#cfg_behaviour{action=Action}|BClist], Acc) -> %% Here we map the actions defined in the config file %% With the code to use in order to make the action Behaviour = #behaviour{id = BC#cfg_behaviour.name, pattern = #data{ header_from = {regexp, BC#cfg_behaviour.from}, header_to = {regexp, BC#cfg_behaviour.to}, header_op = {regexp, BC#cfg_behaviour.op}, header_options = {regexp, BC#cfg_behaviour.option}, body = {regexp, BC#cfg_behaviour.pattern}}, exclude_pattern = #data{ header_from = {regexp, BC#cfg_behaviour.exl_from}, header_to = {regexp, BC#cfg_behaviour.exl_to}, header_op = {regexp, BC#cfg_behaviour.exl_op}, header_options = {regexp, BC#cfg_behaviour.exl_option}, body = {regexp, BC#cfg_behaviour.exl_pattern}}, function = getFun(Action), data = BC#cfg_behaviour.data}, build_behaviours_list(BClist, [Behaviour|Acc]). %%%---------------------------------------------------------------------- %%% Function: getFun/1 %%% Purpose: Given the action name, returns the Module and Function to %%% call. %%%---------------------------------------------------------------------- getFun(Action) -> %% calling list_to_atom, this may be dangerous (DoS) {list_to_atom("mdb_bhv_" ++ Action), behaviour}. %%---------------------------------------------------------------------- %% getBehaviours/3 %% Read the config and find on it our behaviours %%---------------------------------------------------------------------- getBehaviours(#config{servers=SList}, Chan, BotName) -> getBehaviours(SList, Chan, BotName); getBehaviours([#server{channels=CList}|STail], Chan, BotName) -> case getBehaviours(CList, Chan, BotName) of notfound -> getBehaviours(STail, Chan, BotName); BList -> BList end; getBehaviours([#channel{name=Chan, botname=BotName, behaviours=BList}|CTail], Chan, BotName) -> BList; getBehaviours([#channel{}|CTail], Chan, BotName) -> getBehaviours(CTail, Chan, BotName); getBehaviours([], Chan, BotName) -> notfound. %%---------------------------------------------------------------------- %% checkForNewChans/3 %% For each server/chan in the config, add it to the mdb_botlist. %% If no bot instance is connected, a new one will be started, %% calling mdb_botlist:add(Name, Controler, Host, Port, Chan) %%---------------------------------------------------------------------- checkForNewChans(Config) -> checkForNewChans(Config, [], Config). checkForNewChans(#config{name=Name, controler=Ctlr, servers=SList}, Params, Config ) -> checkForNewChans(SList, [Name, Ctlr], Config); checkForNewChans([#server{host=Host, port=Port, passwd=Pass, channels=CList}|Stail], [Name, Ctlr], Config ) -> checkForNewChans(CList, [Name, Ctlr, Host, Port, Pass], Config), checkForNewChans(Stail, [Name, Ctlr], Config); checkForNewChans([Channel=#channel{name=Chan, botname=BotName}|CTail], [Name, Ctlr, Host, Port, Pass], Config) -> %% In order to avoid a re-entrance which faults in timeout, %% we pass directly from here the new bot BList ! mdb_botlist:add(Name, Ctlr, Host, Port, Pass, Channel, getBehaviours(Config, Chan, BotName)), checkForNewChans(CTail, [Name, Ctlr, Host, Port, Pass], Config); checkForNewChans([], Params, Config) -> done. manderlbot-0.9.2/src/debian.erl0100644000175000000620000001102707720720463015157 0ustar acidstaff%%% File : debian.erl %%% Author : Nicolas Niclausse %%% Purpose : search for debian package names or files in debian package %%% Created : 23 Jul 2002 by Nicolas Niclausse %%%---------------------------------------------------------------------- %%% %%% This file is part of Manderlbot. %%% %%% Manderlbot is free software; you can redistribute it and/or modify %%% it under the terms of the GNU General Public License as published by %%% the Free Software Foundation; either version 2 of the License, or %%% (at your option) any later version. %%% %%% Manderlbot is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %%% GNU General Public License for more details. %%% %%% See LICENSE for detailled license %%% %%% In addition, as a special exception, you have the permission to %%% link the code of this program with any library released under %%% the EPL license and distribute linked combinations including %%% the two. If you modify this file, you may extend this exception %%% to your version of the file, but you are not obligated to do %%% so. If you do not wish to do so, delete this exception %%% statement from your version. %%% %%%---------------------------------------------------------------------- -module(debian). -author('nico@niclux.org'). -revision(' $Id: debian.erl,v 1.5 2003/08/20 16:26:27 nico Exp $ '). -vsn(' $Revision: 1.5 $ '). -export([search/5, parse/1, set_request/1]). -include("mdb.hrl"). -define(debian_name, "packages.debian.org"). -define(debian_port, 80). -define(margin, 50). % left margin for file results search(Keywords, Input, BotPid, BotName, Channel) -> mdb_search:search({Keywords, Input, BotPid, BotName, Channel, #search_param{type = ?MODULE, server = ?debian_name, port = ?debian_port } }). %%---------------------------------------------------------------------- %% Func: parse/1 %% Purpose: Parse data %% Returns: {stop, Result} | {stop} | {continue} | {continue, Result} %% continue -> continue parsing of incoming data %% stop -> stop parsing of incoming data %% Result -> String to be printed by mdb %%---------------------------------------------------------------------- parse("HTTP/1.1 404" ++ Data) -> {stop, "HTTP not found"}; parse("HTTP/1.0 404" ++ Data) -> {stop, "HTTP not found"}; parse("" ++ Data) -> {stop}; parse("No responses" ++ Data) -> {stop,"not found"}; %% Package short description parse("  " ++ Data) -> [Description | _Other ] = string:tokens(Data,"<"), {continue, " " ++ Description}; %% URL of package parse("\t [URL | _Other ] = string:tokens(Data,"\""), {continue, URL }; %% Package short description, caps parse("  " ++ Data) -> [Description | _Other ] = string:tokens(Data,"<"), {continue, " " ++ Description}; %% URL of package, caps parse("\t [URL | _Other ] = string:tokens(Data,"\""), {continue, URL }; parse(Data) -> %% search for files case regexp:first_match(Data, "[^\t]+\t+\s+ % ok, found case httpd_util:split(Data, "(<|>)", 10) of {ok, [File, URL, Package | _Other ]} -> {continue, string:left(File, ?margin, $ )++ Package }; _ -> {continue} end; _B -> {continue} end. %%---------------------------------------------------------------------- %% Func: set_request/1 %% Purpose: Set the request given Keywords %% Returns: String %%---------------------------------------------------------------------- set_request([Type, Keyword]) -> set_request([Type, Keyword, "testing"]); set_request([package, Keyword, Version]) -> "GET /cgi-bin/search_packages.pl?keywords=" ++ Keyword ++ "&searchon=names&subword=1&version=" ++ Version ++"&release=all HTTP/1.1" ++ io_lib:nl() ++ "Host: " ++ ?debian_name ++io_lib:nl() ++io_lib:nl(); set_request([file, Keyword, Version]) -> "GET /cgi-bin/search_contents.pl?word=" ++ Keyword ++ "&searchmode=searchfiles&case=insensitive&version=" ++ Version ++ "&arch=i386&directories=yes HTTP/1.1" ++ io_lib:nl() ++ "Host: " ++ ?debian_name ++ io_lib:nl() ++ io_lib:nl(). manderlbot-0.9.2/src/irc_lib.erl0100644000175000000620000001753107757410001015340 0ustar acidstaff%%%---------------------------------------------------------------------- %%% File : irc_lib.erl %%% Author : Mickaël Rémond %%% Purpose : This library gathers all functions to format and send %%% IRC commands. %%% It manage IRC server connexion, automatically answer to %%% server pings (necessary to stay online) and behaviour %%% management. %%% Created : 11 Sep 2001, Mickaël Rémond %%%---------------------------------------------------------------------- %%% %%% This program is free software; you can redistribute it and/or modify %%% it under the terms of the GNU General Public License as published by %%% the Free Software Foundation; either version 2 of the License, or %%% (at your option) any later version. %%% %%%---------------------------------------------------------------------- %%% %%% See LICENSE for detailled license %%% %%% In addition, as a special exception, you have the permission to %%% link the code of this program with any library released under %%% the EPL license and distribute linked combinations including %%% the two. If you modify this file, you may extend this exception %%% to your version of the file, but you are not obligated to do %%% so. If you do not wish to do so, delete this exception %%% statement from your version. %%%---------------------------------------------------------------------- -module(irc_lib). -author('mickael.remond@erlang-fr.org'). -created('Date: 20010911'). -revision(' $Id: irc_lib.erl,v 1.11 2003/11/21 13:15:45 dim Exp $ '). -vsn(' $Revision: 1.11 $ '). %% IRC operations -export([pong/2, join/2, quit/2, say/3, action/3, login/4, who/4]). %% IRC helper functions -export([is_chanop/1, nickname/1, split_nick_user/1]). -include("irc.hrl"). -include("log.hrl"). %%---------------------------------------------------------------------- %% Function: pong/2 %% Purpose: Send a pong answer with the right id %% Args: Sock = socket %% Id = ping id to send back to the server %% Returns: ok %% or {error, Reason} (if the process is dead) %%---------------------------------------------------------------------- pong(Sock, Id)-> Pong = lists:append("PONG ", Id), command(Sock, Pong). %%---------------------------------------------------------------------- %% join/2 %% Join a discussion channel %%---------------------------------------------------------------------- join(Sock, Channel) -> Command = lists:append("JOIN ", Channel), command(Sock, Command). %%---------------------------------------------------------------------- %% quit/2 %% Inform the server we are quitting %%---------------------------------------------------------------------- quit(Sock, Message) -> Command = lists:append("QUIT :", Message), command(Sock, Command). %%---------------------------------------------------------------------- %% say/3 %% Say something in the given channel %%---------------------------------------------------------------------- say(Sock, Channel, Message) -> Command = lists:concat(["PRIVMSG ", Channel, " :", Message]), %% Command = io_lib:format("PRIVMSG ~s :~s", [Channel, Message]), command(Sock, Command). %%---------------------------------------------------------------------- %% say/3 %% Say something in the given channel %%---------------------------------------------------------------------- action(Sock, Channel, Message) -> Command = "PRIVMSG " ++ Channel ++ " :" ++ [1] ++ "ACTION " ++ Message ++ [1], command(Sock, Command). %%---------------------------------------------------------------------- %% login/4 %% Send the user information to terminate the log in phase %%---------------------------------------------------------------------- login(Sock, Nickname, Passwd, Realname) -> PassCommand = "PASS " ++ Passwd, command(Sock, PassCommand), NickCommand = "NICK " ++ Nickname, command(Sock, NickCommand), %% The username, hostname, servername and realname. Hostname and %% servername are only used in server to server communication UserCommand = lists:concat(["USER ", Nickname, " dummy dummy :", Realname]), command(Sock, UserCommand). %%---------------------------------------------------------------------- %% who/4 %% Get the list of people connected to the given channel. %% %% In order not to break the way manderlbot get the data and parses them, %% I have prefered to open a new connexion here. %%---------------------------------------------------------------------- who(Host, Port, Channel, Botname) -> case mdb_connection:connect(Host, Port) of {ok, Sock} -> login(Sock, "manderlbot", "passwd", Botname), command(Sock, "who " ++ Channel), {ok, String} = getData(Sock, []), gen_tcp:close(Sock), Lines = string:tokens(String, "\r\n"), KeepRE = ":" ++ Host ++ " 352", UserList = lists:filter(fun(Line) -> case regexp:match(Line, KeepRE) of {match, S, L} -> true; NoMatch -> false end end, Lines), lists:map(fun parseUserLine/1, UserList); Whatever -> [] end. %%---------------------------------------------------------------------- %% getData/2 %% internal who/4 function, used to get the data from the server. %%---------------------------------------------------------------------- getData(Sock, Buffer) -> receive {tcp, Sock, Data} -> case regexp:match(binary_to_list(Data), ":End of /WHO list.") of {match, S, L} -> {ok, Buffer ++ binary_to_list(Data)}; NoMatch -> getData(Sock, Buffer ++ binary_to_list(Data)) end; {Error, Sock} -> mdb_logger:error("Error: ~p~n", [Error]), {error, Buffer}; Whatever -> mdb_logger:notice("Whatever: ~p~n", [Whatever]), getData(Sock, Buffer) after 10000 -> {ok, Buffer} end. %%---------------------------------------------------------------------- %% parseUserLine/1 %% internal who/4 function, used to build the records from the irc line. %%---------------------------------------------------------------------- parseUserLine(Line) -> List = string:tokens(Line, ": "), #user{login = lists:nth(5, List), from = lists:nth(6, List), nick = lists:nth(8, List), name = lists:nthtail(10, List)}. %%---------------------------------------------------------------------- %% command/2 %% Send a command to the IRC server %%---------------------------------------------------------------------- command(Sock, Command) -> CompleteCmd = io_lib:format("~s~s", [Command, "\r\n"]), mdb_logger:debug("COMMAND: ~s~n", [Command]), gen_tcp:send(Sock, CompleteCmd). %%---------------------------------------------------------------------- %% is_chanop/1 %% Returns true if the given nick is a chanop or false otherwise %% A chanop as an '@' before its nickname. %%---------------------------------------------------------------------- is_chanop([$@ | Nickname]) -> true; is_chanop(Nickname) -> false. %%---------------------------------------------------------------------- %% nickname/1 %% Return the nickname (removing '@' to chanop) %% A chanop as an '@' before its nickname. %%---------------------------------------------------------------------- nickname([$@ | Nickname]) -> Nickname; nickname(Nickname) -> Nickname. %%---------------------------------------------------------------------- %% split_nick_user/1 %% Return the nickname in lower case and %% the user %%---------------------------------------------------------------------- split_nick_user(HeaderFrom) -> %% Split the string between Nick and User (separated by !) [Nick, User] = string:tokens(HeaderFrom, "!"), %% Remove chanop indicator from nick Nick2 = nickname(Nick), %% convert Nickname to lowercase Nick3 = misc_tools:lower_string(Nick2), %% Return a list: Nick, User [Nick3, User]. manderlbot-0.9.2/src/manderlbot.erl0100644000175000000620000000720007766050632016066 0ustar acidstaff%%%---------------------------------------------------------------------- %%% File : manderlbot.erl %%% Author : Dimitri Fontaine %%% Purpose : This app is an IRC bot %%% Created : 19 Feb 2002 by Dimitri Fontaine %%%---------------------------------------------------------------------- %%% %%% This file is part of Manderlbot. %%% %%% Manderlbot is free software; you can redistribute it and/or modify %%% it under the terms of the GNU General Public License as published by %%% the Free Software Foundation; either version 2 of the License, or %%% (at your option) any later version. %%% %%% Manderlbot is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %%% GNU General Public License for more details. %%% %%% See LICENSE for detailled license %%% %%% In addition, as a special exception, you have the permission to %%% link the code of this program with any library released under %%% the EPL license and distribute linked combinations including %%% the two. If you modify this file, you may extend this exception %%% to your version of the file, but you are not obligated to do %%% so. If you do not wish to do so, delete this exception %%% statement from your version. %%% %%%---------------------------------------------------------------------- -module(manderlbot). -author('dim@tuxfamily.org'). -include("config.hrl"). -include("log.hrl"). -behaviour(application). %% application callbacks -export([start/2, stop/1]). %%%---------------------------------------------------------------------- %%% Callback functions from application %%%---------------------------------------------------------------------- %%---------------------------------------------------------------------- %% Func: start/2 %% Returns: {ok, Pid} | %% {ok, Pid, State} | %% {error, Reason} %%---------------------------------------------------------------------- start(Type, StartArgs) -> %% First read args {Config_file, Log_file, Log_level} = read_args(), case manderlbot_sup:start_link(Config_file, Log_file) of {ok, Pid} -> %% init the manderlbot system case init(Config_file, Log_file, Log_level) of ok -> {ok, Pid}; {error, Reason} -> {error, Reason} end; Error -> Error end. %%---------------------------------------------------------------------- %% Func: stop/1 %% Returns: any %%---------------------------------------------------------------------- stop(State) -> ok. %%%---------------------------------------------------------------------- %%% Internal functions %%%---------------------------------------------------------------------- init(Config_file, Log_file, Log_level) -> %% Init first the logger mdb_logger:add_handler({Log_file, Log_level}), mdb_logger:notice("Manderlbot starting with ~s~n", [Config_file]), %% Read the config and start the bots case config_srv:readConf() of ok -> ok; {error, Reason} -> mdb_logger:error("Could not init manderlbot: ~p~n", [Reason]), {error, Reason} end. %%---------------------------------------------------------------------- %% Func: read_args/0 %% Returns: {Config_file, Log_file} %%---------------------------------------------------------------------- read_args() -> %% We take the manderlbot application defaults {ok, ConfigFile} = application:get_env(manderlbot, config_file), {ok, LogFile} = application:get_env(manderlbot, log_file), {ok, LogLevel} = application:get_env(manderlbot, log_level), {ConfigFile, LogFile, LogLevel}. manderlbot-0.9.2/src/manderlbot_sup.erl0100644000175000000620000000663707740537566017002 0ustar acidstaff%%%---------------------------------------------------------------------- %%% File : mdb_sup.erl %%% Author : Dimitri Fontaine %%% Purpose : Supervise all the bot instances (dynamic) %%% Created : 19 Feb 2002 by Dimitri Fontaine %%%---------------------------------------------------------------------- %%% %%% This file is part of Manderlbot. %%% %%% Manderlbot is free software; you can redistribute it and/or modify %%% it under the terms of the GNU General Public License as published by %%% the Free Software Foundation; either version 2 of the License, or %%% (at your option) any later version. %%% %%% Manderlbot is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %%% GNU General Public License for more details. %%% %%% See LICENSE for detailled license %%% %%% In addition, as a special exception, you have the permission to %%% link the code of this program with any library released under %%% the EPL license and distribute linked combinations including %%% the two. If you modify this file, you may extend this exception %%% to your version of the file, but you are not obligated to do %%% so. If you do not wish to do so, delete this exception %%% statement from your version. %%% %%%---------------------------------------------------------------------- -module(manderlbot_sup). -author('dim@tuxfamily.org'). -include("mdb.hrl"). -include("config.hrl"). -behaviour(supervisor). %% External exports -export([start_link/2]). %% supervisor callbacks -export([init/1]). %%%---------------------------------------------------------------------- %%% API %%%---------------------------------------------------------------------- start_link(Config_file, Log_file) -> supervisor:start_link({local, ?MODULE}, ?MODULE, [Config_file, Log_file]). %%%---------------------------------------------------------------------- %%% Callback functions from supervisor %%%---------------------------------------------------------------------- %%---------------------------------------------------------------------- %% Func: init/1 %% Returns: {ok, {SupFlags, [ChildSpec]}} | %% ignore | %% {error, Reason} %%---------------------------------------------------------------------- init([Config_file, Log_file]) -> Logger = {mdb_logger, {mdb_logger, start_link, []}, permanent, 2000, worker, [mdb_logger]}, BotSup = {mdb_bot_sup, {mdb_bot_sup, start_link, []}, permanent, 2000, supervisor, [mdb_bot_sup]}, BotLst = {mdb_botlist, {mdb_botlist, start_link, []}, permanent, 2000, worker, [mdb_botlist]}, BServ = {config_srv, {config_srv, start_link, [Config_file]}, permanent, 2000, worker, [config_srv]}, BLoto = {mdb_bhv_bloto, {mdb_bhv_bloto, start_link, []}, permanent, 2000, worker, [mdb_bhv_bloto]}, Pyramid = {mdb_bhv_pyramid, {mdb_bhv_pyramid, start_link, []}, permanent, 2000, worker, [mdb_bhv_pyramid]}, BSearch = {mdb_search, {mdb_search, start_link, []}, permanent, 2000, worker, [mdb_search]}, {ok, {{one_for_one, 3, 60}, [Logger, BotSup, BotLst, BServ, BLoto, Pyramid, BSearch]}}. %%%---------------------------------------------------------------------- %%% Internal functions %%%---------------------------------------------------------------------- manderlbot-0.9.2/src/mdb_bhv_action.erl0100644000175000000620000000420407720720463016672 0ustar acidstaff%%% File : mdb_bhv_action.erl %%% Author : Nicolas Niclausse %%% Purpose : Answer with Action IRC usage %%% Created : 12 Aug 2003 by Nicolas Niclausse %%%---------------------------------------------------------------------- %%% %%% This file is part of Manderlbot. %%% %%% Manderlbot is free software; you can redistribute it and/or modify %%% it under the terms of the GNU General Public License as published by %%% the Free Software Foundation; either version 2 of the License, or %%% (at your option) any later version. %%% %%% Manderlbot is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %%% GNU General Public License for more details. %%% %%% See LICENSE for detailled license %%% %%% In addition, as a special exception, you have the permission to %%% link the code of this program with any library released under %%% the EPL license and distribute linked combinations including %%% the two. If you modify this file, you may extend this exception %%% to your version of the file, but you are not obligated to do %%% so. If you do not wish to do so, delete this exception %%% statement from your version. %%% %%%---------------------------------------------------------------------- -module(mdb_bhv_action). -vc('$Id: mdb_bhv_action.erl,v 1.2 2003/08/20 16:26:27 nico Exp $ '). -author('nico@niclux.org'). -export([behaviour/5]). % MDB behaviour API -include("mdb.hrl"). %%%---------------------------------------------------------------------- %%% Function: behaviour/5 %%% Purpose: Answer with Action IRC usage - /me %%%---------------------------------------------------------------------- behaviour(Input = #data{header_to=BotName}, BotName, Data, BotPid, Channel) -> [NickFrom|IpFrom] = string:tokens(Input#data.header_from, "!"), lists:map(fun(String) -> mdb_bot:action(BotPid, String, NickFrom) end, Data); behaviour(Input, BotName, Data, BotPid, Channel) -> lists:map(fun(String) -> mdb_bot:action(BotPid, String) end, Data). manderlbot-0.9.2/src/mdb_bhv_answer.erl0100644000175000000620000000431107720720463016713 0ustar acidstaff%%% File : mdb_bhv_answer.erl %%% Author : Nicolas Niclausse %%% Purpose : %%% Created : 12 Aug 2003 by Nicolas Niclausse %%%---------------------------------------------------------------------- %%% %%% This file is part of Manderlbot. %%% %%% Manderlbot is free software; you can redistribute it and/or modify %%% it under the terms of the GNU General Public License as published by %%% the Free Software Foundation; either version 2 of the License, or %%% (at your option) any later version. %%% %%% Manderlbot is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %%% GNU General Public License for more details. %%% %%% See LICENSE for detailled license %%% %%% In addition, as a special exception, you have the permission to %%% link the code of this program with any library released under %%% the EPL license and distribute linked combinations including %%% the two. If you modify this file, you may extend this exception %%% to your version of the file, but you are not obligated to do %%% so. If you do not wish to do so, delete this exception %%% statement from your version. %%% %%%---------------------------------------------------------------------- -module(mdb_bhv_answer). -vc('$Id: mdb_bhv_answer.erl,v 1.2 2003/08/20 16:26:27 nico Exp $ '). -author('nico@niclux.org'). -export([behaviour/5]). % MDB behaviour API -include("mdb.hrl"). %%%---------------------------------------------------------------------- %%% Function: behaviour/5 %%% Purpose: Answer the given (config) data to the one who talk %%%---------------------------------------------------------------------- behaviour(Input = #data{header_to=BotName}, BotName, Data, BotPid, Channel) -> [NickFrom|IpFrom] = string:tokens(Input#data.header_from, "!"), lists:map(fun(String) -> mdb_bot:say(BotPid, String, NickFrom) end, Data); behaviour(Input, BotName, Data, BotPid, Channel) -> [NickFrom|IpFrom] = string:tokens(Input#data.header_from, "!"), lists:map(fun(String) -> mdb_bot:say(BotPid, NickFrom ++ ": " ++ String) end, Data). manderlbot-0.9.2/src/mdb_bhv_bloto.erl0100644000175000000620000001362507720720463016543 0ustar acidstaff%%%---------------------------------------------------------------------- %%% File : mdb_bhv_bloto.erl %%% Author : Dimitri Fontaine %%% Purpose : Count the buzzwords and give a winner %%% Created : 6 Mar 2002 by Dimitri Fontaine %%%---------------------------------------------------------------------- %%% %%% This file is part of Manderlbot. %%% %%% Manderlbot is free software; you can redistribute it and/or modify %%% it under the terms of the GNU General Public License as published by %%% the Free Software Foundation; either version 2 of the License, or %%% (at your option) any later version. %%% %%% Manderlbot is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %%% GNU General Public License for more details. %%% %%% See LICENSE for detailled license %%% %%% In addition, as a special exception, you have the permission to %%% link the code of this program with any library released under %%% the EPL license and distribute linked combinations including %%% the two. If you modify this file, you may extend this exception %%% to your version of the file, but you are not obligated to do %%% so. If you do not wish to do so, delete this exception %%% statement from your version. %%% %%%---------------------------------------------------------------------- -module(mdb_bhv_bloto). -author('tux@tuxfamily.org'). %%-compile(export_all). %%-export([Function/Arity, ...]). -behaviour(gen_server). %% External exports -export([behaviour/5]). % MDB behaviour API -export([start_link/0, add/2, reset/2]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -include("mdb.hrl"). -define(timeout, 5000). -define(MAX, 4). %% put here MAX-1 %%%---------------------------------------------------------------------- %%% API %%%---------------------------------------------------------------------- %%%---------------------------------------------------------------------- %%% Function: behaviour/5 %%% Purpose: Play to business loto... %%%---------------------------------------------------------------------- behaviour(Input, BotName, Data, BotPid, Channel) -> [NickFrom|IpFrom] = string:tokens(Input#data.header_from, "!"), case bloto:add(NickFrom, Channel) of {winner, Nick} -> mdb_bot:say(BotPid, Nick ++ " " ++ Data); Other -> ok end. start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). add(Nick, Channel) -> gen_server:call(?MODULE, {add, Nick, Channel}, ?timeout). %%%---------------------------------------------------------------------- %%% Callback functions from gen_server %%%---------------------------------------------------------------------- %%---------------------------------------------------------------------- %% Func: init/1 %% Returns: {ok, State} | %% {ok, State, Timeout} | %% ignore | %% {stop, Reason} %%---------------------------------------------------------------------- init([]) -> {ok, []}. %%---------------------------------------------------------------------- %% Func: handle_call/3 %% Returns: {reply, Reply, State} | %% {reply, Reply, State, Timeout} | %% {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, Reply, State} | (terminate/2 is called) %% {stop, Reason, State} (terminate/2 is called) %%---------------------------------------------------------------------- handle_call({add, Nick, Channel}, From, List) -> case lists:keysearch({Nick, Channel}, 1, List) of {value, {{Nick, Channel}, ?MAX}} -> {reply, {winner, Nick}, reset(Channel, List)}; {value, {{Nick, Channel}, N}} -> NewList = lists:keyreplace({Nick, Channel}, 1, List, {{Nick, Channel}, N+1}), {reply, ok, NewList}; false -> {reply, ok, [{{Nick, Channel}, 1}|List]} end; handle_call(Request, From, State) -> Reply = ok, {reply, Reply, State}. %%---------------------------------------------------------------------- %% Func: handle_cast/2 %% Returns: {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, State} (terminate/2 is called) %%---------------------------------------------------------------------- handle_cast(Msg, State) -> {noreply, State}. %%---------------------------------------------------------------------- %% Func: handle_info/2 %% Returns: {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, State} (terminate/2 is called) %%---------------------------------------------------------------------- handle_info(Info, State) -> {noreply, State}. %%---------------------------------------------------------------------- %% Func: terminate/2 %% Purpose: Shutdown the server %% Returns: any (ignored by gen_server) %%---------------------------------------------------------------------- terminate(Reason, State) -> ok. %%---------------------------------------------------------------------- %% Func: code_change/3 %%---------------------------------------------------------------------- code_change(OldVsn, State, Extra) -> {ok, State}. %%%---------------------------------------------------------------------- %%% Internal functions %%%---------------------------------------------------------------------- %%---------------------------------------------------------------------- %% Func: reset/3 %% Clean the List of all the Chan occurences %%---------------------------------------------------------------------- reset(Chan, List) -> reset(Chan, List, []). reset(Chan, [], Acc) -> lists:reverse(Acc); reset(Chan, [{{_Nick, Chan}, _Score}|Tail], Acc) -> reset(Chan, Tail, Acc); reset(Chan, [Head|Tail], Acc) -> reset(Chan, Tail, [Head|Acc]). manderlbot-0.9.2/src/mdb_bhv_debian_file.erl0100644000175000000620000000453607752003676017653 0ustar acidstaff%%% File : mdb_bhv_debian_pkg.erl %%% Author : Nicolas Niclausse %%% Purpose : search for files in debian package %%% Created : 12 Aug 2003 by Nicolas Niclausse %%%---------------------------------------------------------------------- %%% %%% This file is part of Manderlbot. %%% %%% Manderlbot is free software; you can redistribute it and/or modify %%% it under the terms of the GNU General Public License as published by %%% the Free Software Foundation; either version 2 of the License, or %%% (at your option) any later version. %%% %%% Manderlbot is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %%% GNU General Public License for more details. %%% %%% See LICENSE for detailled license %%% %%% In addition, as a special exception, you have the permission to %%% link the code of this program with any library released under %%% the EPL license and distribute linked combinations including %%% the two. If you modify this file, you may extend this exception %%% to your version of the file, but you are not obligated to do %%% so. If you do not wish to do so, delete this exception %%% statement from your version. %%% %%%---------------------------------------------------------------------- -module(mdb_bhv_debian_file). -vc('$Id: mdb_bhv_debian_file.erl,v 1.4 2003/11/04 20:10:38 dim Exp $ '). -author('nico@niclux.org'). -export([behaviour/5]). % MDB behaviour API -include("mdb.hrl"). -include("log.hrl"). %%%---------------------------------------------------------------------- %%% Function: behaviour/5 %%% Purpose: search for files in debian package %%%---------------------------------------------------------------------- behaviour(Input, BotName, Data, BotPid, Channel) -> mdb_logger:info("DEBIAN file input: ~p~n", [Input#data.body]), [Key, String | Args] = string:tokens(Input#data.body," "), case Args of [] -> mdb_logger:info("DEBIAN criteria: ~p~n", [String]), debian:search([file, String], Input, BotPid, BotName, Channel); [Version | _] -> % which debian distrib mdb_logger:info("DEBIAN criteria: ~p,~p~n", [String, Version]), debian:search([file, String, Version], Input, BotPid, BotName, Channel) end. manderlbot-0.9.2/src/mdb_bhv_debian_pkg.erl0100644000175000000620000000445607752003676017516 0ustar acidstaff%%% File : mdb_bhv_debian_pkg.erl %%% Author : Nicolas Niclausse %%% Purpose : search for debian packages %%% Created : 12 Aug 2003 by Nicolas Niclausse %%%---------------------------------------------------------------------- %%% %%% This file is part of Manderlbot. %%% %%% Manderlbot is free software; you can redistribute it and/or modify %%% it under the terms of the GNU General Public License as published by %%% the Free Software Foundation; either version 2 of the License, or %%% (at your option) any later version. %%% %%% Manderlbot is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %%% GNU General Public License for more details. %%% %%% See LICENSE for detailled license %%% %%% In addition, as a special exception, you have the permission to %%% link the code of this program with any library released under %%% the EPL license and distribute linked combinations including %%% the two. If you modify this file, you may extend this exception %%% to your version of the file, but you are not obligated to do %%% so. If you do not wish to do so, delete this exception %%% statement from your version. %%% %%%---------------------------------------------------------------------- -module(mdb_bhv_debian_pkg). -vc('$Id: mdb_bhv_debian_pkg.erl,v 1.4 2003/11/04 20:10:38 dim Exp $ '). -author('nico@niclux.org'). -export([behaviour/5]). % MDB behaviour API -include("mdb.hrl"). -include("log.hrl"). %%%---------------------------------------------------------------------- %%% Function: behaviour/5 %%% Purpose: search for debian packages %%%---------------------------------------------------------------------- behaviour(Input, BotName, Data, BotPid, Channel) -> mdb_logger:info("DEBIAN package input: ~p~n", [Input#data.body]), [Key, String | Args] = string:tokens(Input#data.body," "), case Args of [] -> mdb_logger:info("DEBIAN criteria: ~p~n", [String]), debian:search([package, String], Input, BotPid, BotName, Channel); [Version | _] -> % which debian distrib mdb_logger:info("DEBIAN criteria: ~p,~p~n", [String, Version]), debian:search([package, String, Version], Input, BotPid, BotName, Channel) end. manderlbot-0.9.2/src/mdb_bhv_dict.erl0100644000175000000620000001023507752003676016346 0ustar acidstaff%%% File : mdb_bhv_dict.erl %%% Author : Nicolas Niclausse %%% Purpose : search for word definition using the DICT protocol (RFC 2229) %%% Created : 16 Jul 2002 by Nicolas Niclausse %%%---------------------------------------------------------------------- %%% %%% This file is part of Manderlbot. %%% %%% Manderlbot is free software; you can redistribute it and/or modify %%% it under the terms of the GNU General Public License as published by %%% the Free Software Foundation; either version 2 of the License, or %%% (at your option) any later version. %%% %%% Manderlbot is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %%% GNU General Public License for more details. %%% %%% See LICENSE for detailled license %%% %%% In addition, as a special exception, you have the permission to %%% link the code of this program with any library released under %%% the EPL license and distribute linked combinations including %%% the two. If you modify this file, you may extend this exception %%% to your version of the file, but you are not obligated to do %%% so. If you do not wish to do so, delete this exception %%% statement from your version. %%% %%%---------------------------------------------------------------------- -module(mdb_bhv_dict). -author('nniclausse@idealx.com'). -revision(' $Id: mdb_bhv_dict.erl,v 1.4 2003/11/04 20:10:38 dim Exp $ '). -vsn(' $Revision: 1.4 $ '). -export([behaviour/5]). % MDB behaviour API -export([search/5, search/6, parse/1, set_request/1]). -include("mdb.hrl"). -include("log.hrl"). %%%---------------------------------------------------------------------- %%% Function: dict/5 %%% Purpose: ask dict for a word definition %%%---------------------------------------------------------------------- behaviour(Input, BotName, Data, BotPid, Channel) -> [DictName] = Data, mdb_logger:info("DICT input: ~p~n", [Input#data.body]), mdb_logger:info("DICT name: ~p~n", [DictName]), [Key | Args] = string:tokens(Input#data.body," "), Criteria = string:strip(Args), mdb_logger:info("DICT criteria: ~p~n", [Criteria]), case DictName of [] -> search(Criteria, Input, BotPid, BotName, Channel); _ -> search(Criteria, Input, BotPid, BotName, Channel, DictName) end. %% search with default dictionnary search(Keywords, Input, BotPid, BotName, Channel) -> mdb_logger:debug("params: ~p~n", [getConf()]), mdb_search:search({Keywords, Input, BotPid, BotName, Channel, getConf()}). search(Keywords, Input, BotPid, BotName, Channel, Dict) -> mdb_logger:debug("params: ~p~n", [getConf()]), mdb_search:search({[Keywords, Dict], Input, BotPid, BotName, Channel, getConf()}). getConf() -> {ok, {Host, Port, Default}} = config_srv:getDictConf(), #search_param{type = ?MODULE, server = Host, port = Port}. %%---------------------------------------------------------------------- %% Func: parse/1 %% Purpose: Parse data %% Returns: {stop, Result} | {stop} | {continue} | {continue, Result} %% continue -> continue parsing of incoming data %% stop -> stop parsing of incoming data %% Result -> String to be printed by mdb %%---------------------------------------------------------------------- parse("250" ++ Data) -> %% completed {stop}; parse("552" ++ Data) -> %% no match {stop, "not found"}; parse("150" ++ Data) -> %% response headers (n def. found) {continue}; parse("151" ++ Data) -> %% response headers (database name) {continue}; parse("") -> {continue}; parse(".\r\n") -> {continue}; parse(Data) -> case regexp:first_match(Data, "^[0-9][0-9][0-9] ") of {match,Start,Length} -> % skip protocol data {continue}; _ -> {continue, lists:subtract(Data,"\r\n")} end. %%%search using Dict dictionnary set_request([Keyword, Dict]) -> set_request(Keyword, Dict); %%% get default dict configuration set_request(Keyword) -> {ok, {_, _, Default}} = config_srv:getDictConf(), set_request(Keyword, Default). set_request(Keyword, Dict) -> "DEFINE " ++ Dict ++ " " ++ Keyword ++ io_lib:nl(). manderlbot-0.9.2/src/mdb_bhv_fortune.erl0100644000175000000620000000442107764117701017103 0ustar acidstaff%%% File : mdb_bhv_fortune.erl %%% Author : Dimitri Fontaine %%% Purpose : Choose at random a line in Data and answer it. %%% Created : 5 Dec 2003 by Dimitri Fontaine %%%---------------------------------------------------------------------- %%% %%% This file is part of Manderlbot. %%% %%% Manderlbot is free software; you can redistribute it and/or modify %%% it under the terms of the GNU General Public License as published by %%% the Free Software Foundation; either version 2 of the License, or %%% (at your option) any later version. %%% %%% Manderlbot is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %%% GNU General Public License for more details. %%% %%% See LICENSE for detailled license %%% %%% In addition, as a special exception, you have the permission to %%% link the code of this program with any library released under %%% the EPL license and distribute linked combinations including %%% the two. If you modify this file, you may extend this exception %%% to your version of the file, but you are not obligated to do %%% so. If you do not wish to do so, delete this exception %%% statement from your version. %%% %%%---------------------------------------------------------------------- -module(mdb_bhv_fortune). -vc('$Id: mdb_bhv_fortune.erl,v 1.1 2003/12/05 15:09:53 dim Exp $ '). -author('dim@tuxfamily.org'). -export([behaviour/5]). % MDB behaviour API -include("mdb.hrl"). %%%---------------------------------------------------------------------- %%% Function: behaviour/5 %%% Purpose: Choose at random a line in Data and answer it. %%%---------------------------------------------------------------------- behaviour(Input = #data{header_to=BotName}, BotName, Data, BotPid, Channel) -> [NickFrom|IpFrom] = string:tokens(Input#data.header_from, "!"), {A, B, C} = now(), random:seed(A, B, C), list:foreach(fun(Message) -> mdb_bot:say(BotPid, Message, NickFrom) end, lists:nth(random:uniform(length(Data)), Data)); behaviour(Input, BotName, Data, BotPid, Channel) -> {A, B, C} = now(), random:seed(A, B, C), mdb_bot:say(BotPid, lists:nth(random:uniform(length(Data)), Data)). manderlbot-0.9.2/src/mdb_bhv_google.erl0100644000175000000620000000703407752003676016702 0ustar acidstaff%%% File : mdb_bhv_google.erl %%% Author : Nicolas Niclausse %%% Purpose : ask google for the first match of a given keyword %%% Created : 16 Jul 2002 by Nicolas Niclausse %%%---------------------------------------------------------------------- %%% %%% This file is part of Manderlbot. %%% %%% Manderlbot is free software; you can redistribute it and/or modify %%% it under the terms of the GNU General Public License as published by %%% the Free Software Foundation; either version 2 of the License, or %%% (at your option) any later version. %%% %%% Manderlbot is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %%% GNU General Public License for more details. %%% %%% See LICENSE for detailled license %%% %%% In addition, as a special exception, you have the permission to %%% link the code of this program with any library released under %%% the EPL license and distribute linked combinations including %%% the two. If you modify this file, you may extend this exception %%% to your version of the file, but you are not obligated to do %%% so. If you do not wish to do so, delete this exception %%% statement from your version. %%% %%%---------------------------------------------------------------------- -module(mdb_bhv_google). -author('nico@niclux.org'). -revision(' $Id: mdb_bhv_google.erl,v 1.6 2003/11/04 20:10:38 dim Exp $ '). -vsn(' $Revision: 1.6 $ '). -export([behaviour/5]). % MDB behaviour API -export([search/5, parse/1, set_request/1]). -include("mdb.hrl"). -include("log.hrl"). -define(google_name, "www.google.com"). -define(google_port, 80). -define(notfound, "

Aucun document ne correspond"). -define(CR, "\n"). %%%---------------------------------------------------------------------- %%% Function: behaviour/5 %%% Purpose: ask google and give the first response %%%---------------------------------------------------------------------- behaviour(Input, BotName, Data, BotPid, Channel) -> mdb_logger:debug("GOOGLE input: ~p~n", [Input#data.body]), [Key | Args] = string:tokens(Input#data.body," "), Criteria= misc_tools:join("+", Args), mdb_logger:debug("GOOGLE criteria: ~p~n", [Criteria]), search(Criteria, Input, BotPid, BotName, Channel). search(Keywords, Input, BotPid, BotName, Channel) -> mdb_search:search({Keywords, Input, BotPid, BotName, Channel, #search_param{type = ?MODULE, server = ?google_name, port = ?google_port } }). %%---------------------------------------------------------------------- %% Func: parse/1 %% Purpose: Parse data %% Returns: {stop, Result} | {stop} | {continue} | {continue, Result} %% continue -> continue parsing of incoming data %% stop -> stop parsing of incoming data %% Result -> String to be printed by mdb %%---------------------------------------------------------------------- parse("Location: " ++ URL) -> {stop, URL }; parse(?notfound ++ _Data) -> {stop, "not found"}; parse(Data) -> {continue}. %%---------------------------------------------------------------------- %% Func: set_request/1 %% Purpose: Set the request given Keywords %% Returns: String %%---------------------------------------------------------------------- set_request(Keywords) -> "GET /search?q=" ++ Keywords ++"&hl=fr&btnI=J%27ai+de+la+chance HTTP/1.0" ++ ?CR ++ ?CR. manderlbot-0.9.2/src/mdb_bhv_mute.erl0100644000175000000620000000373007752003676016377 0ustar acidstaff%%% File : mdb_bhv_mute.erl %%% Author : Nicolas Niclausse %%% Purpose : allow the bot not to react for a while %%% Created : 12 Aug 2003 by Nicolas Niclausse %%%---------------------------------------------------------------------- %%% %%% This file is part of Manderlbot. %%% %%% Manderlbot is free software; you can redistribute it and/or modify %%% it under the terms of the GNU General Public License as published by %%% the Free Software Foundation; either version 2 of the License, or %%% (at your option) any later version. %%% %%% Manderlbot is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %%% GNU General Public License for more details. %%% %%% See LICENSE for detailled license %%% %%% In addition, as a special exception, you have the permission to %%% link the code of this program with any library released under %%% the EPL license and distribute linked combinations including %%% the two. If you modify this file, you may extend this exception %%% to your version of the file, but you are not obligated to do %%% so. If you do not wish to do so, delete this exception %%% statement from your version. %%% %%%---------------------------------------------------------------------- -module(mdb_bhv_mute). -vc('$Id: mdb_bhv_mute.erl,v 1.4 2003/11/04 20:10:38 dim Exp $ '). -author('nico@niclux.org'). -export([behaviour/5]). % MDB behaviour API -include("mdb.hrl"). -include("log.hrl"). %%%---------------------------------------------------------------------- %%% Function: behaviour/5 %%% Purpose: allow the bot not to react for a while %%%---------------------------------------------------------------------- behaviour(Input, BotName, Data, BotPid, Channel) -> [NickFrom|IpFrom] = string:tokens(Input#data.header_from, "!"), mdb_logger:debug("mute ~n", []), mdb_bot:mute(BotPid, NickFrom). manderlbot-0.9.2/src/mdb_bhv_pyramid.erl0100644000175000000620000002340607752003676017074 0ustar acidstaff%%%------------------------------------------------------------------- %%% File : mdb_bhv_pyramid.erl %%% Author : Dimitri Fontaine %%% Description : Implementation of french TV game « Pyramide » %%% %%% Created : 7 Nov 2002 by Dimitri Fontaine %%%---------------------------------------------------------------------- %%% %%% This file is part of Manderlbot. %%% %%% Manderlbot is free software; you can redistribute it and/or modify %%% it under the terms of the GNU General Public License as published by %%% the Free Software Foundation; either version 2 of the License, or %%% (at your option) any later version. %%% %%% Manderlbot is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %%% GNU General Public License for more details. %%% %%% See LICENSE for detailled license %%% %%% In addition, as a special exception, you have the permission to %%% link the code of this program with any library released under %%% the EPL license and distribute linked combinations including %%% the two. If you modify this file, you may extend this exception %%% to your version of the file, but you are not obligated to do %%% so. If you do not wish to do so, delete this exception %%% statement from your version. %%% %%%---------------------------------------------------------------------- -module(mdb_bhv_pyramid). -behaviour(gen_server). %%-------------------------------------------------------------------- %% Include files %%-------------------------------------------------------------------- -include("mdb.hrl"). -include("log.hrl"). %%-------------------------------------------------------------------- %% External exports -export([start_link/0, setWord/3, start/4, guess/3, behaviour/5]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -define(timeout, 5000). %%==================================================================== %% External functions %%==================================================================== %%-------------------------------------------------------------------- %% Function: start_link/0 %% Description: Starts the server %%-------------------------------------------------------------------- start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). setWord(Nick, Channel, Word) -> gen_server:call(?MODULE, {setWord, Nick, Channel, Word}, ?timeout). start(Nick, Channel, Player2, Nguess) -> gen_server:call(?MODULE, {start, Nick, Channel, Player2, Nguess}, ?timeout). guess(Nick, Channel, Word) -> gen_server:call(?MODULE, {guess, Nick, Channel, Word}, ?timeout). %%%---------------------------------------------------------------------- %%% Function: pyramid/5 %%% Purpose: implements a pyramid game, see comments %%%---------------------------------------------------------------------- behaviour(Input = #data{header_to=BotName}, BotName, Data, BotPid, Channel) -> %% - first player giving the bot the answer, before beginning the game, %% in private dialog [NickFrom|IpFrom] = string:tokens(Input#data.header_from, "!"), [Header, Word] = string:tokens(Input#data.body, ": "), case setWord(NickFrom, Channel, misc_tools:downcase(string:strip(Word))) of {ok, Message} -> mdb_bot:say(BotPid, Message, NickFrom), mdb_bot:say(BotPid, NickFrom ++ " has set a word to guess !"); {error, Reason} -> mdb_bot:say(BotPid, Reason, NickFrom) end; behaviour(Input, BotName, Data, BotPid, Channel) -> %% For this game, we have to detect some different cases on the %% same behaviour, that is : %% %% - beginning of game, first player inviting second and giving the %% number of tries %% %% - second player guess [NickFrom|IpFrom] = string:tokens(Input#data.header_from, "!"), mdb_logger:info("body: ~p~n", [Input#data.body]), case regexp:match(Input#data.body, "[A-Za-z0-9_]+/[0-9]+") of {match, S, L} -> [Player2, Nguess] = string:tokens(string:substr(Input#data.body, S, L), "/"), {ok, [{integer, 1, Iguess}],1} = erl_scan:string(Nguess), mdb_logger:info("pyramid: ~p invite ~p in ~p~n", [NickFrom, Player2, Iguess]), {_ok, SMsg} = start(NickFrom, Channel, Player2, Iguess), mdb_bot:say(BotPid, SMsg); _Whatever -> %% That is a guess [Header, Word] = string:tokens(Input#data.body, ": "), {_State, GMsg} = guess(NickFrom, Channel, misc_tools:downcase(string:strip(Word))), mdb_bot:say(BotPid, GMsg) end. %%==================================================================== %% Server functions %%==================================================================== %%-------------------------------------------------------------------- %% Function: init/1 %% Description: Initiates the server %% Returns: {ok, State} | %% {ok, State, Timeout} | %% ignore | %% {stop, Reason} %%-------------------------------------------------------------------- init([]) -> {ok, []}. %%-------------------------------------------------------------------- %% Function: handle_call/3 %% Description: Handling call messages %% Returns: {reply, Reply, State} | %% {reply, Reply, State, Timeout} | %% {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, Reply, State} | (terminate/2 is called) %% {stop, Reason, State} (terminate/2 is called) %%-------------------------------------------------------------------- handle_call({setWord, Nick, Channel, Word}, From, State) -> %% We receive the word to guess in private. The game has to be started %% to be able to set the word. case lists:keysearch({Nick, Channel}, 1, State) of {value, {{Player1, Channel}, noword, Player2, Nguess, Ntries}} -> {reply, {ok, Word ++ " has been set"}, lists:keyreplace({Player1, Channel}, 1, State, {{Player1, Channel}, Word, Player2, Nguess, Ntries}) }; {value, {{Player1, Channel}, GWord, Player2, Nguess, Ntries}} -> {reply, {error, "word to guess has already been set to: " ++ GWord}, State}; false -> {reply, {error, "No game started"}, State} end; handle_call({start, Nick, Channel, Player2, Nguess}, From, State) -> %% The game will start once the word to guess will be given in private %% We juste prepare and tell the players %% Attention: one game at a time ! case lists:keysearch({Nick, Channel}, 1, State) of {value, _} -> %% Game already started {reply, {error, "Game already started"}, State}; false -> %% Check the second player is not engaged case lists:keysearch(Player2, 3, State) of {value, _} -> {reply, {error, Player2 ++ " is already playing."}, State}; false -> %% We can start a new game {reply, {ok, Nick ++ ": please give me the word to guess (pv)"}, [{{Nick, Channel}, noword, Player2, Nguess, 1}|State]} end end; handle_call({guess, Nick, Channel, Word}, From, State) -> %% The second player is trying to guess the word case lists:keysearch(Nick, 3, State) of {value, {{Player1, Channel}, noword, Player2, Nguess, Ntries}} -> {reply, {ko, Player2 ++ ": please wait for " ++ Player1 ++ " to give a word to guess"}, State}; {value, {{Player1, Channel}, Word, Player2, Nguess, Ntries}} -> {reply, {ok, Player2 ++ " won in " ++ [48+Ntries] ++ " tries !"}, lists:keydelete({Player1, Channel}, 1, State)}; {value, {{Player1, Channel}, GWord, Player2, Nguess, Nguess}} -> {reply, {ko, Player2 ++ " failed to guess the word: " ++ GWord}, lists:keydelete({Player1, Channel}, 1, State)}; {value, {{Player1, Channel}, GWord, Player2, Nguess, Ntries}} -> %% Don't forget to increment the Ntries {reply, {ko, Player2 ++ ": try again ! "}, lists:keyreplace({Player1, Channel}, 1, State, {{Player1, Channel}, GWord, Player2, Nguess, Ntries + 1})}; false -> {reply, {ko, "No game started"}, State} end; handle_call(Request, From, State) -> Reply = ok, {reply, Reply, State}. %%-------------------------------------------------------------------- %% Function: handle_cast/2 %% Description: Handling cast messages %% Returns: {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, State} (terminate/2 is called) %%-------------------------------------------------------------------- handle_cast(Msg, State) -> {noreply, State}. %%-------------------------------------------------------------------- %% Function: handle_info/2 %% Description: Handling all non call/cast messages %% Returns: {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, State} (terminate/2 is called) %%-------------------------------------------------------------------- handle_info(Info, State) -> {noreply, State}. %%-------------------------------------------------------------------- %% Function: terminate/2 %% Description: Shutdown the server %% Returns: any (ignored by gen_server) %%-------------------------------------------------------------------- terminate(Reason, State) -> ok. %%-------------------------------------------------------------------- %% Func: code_change/3 %% Purpose: Convert process state when code is changed %% Returns: {ok, NewState} %%-------------------------------------------------------------------- code_change(OldVsn, State, Extra) -> {ok, State}. %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- manderlbot-0.9.2/src/mdb_bhv_random.erl0100644000175000000620000000433407720720464016702 0ustar acidstaff%%% File : mdb_bhv_random.erl %%% Author : Nicolas Niclausse %%% Purpose : Choose at random a line in Data and answer it. %%% Created : 12 Aug 2003 by Nicolas Niclausse %%%---------------------------------------------------------------------- %%% %%% This file is part of Manderlbot. %%% %%% Manderlbot is free software; you can redistribute it and/or modify %%% it under the terms of the GNU General Public License as published by %%% the Free Software Foundation; either version 2 of the License, or %%% (at your option) any later version. %%% %%% Manderlbot is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %%% GNU General Public License for more details. %%% %%% See LICENSE for detailled license %%% %%% In addition, as a special exception, you have the permission to %%% link the code of this program with any library released under %%% the EPL license and distribute linked combinations including %%% the two. If you modify this file, you may extend this exception %%% to your version of the file, but you are not obligated to do %%% so. If you do not wish to do so, delete this exception %%% statement from your version. %%% %%%---------------------------------------------------------------------- -module(mdb_bhv_random). -vc('$Id: mdb_bhv_random.erl,v 1.2 2003/08/20 16:26:28 nico Exp $ '). -author('nico@niclux.org'). -export([behaviour/5]). % MDB behaviour API -include("mdb.hrl"). %%%---------------------------------------------------------------------- %%% Function: behaviour/5 %%% Purpose: Choose at random a line in Data and answer it. %%%---------------------------------------------------------------------- behaviour(Input = #data{header_to=BotName}, BotName, Data, BotPid, Channel) -> [NickFrom|IpFrom] = string:tokens(Input#data.header_from, "!"), {A, B, C} = now(), random:seed(A, B, C), mdb_bot:say(BotPid, lists:nth(random:uniform(length(Data)), Data), NickFrom); behaviour(Input, BotName, Data, BotPid, Channel) -> {A, B, C} = now(), random:seed(A, B, C), mdb_bot:say(BotPid, lists:nth(random:uniform(length(Data)), Data)). manderlbot-0.9.2/src/mdb_bhv_reconf.erl0100644000175000000620000000363707720720464016703 0ustar acidstaff%%% File : mdb_bhv_reconf.erl %%% Author : Nicolas Niclausse %%% Purpose : Re-read the config file %%% Created : 12 Aug 2003 by Nicolas Niclausse %%%---------------------------------------------------------------------- %%% %%% This file is part of Manderlbot. %%% %%% Manderlbot is free software; you can redistribute it and/or modify %%% it under the terms of the GNU General Public License as published by %%% the Free Software Foundation; either version 2 of the License, or %%% (at your option) any later version. %%% %%% Manderlbot is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %%% GNU General Public License for more details. %%% %%% See LICENSE for detailled license %%% %%% In addition, as a special exception, you have the permission to %%% link the code of this program with any library released under %%% the EPL license and distribute linked combinations including %%% the two. If you modify this file, you may extend this exception %%% to your version of the file, but you are not obligated to do %%% so. If you do not wish to do so, delete this exception %%% statement from your version. %%% %%%---------------------------------------------------------------------- -module(mdb_bhv_reconf). -vc('$Id: mdb_bhv_reconf.erl,v 1.2 2003/08/20 16:26:28 nico Exp $ '). -author('nico@niclux.org'). -export([behaviour/5]). % MDB behaviour API -include("mdb.hrl"). %%%---------------------------------------------------------------------- %%% Function: behaviour/5 %%% Purpose: Re-read now the config file %%%---------------------------------------------------------------------- behaviour(Input, BotName, ConfigFile, BotPid, Channel) -> [NickFrom|IpFrom] = string:tokens(Input#data.header_from, "!"), mdb_bot:reconf(BotPid, NickFrom, ConfigFile). manderlbot-0.9.2/src/mdb_bhv_rejoin.erl0100644000175000000620000000347607720720464016716 0ustar acidstaff%%% File : mdb_bhv_reconf.erl %%% Author : Nicolas Niclausse %%% Purpose : When kicked, rejoin the chan %%% Created : 12 Aug 2003 by Nicolas Niclausse %%%---------------------------------------------------------------------- %%% %%% This file is part of Manderlbot. %%% %%% Manderlbot is free software; you can redistribute it and/or modify %%% it under the terms of the GNU General Public License as published by %%% the Free Software Foundation; either version 2 of the License, or %%% (at your option) any later version. %%% %%% Manderlbot is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %%% GNU General Public License for more details. %%% %%% See LICENSE for detailled license %%% %%% In addition, as a special exception, you have the permission to %%% link the code of this program with any library released under %%% the EPL license and distribute linked combinations including %%% the two. If you modify this file, you may extend this exception %%% to your version of the file, but you are not obligated to do %%% so. If you do not wish to do so, delete this exception %%% statement from your version. %%% %%%---------------------------------------------------------------------- -module(mdb_bhv_rejoin). -vc('$Id: mdb_bhv_rejoin.erl,v 1.2 2003/08/20 16:26:28 nico Exp $ '). -author('nico@niclux.org'). -export([behaviour/5]). % MDB behaviour API -include("mdb.hrl"). %%%---------------------------------------------------------------------- %%% Function: behaviour/5 %%% Purpose: When kicked, rejoin the chan %%%---------------------------------------------------------------------- behaviour(Input, BotName, Data, BotPid, Channel) -> mdb_bot:rejoin(BotPid). manderlbot-0.9.2/src/mdb_bhv_say.erl0100644000175000000620000000422107720720464016211 0ustar acidstaff%%% File : mdb_bhv_say.erl %%% Author : Nicolas Niclausse %%% Purpose : Say the data in the channel or to the speaker %%% Created : 12 Aug 2003 by Nicolas Niclausse %%%---------------------------------------------------------------------- %%% %%% This file is part of Manderlbot. %%% %%% Manderlbot is free software; you can redistribute it and/or modify %%% it under the terms of the GNU General Public License as published by %%% the Free Software Foundation; either version 2 of the License, or %%% (at your option) any later version. %%% %%% Manderlbot is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %%% GNU General Public License for more details. %%% %%% See LICENSE for detailled license %%% %%% In addition, as a special exception, you have the permission to %%% link the code of this program with any library released under %%% the EPL license and distribute linked combinations including %%% the two. If you modify this file, you may extend this exception %%% to your version of the file, but you are not obligated to do %%% so. If you do not wish to do so, delete this exception %%% statement from your version. %%% %%%---------------------------------------------------------------------- -module(mdb_bhv_say). -vc('$Id: mdb_bhv_say.erl,v 1.2 2003/08/20 16:26:28 nico Exp $ '). -author('nico@niclux.org'). -export([behaviour/5]). % MDB behaviour API -include("mdb.hrl"). %%%---------------------------------------------------------------------- %%% Function: behaviour/5 %%% Purpose: Say the data in the channel or to the speaker %%%---------------------------------------------------------------------- behaviour(Input = #data{header_to=BotName}, BotName, Data, BotPid, Channel) -> [NickFrom|IpFrom] = string:tokens(Input#data.header_from, "!"), lists:map(fun(String) -> mdb_bot:say(BotPid, String, NickFrom) end, Data); behaviour(Input, BotName, Data, BotPid, Channel) -> lists:map(fun(String) -> mdb_bot:say(BotPid, String) end, Data). manderlbot-0.9.2/src/mdb_bhv_think.erl0100644000175000000620000000544407720720464016542 0ustar acidstaff%%% File : mdb_bhv_think.erl %%% Author : Nicolas Niclausse %%% Purpose : Answer the given data, but waiting for random time %%% Created : 12 Aug 2003 by Nicolas Niclausse %%%---------------------------------------------------------------------- %%% %%% This file is part of Manderlbot. %%% %%% Manderlbot is free software; you can redistribute it and/or modify %%% it under the terms of the GNU General Public License as published by %%% the Free Software Foundation; either version 2 of the License, or %%% (at your option) any later version. %%% %%% Manderlbot is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %%% GNU General Public License for more details. %%% %%% See LICENSE for detailled license %%% %%% In addition, as a special exception, you have the permission to %%% link the code of this program with any library released under %%% the EPL license and distribute linked combinations including %%% the two. If you modify this file, you may extend this exception %%% to your version of the file, but you are not obligated to do %%% so. If you do not wish to do so, delete this exception %%% statement from your version. %%% %%%---------------------------------------------------------------------- -module(mdb_bhv_think). -vc('$Id: mdb_bhv_think.erl,v 1.2 2003/08/20 16:26:28 nico Exp $ '). -author('nico@niclux.org'). -export([behaviour/5]). % MDB behaviour API -include("mdb.hrl"). %%%---------------------------------------------------------------------- %%% Function: behaviour/5 %%% Purpose: Answer the given data, but waiting for random time %%% between the 2 lines to say. %%%---------------------------------------------------------------------- behaviour(Input = #data{header_to=BotName}, BotName, Data, BotPid, Channel) -> [NickFrom|IpFrom] = string:tokens(Input#data.header_from, "!"), case length(Data) of 1 -> [String] = Data, mdb_bot:say(BotPid, String, NickFrom); 2 -> [H|T] = Data, mdb_bot:say(BotPid, H, NickFrom), %% we sleep for a random time {A, B, C} = now(), random:seed(A, B, C), timer:sleep(random:uniform(?RNDTIME) + ?TIME), mdb_bot:say(BotPid, T, NickFrom); More -> behaviour(Input, BotName, Data, BotPid, Channel) end; behaviour(Input, BotName, Data, BotPid, Channel) -> case length(Data) of 1 -> [String] = Data, mdb_bot:say(BotPid, String); 2 -> [H|T] = Data, mdb_bot:say(BotPid, H), %% we sleep for a random time {A, B, C} = now(), random:seed(A, B, C), timer:sleep(random:uniform(?RNDTIME) + ?TIME), mdb_bot:say(BotPid, T); More -> behaviour(Input, BotName, Data, BotPid, Channel) end. manderlbot-0.9.2/src/mdb_bhv_timer.erl0100644000175000000620000000536207720720464016544 0ustar acidstaff%%% File : mdb_bhv_timer.erl %%% Author : Nicolas Niclausse %%% Purpose : %%% Created : 12 Aug 2003 by Nicolas Niclausse %%%---------------------------------------------------------------------- %%% %%% This file is part of Manderlbot. %%% %%% Manderlbot is free software; you can redistribute it and/or modify %%% it under the terms of the GNU General Public License as published by %%% the Free Software Foundation; either version 2 of the License, or %%% (at your option) any later version. %%% %%% Manderlbot is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %%% GNU General Public License for more details. %%% %%% See LICENSE for detailled license %%% %%% In addition, as a special exception, you have the permission to %%% link the code of this program with any library released under %%% the EPL license and distribute linked combinations including %%% the two. If you modify this file, you may extend this exception %%% to your version of the file, but you are not obligated to do %%% so. If you do not wish to do so, delete this exception %%% statement from your version. %%% %%%---------------------------------------------------------------------- -module(mdb_bhv_timer). -vc('$Id: mdb_bhv_timer.erl,v 1.2 2003/08/20 16:26:28 nico Exp $ '). -author('nico@niclux.org'). -export([behaviour/5]). % MDB behaviour API -include("mdb.hrl"). %%%---------------------------------------------------------------------- %%% Function: behaviour/5 %%% Purpose: Answer the given data, but waiting for random time %%% between the 2 lines to say. %%%---------------------------------------------------------------------- behaviour(Input = #data{header_to=BotName}, BotName, Data, BotPid, Channel) -> [NickFrom|IpFrom] = string:tokens(Input#data.header_from, "!"), case length(Data) of 1 -> [String] = Data, mdb_bot:say(BotPid, String, NickFrom); 2 -> [H|T] = Data, mdb_bot:say(BotPid, H, NickFrom), %% we sleep for a random time {A, B, C} = now(), random:seed(A, B, C), timer:sleep(random:uniform(?RNDTIME) + ?TIME), mdb_bot:say(BotPid, T, NickFrom); More -> behaviour(Input, BotName, Data, BotPid, Channel) end; behaviour(Input, BotName, Data, BotPid, Channel) -> case length(Data) of 1 -> [String] = Data, mdb_bot:say(BotPid, String); 2 -> [H|T] = Data, mdb_bot:say(BotPid, H), %% we sleep for a random time {A, B, C} = now(), random:seed(A, B, C), timer:sleep(random:uniform(?RNDTIME) + ?TIME), mdb_bot:say(BotPid, T); More -> behaviour(Input, BotName, Data, BotPid, Channel) end. manderlbot-0.9.2/src/mdb_bot.erl0100644000175000000620000002316207752003676015353 0ustar acidstaff%%%---------------------------------------------------------------------- %%% File : mdb_bot.erl %%% Author : Dimitri Fontaine %%% Purpose : Bot behaviours and connection manager %%% Created : 11 Aug 2002 by Dimitri Fontaine %%%---------------------------------------------------------------------- %%% %%% This file is part of Manderlbot. %%% %%% Manderlbot is free software; you can redistribute it and/or modify %%% it under the terms of the GNU General Public License as published by %%% the Free Software Foundation; either version 2 of the License, or %%% (at your option) any later version. %%% %%% Manderlbot is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %%% GNU General Public License for more details. %%% %%% See LICENSE for detailled license %%% %%% In addition, as a special exception, you have the permission to %%% link the code of this program with any library released under %%% the EPL license and distribute linked combinations including %%% the two. If you modify this file, you may extend this exception %%% to your version of the file, but you are not obligated to do %%% so. If you do not wish to do so, delete this exception %%% statement from your version. %%% %%%---------------------------------------------------------------------- -module(mdb_bot). -author('dim@tuxfamily.org'). %%-compile(export_all). %%-export([Function/Arity, ...]). -behaviour(gen_server). %% External exports -export([start_link/0, start_link/1, stop/0]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -export([say/2, say/3, action/2, mute/2, rejoin/1, reconf/3]). -define(timeout, 25000). %% Configure debugging mode: -include("mdb_macros.hrl"). %% Include record description -include("mdb.hrl"). -include("config.hrl"). -include("log.hrl"). %%%---------------------------------------------------------------------- %%% API %%%---------------------------------------------------------------------- start_link() -> gen_server:start_link(?MODULE, [], []). start_link(Args) -> gen_server:start_link(?MODULE, Args, []). stop() -> gen_server:cast(?MODULE, {stop}). %% Controling the bot environment %% Send a message a the channel the bot is connected to say(BotPid, Message) -> gen_server:call(BotPid, {say, Message}, ?timeout). say(BotPid, Message, To) -> gen_server:call(BotPid, {say, Message, To}, ?timeout). action(BotPid, Message) -> gen_server:call(BotPid, {action, Message}, ?timeout). mute(BotPid, NickName) -> gen_server:call(BotPid, {mute, NickName}, ?timeout). %% Rejoin the channel (Use it when you have been kicked) rejoin(BotPid) -> gen_server:call(BotPid, rejoin, ?timeout). reconf(BotPid, NickName, ConfigFile) -> gen_server:call(BotPid, {reconf, NickName, ConfigFile}, ?timeout). %%%---------------------------------------------------------------------- %%% Callback functions from gen_server %%%---------------------------------------------------------------------- %%---------------------------------------------------------------------- %% Func: init/1 %% Returns: {ok, State} | %% {ok, State, Timeout} | %% ignore | %% {stop, Reason} %%---------------------------------------------------------------------- init([RealName, Controler, Host, Port, Passwd, Channel, BList]) -> mdb_logger:debug("launching a new bot: ~p~n", [Channel]), {ok, Sock} = mdb_connection:connect(Host, Port), mdb_connection:log(Sock, Channel, Passwd, RealName), %% To avoid some re-entrance issue when starting bot from a reconf, %% we may start the bot giving it its behaviours list... {ok, RealBList} = case BList of [] -> config_srv:getBList(Channel#channel.name, Channel#channel.botname); List -> {ok, BList} end, State = #state{bot_pid=self(), channel = Channel#channel.name, controler = Controler, socket = Sock, nickname = Channel#channel.botname, passwd = Passwd, date = calendar:local_time(), behaviours = RealBList, host = Host, port = Port, joined = false, mode = unmuted }, {ok, State}. %%---------------------------------------------------------------------- %% Func: handle_call/3 %% Returns: {reply, Reply, State} | %% {reply, Reply, State, Timeout} | %% {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, Reply, State} | (terminate/2 is called) %% {stop, Reason, State} (terminate/2 is called) %%---------------------------------------------------------------------- handle_call({say, Message}, From, State=#state{socket=Sock, channel=Chan}) -> irc_lib:say(Sock, Chan, Message), {reply, ok, State}; handle_call({say, Message, To}, From, State=#state{socket=Sock, channel=Chan}) -> irc_lib:say(Sock, To, Message), {reply, ok, State}; handle_call({action, Message}, From, State=#state{socket=Sock, channel=Chan}) -> irc_lib:action(Sock, Chan, Message), {reply, ok, State}; handle_call(rejoin, From, State=#state{socket=Sock, channel=Chan}) -> irc_lib:join(Sock, Chan), {reply, ok, State}; handle_call({reconf, NickName, ConfigFile}, From, State=#state{socket=Sock, nickname=Nick, channel=Chan}) -> %% First read the conf file given %% Then get our behaviours list, and replace it in the State case is_controler(NickName, State#state.controler) of true -> case config_srv:reconf(Chan, Nick, ConfigFile) of {ok, BList} -> irc_lib:say(Sock, Chan, NickName ++ ": reconf done !"), {reply, ok, State#state{behaviours=BList}}; Error -> irc_lib:say(Sock, Chan, NickName ++ ": could not reconf " ++ ConfigFile ++ " !"), {reply, {error, reconf}, State} end; false -> irc_lib:say(Sock, Chan, NickName ++ ": " ++ "Who do you think you are to 'reconf' me ?"), {reply, {error, controller}, State} end; handle_call({mute, NickName}, From, State=#state{socket=Sock, channel=Chan}) -> case is_controler(NickName, State#state.controler) of true -> case State#state.mode of muted -> irc_lib:action(Sock, Chan, "is back"), {reply, ok, State#state{mode = unmuted}}; unmuted -> irc_lib:action(Sock, Chan, "is away"), {reply, ok, State#state{mode = muted}} end; false -> irc_lib:say(Sock, Chan, NickName ++ ": " ++ "Who do you think you are to mute me ?"), {reply, {error, controller}, State} end. %%---------------------------------------------------------------------- %% Func: handle_cast/2 %% Returns: {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, State} (terminate/2 is called) %%---------------------------------------------------------------------- handle_cast(Msg, State) -> {noreply, State}. %%---------------------------------------------------------------------- %% Func: handle_info/2 %% Returns: {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, State} (terminate/2 is called) %%---------------------------------------------------------------------- handle_info({tcp, Socket, Data}, State = #state{joined = false}) -> Buffer = binary_to_list(State#state.buffer), List = Buffer ++ binary_to_list(Data), case mdb_dispatch:process_data(Socket, List, State) of {joined, Rest} -> {noreply, State#state{joined = true, buffer=list_to_binary(Rest)}}; {pong, Rest} -> %% This was just a 'ping' request %% We can now join the channel irc_lib:join(Socket, State#state.channel), {noreply, State#state{joined = true, buffer=list_to_binary(Rest)}}; {ok, Rest} -> {noreply, State#state{buffer=list_to_binary(Rest)}} end; handle_info({tcp, Socket, Data}, State) -> Buffer = binary_to_list(State#state.buffer), List = Buffer ++ binary_to_list(Data), case mdb_dispatch:process_data(Socket, List, State) of {pong, Rest} -> {noreply, State#state{buffer=list_to_binary(Rest)}}; {ok, Rest} -> {noreply, State#state{buffer=list_to_binary(Rest)}} end; handle_info({tcp_einval, Socket}, State) -> {noreply, State}; handle_info({tcp_error, Socket}, State) -> {ok, NewState} = mdb_connection:manage_reconnect(State), {noreply, NewState}; handle_info({tcp_closed, Socket}, State) -> {ok, NewState} = mdb_connection:manage_reconnect(State), {noreply, NewState}. %%---------------------------------------------------------------------- %% Func: terminate/2 %% Purpose: Shutdown the server %% Returns: any (ignored by gen_server) %%---------------------------------------------------------------------- terminate(Reason, State) -> mdb_logger:notice("~p is quitting ~p [~p]~n", [State#state.nickname, State#state.channel, Reason]), irc_lib:quit(State#state.socket, Reason), ok. %%---------------------------------------------------------------------- %% Func: code_change/3 %%---------------------------------------------------------------------- code_change(OldVsn, State, Extra) -> {ok, State}. %%%---------------------------------------------------------------------- %%% Internal functions %%%---------------------------------------------------------------------- %% This function support old way where controler is not a list is_controler(Nickname, Nickname) -> true; is_controler(Nickname, Controlers) -> lists:member(Nickname, Controlers). manderlbot-0.9.2/src/mdb_botlist.erl0100644000175000000620000001355207752003676016251 0ustar acidstaff%%%---------------------------------------------------------------------- %%% File : mdb_botlist.erl %%% Author : Dimitri Fontaine %%% Purpose : Manage the mbd_bot running servers, and the Sockets %%% already in use for each bot. %%% Created : 16 Aug 2002 by Dimitri Fontaine %%%---------------------------------------------------------------------- %%% %%% This file is part of Manderlbot. %%% %%% Manderlbot is free software; you can redistribute it and/or modify %%% it under the terms of the GNU General Public License as published by %%% the Free Software Foundation; either version 2 of the License, or %%% (at your option) any later version. %%% %%% Manderlbot is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %%% GNU General Public License for more details. %%% %%% See LICENSE for detailled license %%% %%% In addition, as a special exception, you have the permission to %%% link the code of this program with any library released under %%% the EPL license and distribute linked combinations including %%% the two. If you modify this file, you may extend this exception %%% to your version of the file, but you are not obligated to do %%% so. If you do not wish to do so, delete this exception %%% statement from your version. %%% %%%---------------------------------------------------------------------- -module(mdb_botlist). -author('dim@tuxfamily.org'). %%-compile(export_all). %%-export([Function/Arity, ...]). -behaviour(gen_server). %% External exports -export([start_link/0]). -export([add/6, add/7, list/0]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2]). -export([code_change/3]). -include("config.hrl"). -include("log.hrl"). %% We need a big timeout in order to be able to connect to the server. -define(timeout, 10000). %%%---------------------------------------------------------------------- %%% API %%%---------------------------------------------------------------------- start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). add(Name, Controler, Host, Port, Passwd, Chan) -> gen_server:call(?MODULE, {add, Name, Controler, Host, Port, Passwd, Chan, []}, ?timeout). add(Name, Controler, Host, Port, Passwd, Chan, BList) -> gen_server:call(?MODULE, {add, Name, Controler, Host, Port, Passwd, Chan, BList}, ?timeout). list() -> gen_server:call(?MODULE, {list}, ?timeout). %%%---------------------------------------------------------------------- %%% Callback functions from gen_server %%%---------------------------------------------------------------------- %%---------------------------------------------------------------------- %% Func: init/1 %% Returns: {ok, State} | %% {ok, State, Timeout} | %% ignore | %% {stop, Reason} %%---------------------------------------------------------------------- init([]) -> {ok, []}. %%---------------------------------------------------------------------- %% Func: handle_call/3 %% Returns: {reply, Reply, State} | %% {reply, Reply, State, Timeout} | %% {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, Reply, State} | (terminate/2 is called) %% {stop, Reason, State} (terminate/2 is called) %%---------------------------------------------------------------------- handle_call( {add, Name, Controler, Host, Port, Pass, Chan, BList}, From, State) -> AlreadyStarted = fun({H, C}) when H == Host, C == Chan -> true; (_) -> false end, %% We search our entry in the State case length(lists:filter(AlreadyStarted, State)) of 1 -> %% This Bot is already running mdb_logger:notice( "Bot ~p already running on ~p ~n", [Name, Chan#channel.name]), {reply, {error, running}, State}; NotFound -> %% We have to start this bot mdb_logger:notice("starting bot ~p on ~p~n", [Name, Chan#channel.name]), case mdb_bot_sup:start_child(Name, Controler, Host, Port, Pass, Chan, BList) of {ok, Sock} -> {reply, ok, State ++ [{Host, Chan}]}; {error, Reason} -> {reply, {error, Reason}, State} end end; handle_call({list}, From, State) -> {reply, {ok, State}, State}; handle_call(Request, From, State) -> Reply = ok, {reply, Reply, State}. %%---------------------------------------------------------------------- %% Func: handle_cast/2 %% Returns: {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, State} (terminate/2 is called) %%---------------------------------------------------------------------- handle_cast(Msg, State) -> {noreply, State}. %%---------------------------------------------------------------------- %% Func: handle_info/2 %% Returns: {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, State} (terminate/2 is called) %%---------------------------------------------------------------------- handle_info(Info, State) -> {noreply, State}. %%---------------------------------------------------------------------- %% Func: terminate/2 %% Purpose: Shutdown the server %% Returns: any (ignored by gen_server) %%---------------------------------------------------------------------- terminate(Reason, State) -> ok. %%---------------------------------------------------------------------- %% Func: code_change/3 %%---------------------------------------------------------------------- code_change(OldVsn, State, Extra) -> {ok, State}. %%%---------------------------------------------------------------------- %%% Internal functions %%%---------------------------------------------------------------------- manderlbot-0.9.2/src/mdb_bot_sup.erl0100644000175000000620000000572207720720464016240 0ustar acidstaff%%%---------------------------------------------------------------------- %%% File : mdb_sup.erl %%% Author : Dimitri Fontaine %%% Purpose : Supervise all the bot instances (dynamic) %%% Created : 19 Feb 2002 by Dimitri Fontaine %%%---------------------------------------------------------------------- %%% %%% This file is part of Manderlbot. %%% %%% Manderlbot is free software; you can redistribute it and/or modify %%% it under the terms of the GNU General Public License as published by %%% the Free Software Foundation; either version 2 of the License, or %%% (at your option) any later version. %%% %%% Manderlbot is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %%% GNU General Public License for more details. %%% %%% See LICENSE for detailled license %%% %%% In addition, as a special exception, you have the permission to %%% link the code of this program with any library released under %%% the EPL license and distribute linked combinations including %%% the two. If you modify this file, you may extend this exception %%% to your version of the file, but you are not obligated to do %%% so. If you do not wish to do so, delete this exception %%% statement from your version. %%% %%%---------------------------------------------------------------------- -module(mdb_bot_sup). -author('dim@tuxfamily.org'). -include("mdb.hrl"). -include("config.hrl"). -behaviour(supervisor). %% External exports -export([start_link/0, start_child/6, start_child/7]). %% supervisor callbacks -export([init/1]). %%%---------------------------------------------------------------------- %%% API %%%---------------------------------------------------------------------- start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []). start_child(Name, Controler, Host, Port, Pass, Chan) -> supervisor: start_child(?MODULE, [[Name, Controler, Host, Port, Pass, Chan, []]]). start_child(Name, Controler, Host, Port, Pass, Chan, BList) -> supervisor: start_child(?MODULE, [[Name, Controler, Host, Port, Pass, Chan, BList]]). %%%---------------------------------------------------------------------- %%% Callback functions from supervisor %%%---------------------------------------------------------------------- %%---------------------------------------------------------------------- %% Func: init/1 %% Returns: {ok, {SupFlags, [ChildSpec]}} | %% ignore | %% {error, Reason} %%---------------------------------------------------------------------- init([]) -> Bot = {manderlbot, {mdb_bot, start_link, []}, transient, 2000, worker, [mdb_bot]}, {ok, {{simple_one_for_one, 3, 60}, [Bot]}}. %%%---------------------------------------------------------------------- %%% Internal functions %%%---------------------------------------------------------------------- manderlbot-0.9.2/src/mdb_connection.erl0100644000175000000620000001005607740505754016725 0ustar acidstaff%%%---------------------------------------------------------------------- %%% File : mdb_connection.erl %%% Author : Mickaël Rémond %%% Purpose : Connection management library. %%% Used by mdb_bot.erl %%% Created : 16 Sep 2001, Mickaël Rémond %%%---------------------------------------------------------------------- %%% %%% This program is free software; you can redistribute it and/or modify %%% it under the terms of the GNU General Public License as published by %%% the Free Software Foundation; either version 2 of the License, or %%% (at your option) any later version. %%% %%%---------------------------------------------------------------------- %%% %%% See LICENSE for detailled license %%% %%% In addition, as a special exception, you have the permission to %%% link the code of this program with any library released under %%% the EPL license and distribute linked combinations including %%% the two. If you modify this file, you may extend this exception %%% to your version of the file, but you are not obligated to do %%% so. If you do not wish to do so, delete this exception %%% statement from your version. %%%---------------------------------------------------------------------- -module(mdb_connection). -author('mickael.remond@erlang-fr.org'). -created('Date: 20010916'). -revision(' $Id: mdb_connection.erl,v 1.10 2003/10/07 09:48:28 dim Exp $ '). -vsn(' $Revision: 1.10 $ '). %% External exports (API) -export([connect/2, log/4, manage_reconnect/1]). %% Configure debugging mode: -include("mdb_macros.hrl"). -include("config.hrl"). -include("mdb.hrl"). %%---------------------------------------------------------------------- %% connect/2 %% Physically connects to the IRC server %%---------------------------------------------------------------------- connect(Server, Ip_port) -> %% TCP connection to the IRC server Connect = fun() -> gen_tcp:connect(Server, Ip_port, [binary, {packet, 0}, {nodelay, true}, {keepalive, true}, {active, true}, {reuseaddr, true}]) end, case Connect() of {ok, Sock} -> %% ?dbg("Connected to ~p", [Server]), {ok, Sock}; {error, Reason} -> %% If there is an error, wait 30 secondes and try to reconnect ?dbg("Server connection error: ~p", [Reason]), timer:sleep(30000), connect(Server, Ip_port) end. %% [#Port<0.80>,"#gli","h4ckd4w0rld","dtc"]}, %%---------------------------------------------------------------------- %% log/3 %% connect to a given channel %%---------------------------------------------------------------------- log(Sock, Channel = #channel{}, Passwd, RealName) -> %% Logging in log_in(Sock, Channel#channel.botname, Passwd, RealName), %% Join the given channel irc_lib:join(Sock, Channel#channel.name); % sometimes, Channel is not a record but the channel name only (bug ?) log(Sock, ChannelName, Passwd, RealName) -> %% Logging in log_in(Sock, ChannelName, Passwd, RealName), %% Join the given channel irc_lib:join(Sock, ChannelName). %%---------------------------------------------------------------------- %% log_in/3 %% Logging in: Give nick and realname to the server %%---------------------------------------------------------------------- %%log_in(Sock, Nickname, RealName, Password) -> log_in(Sock, Nickname, Passwd, RealName) -> irc_lib:login(Sock, Nickname, Passwd, RealName). %%irc_lib:passwd(Sock, "Password") %%---------------------------------------------------------------------- %% manage_reconnect/1 %% When something fails, automatically reconnects the bot %%---------------------------------------------------------------------- manage_reconnect(State) -> Host = State#state.host, Port = State#state.port, Chan = State#state.channel, Pass = State#state.passwd, Nick = State#state.nickname, {ok, Sock} = connect(Host, Port), log(Sock, Chan, Pass, Nick), {ok, State#state{socket = Sock, date = calendar:local_time(), joined = false }}. manderlbot-0.9.2/src/mdb_control.erl0100644000175000000620000000212407752003676016242 0ustar acidstaff%%%---------------------------------------------------------------------- %%% File : mdb_control.erl %%% Author : Dimitri Fontaine %%% Purpose : Control manderlbot, rpc to the running node %%% Created : 26 Aug 2003 by Dimitri Fontaine %%%---------------------------------------------------------------------- -module(mdb_control). -author('dim@tuxfamily.org'). -export([stop/0, status/0]). -include("config.hrl"). -include("log.hrl"). -define(mdb_node, "manderlbot"). %% %% The main control function %% stop() -> rpc:call(getNode(), application, stop, [manderlbot]), rpc:call(getNode(), init, stop, []), init:stop(). status() -> {ok, List} = rpc:call(getNode(), mdb_botlist, list, []), lists:map(fun({Host, Chan = #channel{}}) -> mdb_logger:notice("~s connected on ~s ~s~n", [Chan#channel.botname, Host, Chan#channel.name]) end, List), init:stop(). %% %% Some util functions %% getNode() -> [Node, Host] = string:tokens(atom_to_list(node()), "@"), list_to_atom(?mdb_node ++ "@" ++ Host). manderlbot-0.9.2/src/mdb_dispatch.erl0100644000175000000620000002654307740505754016375 0ustar acidstaff%%%---------------------------------------------------------------------- %%% File : mdb_dispatch.erl %%% Author : Mickaël Rémond %%% Purpose : Library gather the process of IRC event and the execution %%% of "behaviours" (event handling code). %%% Created : 16 Sep 2001, Mickaël Rémond %%%---------------------------------------------------------------------- %%% %%% This program is free software; you can redistribute it and/or modify %%% it under the terms of the GNU General Public License as published by %%% the Free Software Foundation; either version 2 of the License, or %%% (at your option) any later version. %%% %%%---------------------------------------------------------------------- %%% %%% See COPYING for detailled license %%% %%% In addition, as a special exception, you have the permission to %%% link the code of this program with any library released under %%% the EPL license and distribute linked combinations including %%% the two. If you modify this file, you may extend this exception %%% to your version of the file, but you are not obligated to do %%% so. If you do not wish to do so, delete this exception %%% statement from your version. %%%---------------------------------------------------------------------- -module(mdb_dispatch). -author('mickael.remond@erlang-fr.org'). -created('Date: 20010916'). -revision(' $Id: mdb_dispatch.erl,v 1.17 2003/10/07 09:48:28 dim Exp $ '). -vsn(' $Revision: 1.17 $ '). %% External exports (API) -export([process_data/3, treat_recv/3, append_botname/2]). %% -- Includes -- %% Configure debugging mode: -include("mdb_macros.hrl"). %% Include record description -include("mdb.hrl"). -include("config.hrl"). -define(NL, "\r\n"). -define(BOTNAME, "%BOTNAME"). %%---------------------------------------------------------------------- %% process_data/3 %% Parse the incoming data into lines, %% and spawn a treat_recv process on each one %%---------------------------------------------------------------------- process_data(Sock, [], State=#state{}) -> {ok, []}; process_data(Sock, [$P, $I, $N, $G, $ , $: | Tail], State=#state{}) -> %% We consider PING separately {Id, Rest} = cut_line(Tail), irc_lib:pong(Sock, Id), {pong, Rest}; process_data(Sock, Data, State=#state{joined = false}) -> %% When we do not have joined, we have to test for a join chan %% response. This can occurs when server require a pong before %% anything else %% So this code will manage the MOTD of the server {Line, Rest} = cut_line(Data), %% mdb_logger:log("MOTD ~p~n", [Line]), Chan = [$: | State#state.channel], case string:tokens(Line, " ") of [_Name, "JOIN", Chan | Tail] -> mdb_logger:log( "~s joined channel ~s~n", [State#state.nickname, State#state.channel]), %% There could be some behaviours on login process_data(Sock, Line ++ ?NL, State#state{joined=true}), {joined, Rest}; _ -> process_data(Sock, Rest, State) end; process_data(Sock, Data, State=#state{joined = true}) -> case cut_line(Data) of %% If we don't find ?NL in the data, then we add it in the buffer {Data, []} -> {ok, Data}; {Line, Rest} -> proc_lib:spawn(?MODULE, treat_recv, [Sock, list_to_binary(Line), State]), process_data(Sock, Rest, State) end; process_data(Sock, Data, State) -> ?dbg("process_data: ~p ~p ~p ~n", [Sock, Data, State]), {ok, []}. cut_line(Data) -> Pos = string:str(Data, ?NL), case Pos of 0 -> {Data, []}; _ -> Line = string:substr( Data, 1, Pos-1 ), Rest = string:substr( Data, Pos+2, string:len(Data) - (Pos-1) ), {Line, Rest} end. %%---------------------------------------------------------------------- %% treat_recv/3 %% Otherwise: %%---------------------------------------------------------------------- treat_recv(Sock, Data, State=#state{}) -> Result = binary_to_list(Data), %% The Parsed_Result is a list of data records. Parsed_result = input_to_record(Result), %% Get the list of behaviours that match to the current IRC line %% And for which the corresponding fun will be executed {ok, BList} = config_srv:getBehaviours(State#state.behaviours), %% mdb_logger:log("BList: ~p~n", [State#state.behaviours]), lists:map(fun(X) -> MatchingList = match_event(X, BList, State#state.nickname), dispatch_message(MatchingList, X, State) end, Parsed_result), %% Trace lists:map(fun(Res) -> [NickFrom|_] = string:tokens(Res#data.header_from, "!"), mdb_logger:log("~s ~s <~s> ~s~n", [State#state.nickname, Res#data.header_to, NickFrom, Res#data.body]) end, Parsed_result). %%---------------------------------------------------------------------- %% dispatch_message/3 %% We are executing the behaviour whose pattern is matching with %% the input from the IRC server %%---------------------------------------------------------------------- dispatch_message(Behaviours, Input, State = #state{mode=muted}) -> lists:map(fun(Behaviour = #behaviour{id = "mute"}) -> {M, F} = Behaviour#behaviour.function, apply(M, F, [Input, State#state.nickname, Behaviour#behaviour.data, State#state.bot_pid, State#state.channel]); (_) -> mdb_logger:log("~s MUTED", [State#state.nickname]) end, Behaviours); dispatch_message(Behaviours, Input, State = #state{}) -> lists:map(fun(Behaviour) -> mdb_logger:log("Match= ~p~n", [Behaviour#behaviour.function]), {M, F} = Behaviour#behaviour.function, apply(M, F, [Input, State#state.nickname, Behaviour#behaviour.data, State#state.bot_pid, State#state.channel]) end, Behaviours). %%---------------------------------------------------------------------- %% input_to_record/1 %% Convert a given input to a list of preparsed data records %%---------------------------------------------------------------------- input_to_record(ServerData) -> Lines = string:tokens(ServerData, ?NL), parse_lines(Lines, []). %%---------------------------------------------------------------------- %% parse_lines/2 %% Each input line will be a data record %%---------------------------------------------------------------------- parse_lines([], Result) -> lists:reverse(Result); parse_lines([Line|Lines], Result) -> parse_lines(Lines, [parse_line(Line) | Result]). %%---------------------------------------------------------------------- %% parse_line/1 %% Each line is split between the data record fields %%---------------------------------------------------------------------- parse_line([$: | ServerData]) -> BodyPos = string:chr(ServerData, $:), case BodyPos > 0 of true -> Header = string:substr(ServerData, 1, BodyPos - 1), Body = string:substr(ServerData, BodyPos + 1), Result = string:tokens(Header, " "), [Header_from, Header_op, Header_to, Header_options] = case Result of [From] -> [From, ?nodata, ?nodata, ?nodata]; [From, Op] -> [From, Op, ?nodata, ?nodata]; [From, Op, To | Options] -> [From, Op, To, lists:flatten(Options)] end, #data{header_from = Header_from, header_op = Header_op, header_to = Header_to, header_options = Header_options, body = Body}; false -> [Header_from, Header_op, Header_to | _Rest] = string:tokens(ServerData, " "), #data{header_from = Header_from, header_op = Header_op, header_to = Header_to, body = ""} end; %% I think that missing a ping generate a message that fall in this case and %% crash the process parse_line(ServerData) -> ?dbg("In ParseLine: unidentified: ~p", [ServerData]), %% Ignore. #data{}. %%---------------------------------------------------------------------- %% match_event/3 %% Returns the list of behaviour that should be executed on an irc input %%---------------------------------------------------------------------- match_event(Data, Behaviours, Nickname) -> match_event(data_as_list(Data), Behaviours, Nickname, []). match_event(Data, [], Nickname, Acc) -> lists:reverse(Acc); match_event(Data, [Behaviour|Behaviours], Nickname, Acc) -> MatchCritList = append_botname(data_as_list(Behaviour#behaviour.pattern), Nickname), ExlCritList = append_botname( data_as_list(Behaviour#behaviour.exclude_pattern), Nickname), %% We react on the behaviour only when the pattern is matched and %% the exclude pattern is not case {is_matching(Data, MatchCritList), is_matching(Data, ExlCritList, exclude)} of {true, false} -> match_event(Data, Behaviours, Nickname, [Behaviour|Acc]); _DontMatch -> match_event(Data, Behaviours, Nickname, Acc) end. %%---------------------------------------------------------------------- %% data_as_list/1 %% Convert the data record to a list of values %%---------------------------------------------------------------------- data_as_list(Data) -> DataList = tuple_to_list(Data), [RecordName | Rest] = DataList, Rest. %%---------------------------------------------------------------------- %% append_botname/2 %% Convert '%BOTNAME' wherever in the list by its real name %%---------------------------------------------------------------------- append_botname(List, Botname) -> lists:map(fun(Exp = {regexp, '_'}) -> Exp; ({regexp, String}) -> {ok, NewString, _C} = regexp:sub(String, ?BOTNAME, Botname), {regexp, NewString}; (Other) -> Other end, List). %%---------------------------------------------------------------------- %% is_matching/3 %% Check if the first list (data record field values) match the %% Criterium %% %% The last parameter is the mathing mode. %% %%---------------------------------------------------------------------- is_matching(Data, Criterium) -> is_matching(Data, Criterium, true, normal). is_matching(Data, Criterium, exclude) -> %% Weh excluding, we fail the test by default is_matching(Data, Criterium, false, exclude). is_matching(_Data, _Criterium, Result = false, normal) -> %% We cut the tests when in normal mode and found false result Result; is_matching(_Data, _Criterium, Result = true, exclude) -> %% We cut the tests when in exclude mode and found true result Result; is_matching([],[], Result, Mode) -> Result; is_matching([E|Elements], [C|Criteria], Result, Mode) -> %% mdb_logger:log("is_matching: ~p ~p~n", [C, E]), %% %% When we have no criterium, in exclude mode, we consider %% the match has failed. NoCrit = case Mode of normal -> true; exclude -> false end, case C of ?nodata -> is_matching(Elements, Criteria, lop(NoCrit, Result, Mode), Mode); {regexp, ?nodata} -> is_matching(Elements, Criteria, lop(NoCrit, Result, Mode), Mode); {regexp, Expr} -> is_matching(Elements, Criteria, misc_tools:is_matching_regexp(E, Expr), Mode); %% Should tag the Criterium value as {exact, Criterium} E -> is_matching(Elements, Criteria, lop(true, Result, Mode), Mode); _Other -> is_matching(Elements, Criteria, lop(false, Result, Mode), Mode) end. %% Logic Operator %% When excluding, keep current state if there is no criterium lop(false, true, exclude) -> true; lop(Bool, State, Mode) -> Bool. manderlbot-0.9.2/src/mdb_logger.erl0100644000175000000620000001406307752003676016046 0ustar acidstaff%%%---------------------------------------------------------------------- %%% File : mdb_logger.erl %%% Author : Dimitri Fontaine %%% Purpose : Manage to write the logs to a file %%% Created : 7 Oct 2003 by Dimitri Fontaine %%%---------------------------------------------------------------------- %%% %%% This file is part of Manderlbot. %%% %%% Manderlbot is free software; you can redistribute it and/or modify %%% it under the terms of the GNU General Public License as published by %%% the Free Software Foundation; either version 2 of the License, or %%% (at your option) any later version. %%% %%% Manderlbot is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %%% GNU General Public License for more details. %%% %%% See LICENSE for detailled license %%% %%% In addition, as a special exception, you have the permission to %%% link the code of this program with any library released under %%% the EPL license and distribute linked combinations including %%% the two. If you modify this file, you may extend this exception %%% to your version of the file, but you are not obligated to do %%% so. If you do not wish to do so, delete this exception %%% statement from your version. %%% %%%---------------------------------------------------------------------- -module(mdb_logger). -author('dim@tuxfamily.org'). %%-export([Function/Arity, ...]). -behaviour(gen_event). %% External exports -export([start_link/0, add_handler/1]). -export([log/2, log/3, emerg/2, alert/2, critic/2, error/2, warn/2, notice/2, info/2, debug/2]). %% gen_event callbacks -export([init/1, handle_event/2, handle_call/2, handle_info/2, terminate/2, code_change/3]). %% include -include("log.hrl"). %%%---------------------------------------------------------------------- %%% API %%%---------------------------------------------------------------------- start_link() -> gen_event:start_link({local, ?MODULE}). add_handler({LogFile, Level}) -> gen_event:add_handler(?MODULE, ?MODULE, [LogFile, Level]). %% Default to DEBUG level log(Mesg, Args) -> log(Mesg, Args, ?DEBUG). %% log with given level log(Mesg, Args, Level) -> gen_event:notify(?MODULE, {log, Mesg, Args, Level}). %% specific logs emerg(Mesg, Args) -> gen_event:notify(?MODULE, {log, Mesg, Args, ?EMERG}). alert(Mesg, Args) -> gen_event:notify(?MODULE, {log, Mesg, Args, ?ALERT}). critic(Mesg, Args) -> gen_event:notify(?MODULE, {log, Mesg, Args, ?CRIT}). error(Mesg, Args) -> gen_event:notify(?MODULE, {log, Mesg, Args, ?ERR}). warn(Mesg, Args) -> gen_event:notify(?MODULE, {log, Mesg, Args, ?WARN}). notice(Mesg, Args) -> gen_event:notify(?MODULE, {log, Mesg, Args, ?NOTICE}). info(Mesg, Args) -> gen_event:notify(?MODULE, {log, Mesg, Args, ?INFO}). debug(Mesg, Args) -> gen_event:notify(?MODULE, {log, Mesg, Args, ?DEBUG}). %%%---------------------------------------------------------------------- %%% Callback functions from gen_event %%%---------------------------------------------------------------------- %%---------------------------------------------------------------------- %% Func: init/1 %% Returns: {ok, State} | %% Other %%---------------------------------------------------------------------- init([LogFile, Level]) -> {ok, Fd} = file:open(LogFile, write), Level_num = lvl2numeric(Level), {ok, #log{fd=Fd, level=Level_num}}. %%---------------------------------------------------------------------- %% Func: handle_event/2 %% Returns: {ok, State} | %% {swap_handler, Args1, State1, Mod2, Args2} | %% remove_handler %%---------------------------------------------------------------------- handle_event({log, Mesg, Args}, State) -> do_log(Mesg, Args, State#log.fd), {ok, State}; %% when the loglevel is given, log only if the level is high enough handle_event({log, Mesg, Args, Level}, State) when State#log.level >= Level -> do_log(Mesg, Args, State#log.fd), {ok, State}; handle_event(Event, State) -> {ok, State}. %%---------------------------------------------------------------------- %% Func: handle_call/2 %% Returns: {ok, Reply, State} | %% {swap_handler, Reply, Args1, State1, Mod2, Args2} | %% {remove_handler, Reply} %%---------------------------------------------------------------------- handle_call(Request, State) -> Reply = ok, {ok, Reply, State}. %%---------------------------------------------------------------------- %% Func: handle_info/2 %% Returns: {ok, State} | %% {swap_handler, Args1, State1, Mod2, Args2} | %% remove_handler %%---------------------------------------------------------------------- handle_info(Info, State) -> {ok, State}. %%---------------------------------------------------------------------- %% Func: terminate/2 %% Purpose: Shutdown the server %% Returns: any %%---------------------------------------------------------------------- terminate(Reason, State) -> file:close(State#log.fd). %%---------------------------------------------------------------------- %% Func: code_change/3 %%---------------------------------------------------------------------- code_change(OldVsn, State, Extra) -> {ok, State}. %%%---------------------------------------------------------------------- %%% Internal functions %%%---------------------------------------------------------------------- do_log(Mesg, Args, Fd) -> {{Y, Mth, D}, {H, Min, S}} = calendar:local_time(), io:format(Fd, "~p/~p/~p ~p:~p:~p ", [Y, Mth, D, H, Min, S]), io:format(Fd, Mesg, Args). lvl2numeric(emerg) -> ?EMERG; lvl2numeric(alert) -> ?ALERT; lvl2numeric(crit) -> ?CRIT; lvl2numeric(err) -> ?ERR; lvl2numeric(warn) -> ?WARN; lvl2numeric(notice) -> ?NOTICE; lvl2numeric(info) -> ?INFO; lvl2numeric(debug) -> ?DEBUG; lvl2numeric(_) -> ?DEBUG. manderlbot-0.9.2/src/mdb_search.erl0100644000175000000620000002273307757410001016024 0ustar acidstaff%%%---------------------------------------------------------------------- %%% File : mdb_search.erl %%% Author : Nicolas Niclausse %%% Purpose : generic server for sending search requests and parsing %%% responses from remote servers %%% Created : 10 Aug 2002 by Nicolas Niclausse %%%---------------------------------------------------------------------- %%% %%% This file is part of Manderlbot. %%% %%% Manderlbot is free software; you can redistribute it and/or modify %%% it under the terms of the GNU General Public License as published by %%% the Free Software Foundation; either version 2 of the License, or %%% (at your option) any later version. %%% %%% Manderlbot is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %%% GNU General Public License for more details. %%% %%% See LICENSE for detailled license %%% %%% In addition, as a special exception, you have the permission to %%% link the code of this program with any library released under %%% the EPL license and distribute linked combinations including %%% the two. If you modify this file, you may extend this exception %%% to your version of the file, but you are not obligated to do %%% so. If you do not wish to do so, delete this exception %%% statement from your version. %%% %%%---------------------------------------------------------------------- -module(mdb_search). -author('nico@niclux.org'). -revision(' $Id: mdb_search.erl,v 1.8 2003/11/21 13:15:45 dim Exp $ '). -vsn(' $Revision: 1.8 $ '). %%-compile(export_all). %%-export([Function/Arity, ...]). -behaviour(gen_server). %% External exports -export([start_link/1]). -export([start_link/0]). -export([say_slow/5]). -export([stop/0, search/1]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2]). -export([code_change/3]). -include("mdb.hrl"). -define(tcp_timeout, 10000). % 10sec -define(say_sleep, 2000). % 2sec wait between each line to avoid flooding -define(max_lines, 8). % if more that max_lines to say, say it in private %%%---------------------------------------------------------------------- %%% API %%%---------------------------------------------------------------------- start_link(Args) -> gen_server:start_link({local, ?MODULE}, ?MODULE, Args, []). start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). stop() -> gen_server:cast(?MODULE, {stop}). %% asynchronous search search({Keywords, Input, BotPid, BotName, Channel, Params}) -> gen_server:cast(?MODULE, {search, Keywords, Input, BotPid, BotName, Channel, Params}). %%%---------------------------------------------------------------------- %%% Callback functions from gen_server %%%---------------------------------------------------------------------- %%---------------------------------------------------------------------- %% Func: init/1 %% Returns: {ok, State} | %% {ok, State, Timeout} | %% ignore | %% {stop, Reason} %%---------------------------------------------------------------------- init(Args) -> {ok, #search_state{}}. %%---------------------------------------------------------------------- %% Func: handle_call/3 %% Returns: {reply, Reply, State} | %% {reply, Reply, State, Timeout} | %% {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, Reply, State} | (terminate/2 is called) %% {stop, Reason, State} (terminate/2 is called) %%---------------------------------------------------------------------- handle_call(Args, From, State) -> {noreply, State}. %%---------------------------------------------------------------------- %% Func: handle_cast/2 %% Returns: {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, State} (terminate/2 is called) %%---------------------------------------------------------------------- handle_cast({search, Keywords, Input, BotPid, BotName, Channel, Params}, State) -> case gen_tcp:connect(Params#search_param.server, Params#search_param.port, [list, {packet, line}, {active, true}], ?tcp_timeout) of {ok, Socket} -> Request = apply(Params#search_param.type, set_request, [Keywords]), gen_tcp:send(Socket, Request), %% the request is identified by the Socket {noreply, State#search_state{requests=[{Socket, Input, BotPid, BotName, Channel, Params#search_param.type, []} | State#search_state.requests]}}; {error, Reason} -> say(Input, BotName, [atom_to_list(Params#search_param.type)++" connection failed"], BotPid, Channel), {noreply, State} end; handle_cast({stop}, State) -> {stop, normal, State}. %%---------------------------------------------------------------------- %% Func: handle_info/2 %% Returns: {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, State} (terminate/2 is called) %%---------------------------------------------------------------------- handle_info({tcp, Socket, Data}, State) -> case lists:keysearch(Socket, 1 , State#search_state.requests) of {value, {Socket, Input, BotPid, BotName, Channel, Type, Buffer}} -> case apply(Type, parse, [Data]) of {stop, Result} -> %% stop this connection and say result NewState = remove(Socket, State), say(Input, BotName, Buffer ++ [Result], BotPid, Channel), {noreply, NewState}; {continue, Result} -> %% continue and push string in buffer NewRequests = lists:keyreplace(Socket, 1, State#search_state.requests, {Socket, Input, BotPid, BotName, Channel, Type, Buffer ++ [Result]}), {noreply, State#search_state{requests= NewRequests}}; {say, Result} -> %% say result and continue to read data say(Input, BotName, Buffer ++ [Result], BotPid, Channel), {noreply, State}; {continue} -> %% continue to read {noreply, State}; {stop} -> %% close connection say(Input, BotName, Buffer, BotPid, Channel), NewState = remove(Socket, State), {noreply, NewState} end; _ -> {noreply, State} end; handle_info({tcp_einval, Socket}, State) -> {noreply, State}; handle_info({tcp_error, Socket}, State) -> NewState = remove(Socket, State), {noreply, NewState}; handle_info({tcp_closed, Socket}, State) -> NewState = remove(Socket, State), {noreply, NewState}. %%---------------------------------------------------------------------- %% Func: terminate/2 %% Purpose: Shutdown the server %% Returns: any (ignored by gen_server) %%---------------------------------------------------------------------- terminate(Reason, State) -> ok. %%---------------------------------------------------------------------- %% Func: code_change/3 %%---------------------------------------------------------------------- code_change(OldVsn, State, Extra) -> {ok, State}. %%%---------------------------------------------------------------------- %%% Internal functions %%%---------------------------------------------------------------------- %%---------------------------------------------------------------------- %% Func: say/4 %% Purpose: say a string to chan or to private %%---------------------------------------------------------------------- say(Input, BotName, [], BotPid, Channel) -> empty; %% list of strings to say in private, use mdb_bhv_say behaviour say(Input = #data{header_to=BotName}, BotName, Data, BotPid, Channel) -> spawn_link(?MODULE, say_slow, [Input, BotName, Data, BotPid, Channel]); %%% too much lines, talk in private say(Input, BotName, Data, BotPid, Channel) when length(Data) > ?max_lines -> %% set header_to to say it in private mdb_bot:say(BotPid, "answer in private"), %% spawn a new process to talk in private, with a sleep between each line spawn_link(?MODULE, say_slow, [Input#data{header_to=BotName}, BotName, Data, BotPid, Channel]); %%% talk in the channel say(Input, BotName, Data, BotPid, Channel) -> mdb_bhv_say:behaviour(Input, BotName, Data, BotPid, Channel). %%---------------------------------------------------------------------- %% Func: say_slow/4 %% Purpose: say a list of string with sleep intervals %%---------------------------------------------------------------------- say_slow(Input, BotName, [], BotPid, Channel) -> empty; say_slow(Input, BotName, [String | Data], BotPid, Channel) -> mdb_bhv_say:behaviour(Input, BotName, [String], BotPid, Channel), timer:sleep(?say_sleep), say_slow(Input, BotName, Data, BotPid, Channel). %%---------------------------------------------------------------------- %% Func: remove/2 %% Purpose: remove (and close) Socket entry if found in requests list %% Returns: new state %%---------------------------------------------------------------------- remove(Socket, State) -> gen_tcp:close(Socket), NewList = lists:keydelete(Socket, 1, State#search_state.requests), State#search_state{requests = NewList}. manderlbot-0.9.2/src/misc_tools.erl0100644000175000000620000001667707752003676016135 0ustar acidstaff%%%---------------------------------------------------------------------- %%% File : misc_tools.erl %%% Author : Mickael Remond %%% Purpose : This module gather various generic functions %%% we used for Manderlbot developpment %%% Created : 16 Nov 2000, Mickael Remond %%% %%%---------------------------------------------------------------------- %%% %%% This program is free software; you can redistribute it and/or modify %%% it under the terms of the GNU General Public License as published by %%% the Free Software Foundation; either version 2 of the License, or %%% (at your option) any later version. %%% %%%---------------------------------------------------------------------- %%% %%% See LICENSE for detailled license %%% %%% In addition, as a special exception, you have the permission to %%% link the code of this program with any library released under %%% the EPL license and distribute linked combinations including %%% the two. If you modify this file, you may extend this exception %%% to your version of the file, but you are not obligated to do %%% so. If you do not wish to do so, delete this exception %%% statement from your version. %%% %%%---------------------------------------------------------------------- -module(misc_tools). -author('mickael.remond@erlang-fr.org'). -created('Date: 20001116'). -revision(' $Revision: 1.11 $ '). -vsn(' $Id: misc_tools.erl,v 1.11 2003/11/04 20:10:38 dim Exp $ '). -export([nth/2, regexpize/1, date_to_integer/1, date_to_string/2, join/2, last_event/1, lower_string/1, upper_string/1, downcase/1, is_matching_regexp/2, is_matching_regexp/3]). -include("log.hrl"). %%---------------------------------------------------------------------- %% Function: nth/2 %% Purpose: Returns the element of a list according to its position %% Args: N = element position %% List = list to extract the element from %% Returns: The requested element %% or "" if the requested element does not exist %%---------------------------------------------------------------------- nth(N,List) when N > length(List) -> ""; nth(N, List) when N =< length(List) -> lists:nth(N, List). %%--------------------------------------------------------------------- %% Utility fonction : regexpize %% arg : a token in caps (ex DATE) %% return : a regexp allowing any combination of uppercase/lowercase %% for example : DATE gives [Dd][Aa][Tt][Ee] %% -------------------------------------------------------------------- regexpize(Token) when list(Token) -> %% This fonction transform the element "E" for example to %% "[Ee]" F = fun(Element) -> "["++ [Element] ++ [lower_char(Element)] ++ "]" end, %% Convert all the token using this function lists:flatmap(F, Token). %%-------------------------------------------------------------------- %% lower_char/1 %% It seems there isn't a lower/upper function in stdlib, so here's a %% quick hack, using specific ASCII charset %% FIXME : maybe not portable %%-------------------------------------------------------------------- lower_char(Char) when Char >= $A, Char =< $Z -> Char + 32 ; lower_char(OtherChar) -> OtherChar. %%---------------------------------------------------------------------- %% Function: lower_string/1 %% Purpose: Convert a string to lower case. %% It seems there isn't a lower/upper function in stdlib, so here it is %% Args: String is an erlang string. %% Returns: A string %%---------------------------------------------------------------------- lower_string(String) -> lower_string(String, []). lower_string([], Acc) -> lists:reverse(Acc); lower_string([H|T], Acc) when H >= $A, H =< $Z -> LowerChar = H + 32, lower_string(T, [LowerChar|Acc]); lower_string([H|T], Acc) -> lower_string(T, [H|Acc]). %%---------------------------------------------------------------------- %% Function: upper_string/1 %% Purpose: Convert a string to upper case. %% Args: String is an erlang string. %% Returns: A string %%---------------------------------------------------------------------- upper_string(String) -> upper_string(String, []). upper_string([], Acc) -> lists:reverse(Acc); upper_string([H|T], Acc) when H >= $a, H =< $z -> UpperChar = H - 32, upper_string(T, [UpperChar|Acc]); upper_string([H|T], Acc) -> upper_string(T, [H|Acc]). %%-------------------------------------------------------------------- %% date_to_integer/1 %% Converts a date to an Integer %%-------------------------------------------------------------------- date_to_integer({}) -> 0; date_to_integer({Date,Time}) -> {Year, Month, Day} = Date, {Hour, Minute, Second} = Time, Second + (Minute * 100) + (Hour * 10000) + (Day * 1000000) + (Month * 100000000) + (Year * 10000000000). %%-------------------------------------------------------------------- %% date_to_string/2 %% Converts a date into an English string %%-------------------------------------------------------------------- date_to_string(_Language, {}) -> ""; date_to_string(en, {Date, Time}) -> {Year, Month, Day} = Date, {Hour, Minute, Second} = Time, integer_to_list(Year) ++ "-" ++ integer_to_list(Month) ++ "-" ++ integer_to_list(Day) ++ " " ++ integer_to_list(Hour) ++ ":" ++ integer_to_list(Minute) ++ ":" ++ integer_to_list(Second). %%-------------------------------------------------------------------- %% last_event/1 %% TODO: Rewrite it to make it more generic and pass it to misc_tools. %% Return the last event from a given list of events: %% Events are of the form: %% {eventname, Date} %% Return the eventname of the latest event. %%-------------------------------------------------------------------- last_event(Events) -> last_event(Events, {none, {{0,0,0}, {0,0,0}}}). last_event([], LastEvent)-> LastEvent; last_event([Event|Events], LastEvent) -> {EventName, Date} = Event, {LastEventName, LastDate} = LastEvent, DateInt = date_to_integer(Date), LastDateInt = date_to_integer(LastDate), case DateInt > LastDateInt of true -> last_event(Events, Event); false -> last_event(Events, LastEvent) end. %% A Perl-style join --- concatenates all strings in Strings, %% separated by Sep. join(Sep, []) -> []; join(Sep, [First | List]) -> lists:foldl(fun(X, Sum) -> X ++ Sep ++ Sum end, First, List). %%---------------------------------------------------------------------- %% downcase/1 %% All upper cars of the String will be down cased %%---------------------------------------------------------------------- downcase(String) -> lists:map(fun(X) when $A =< X, X =< $Z -> X + $a - $A; (X) -> X end, String). %%---------------------------------------------------------------------- %% is_matching_regexp/2 %% is_matching_regexp/3 %% Check the match based on a regexp expression %% Here again you can pass in arguments the true and false values to be %% used. %%---------------------------------------------------------------------- is_matching_regexp(String, Regexp) -> is_matching_regexp(String, Regexp, {true, false}). is_matching_regexp(String, Regexp, {True, False}) -> mdb_logger:debug("is_matching_regexp: ~p ~p~n", [String, Regexp]), case regexp:match(misc_tools:downcase(String), misc_tools:downcase(Regexp)) of {match, _Start, _Length} -> True; nomatch -> False; {error, Error} -> False end. manderlbot-0.9.2/src/manderlbot.app.src0100644000175000000620000000162707770621626016663 0ustar acidstaff%% Manderlbot Application Configuration File %manderlbot.app.src ----- %% {application, manderlbot, [ {description, "Manderlbot"}, {vsn, "&manderlbot_vsn&"}, {id, "Manderlbot"}, {modules, [misc_tools, manderlbot, manderlbot_sup, mdb_connection, mdb_dispatch, mdb_logger, mdb_bhv_google, mdb_botlist, mdb_control, config, config_srv, debian, irc_lib, mdb_bhv_action, mdb_bhv_answer, mdb_bhv_bloto, mdb_bhv_debian_file, mdb_bhv_debian_pkg, mdb_bhv_dict, mdb_bhv_mute, mdb_bhv_pyramid, mdb_bhv_random, mdb_bhv_reconf, mdb_bhv_rejoin, mdb_bhv_say, mdb_bhv_think, mdb_bhv_timer, mdb_bot, mdb_bot_sup, mdb_search]}, {registered, []}, {applications, [kernel, stdlib]}, {env, [ {config_file, "/etc/manderlbot/config.xml"}, {log_file, "/var/log/manderlbot.log"}, {log_level, notice} ]}, {mod, {manderlbot, []}} ] }. manderlbot-0.9.2/src/manderlbot.rel.src0100644000175000000620000000017607720702434016653 0ustar acidstaff{release, {"Manderlbot", "&manderlbot_vsn&"}, {erts, "&erts_vsn&"}, [{kernel,"&kernel_vsn&"}, {stdlib,"&stdlib_vsn&"}]}. manderlbot-0.9.2/inc/0040755000175000000620000000000010000342175013176 5ustar acidstaffmanderlbot-0.9.2/inc/config.hrl0100644000175000000620000000211607740264635015174 0ustar acidstaff%%%---------------------------------------------------------------------- %%% File : config.hrl %%% Author : Dimitri Fontaine %%% Purpose : Define some config element as erlang structures %%% Created : 19 Feb 2002 by Dimitri Fontaine %%%---------------------------------------------------------------------- -author('dim@tuxfamily.org'). -define(arg_conffile, conf). -define(arg_logfile, log). -record(config, {name, % the name of the bot controler=[], % the nick of the one wich % controls the bot from irc dict={"localhost", "2628", "wn"}, servers=[], behaviours=[] }). -record(server, {host, port, passwd, channels = [] }). -record(channel, {name, botname, behaviours = [] }). %% The behavior as found in the config file %% With some patterns and some exclude patterns -record(cfg_behaviour, {name, action, from, to, op, option, pattern, exl_from, exl_to, exl_op, exl_option, exl_pattern, data = [] }). manderlbot-0.9.2/inc/irc.hrl0100644000175000000620000000056207564061621014501 0ustar acidstaff%%% File : irc.hrl %%% Author : Dimitri Fontaine %%% Purpose : Define some irc related data structures %%% Created : 12 Nov 2002 by Dimitri Fontaine -author('dim@tuxfamily.org'). %% --- #tuxfamily ~fontaine dim.net1.nerim.net irc.localnet dim H :0 Dimitri -record(user, {login, from, nick, name}). manderlbot-0.9.2/inc/log.hrl0100644000175000000620000000134107752003675014505 0ustar acidstaff%%% File : log.hrl %%% Author : Dimitri Fontaine %%% Description : Definitions for logger module (mdb_logger) %%% Created : 4 Nov 2003 by Dimitri Fontaine -record(log, {fd, level }). %% -define(EMERG, 0). % The system is unusable. -define(ALERT, 1). % Action should be taken immediately to address the problem. -define(CRIT, 2). % A critical condition has occurred. -define(ERR, 3). % An error has occurred. -define(WARN, 4). % A significant event that may require attention has occurred. -define(NOTICE,5). % An event that does not affect system operation has occurred. -define(INFO, 6). % An normal operation has occurred. -define(DEBUG, 7). % Debugging info manderlbot-0.9.2/inc/mdb.hrl0100644000175000000620000000347607720433716014477 0ustar acidstaff%mdb.hrl -------------------------- %% Parameters to start a bot -record(params, {server="", port=0, password="", channel="", nickserv_password="", nickname="", realname=""}). %% ---------------------- %% Used to keep track of %% who is connecting to %% the channel -record(spy, {nickname="", user="", last_phrase_date={}, last_phrase="", join_date={}, quit_date={}, quit_reason=""}). %% ---------------------- %% Used by mdb_bot.erl %% Parsed incoming IRC data -define(nodata, '_'). -record(data, {body = ?nodata, header_from = ?nodata, header_op = ?nodata, header_to = ?nodata, header_options = ?nodata}). -define(TIME, 2000). -define(RNDTIME, 3000). %% Bot process state -record(state, {bot_pid = "", channel = "", nickname = "", passwd = "", controler = "", socket = "", buffer = <<>>, behaviours = [], bot_state={}, date={}, host = "", port = "", joined = false, mode = unmuted % will either be muted or unmuted atom }). %% Behaviour description -record(behaviour, {id="", pattern, exclude_pattern, function, data}). %% ---------------------- %% Used by mdb_srv.erl %% Use to keep trace of the networks that we can connect to -record(network, {network_id=0, network_name="", server="", ip_port=0}). %% Use to keep track of running bots -record(bot, {bot_id, network_id, password, channel, nickserv_password, nickname, real_name, botpid, %% socket, date}). %% State of the server -record(srv_state, {networks=[], bots=[]}). -record(search_state, {requests = []}). -record(search_param, {server, port, type}). manderlbot-0.9.2/inc/mdb_macros.hrl0100644000175000000620000000056307516267324016040 0ustar acidstaff%% Debugging macro %% If the macro debug is defined before including this file, %% the debug print-outs are enabled. -define(debug, true). -ifdef(debug). -define(dbg(Fmt, Args), ok=io:format("~p: " ++ Fmt ++ "~n", [?LINE|Args])). -define(trace, ok=io:format("<<~p:~p>>~n", [?MODULE, ?LINE])). -else. -define(dbg(Fmt, Args), no_debug). -define(trace, no_debug). -endif. manderlbot-0.9.2/inc/xmerl.hrl0100644000175000000620000001142507516267324015060 0ustar acidstaff%%% The contents of this file are subject to the Erlang Public License, %%% Version 1.0, (the "License"); you may not use this file except in %%% compliance with the License. You may obtain a copy of the License at %%% http://www.erlang.org/license/EPL1_0.txt %%% %%% Software distributed under the License is distributed on an "AS IS" %%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %%% the License for the specific language governing rights and limitations %%% under the License. %%% %%% The Original Code is xmerl-0.13 %%% %%% The Initial Developer of the Original Code is Ericsson Telecom %%% AB. Portions created by Ericsson are Copyright (C), 1998, Ericsson %%% Telecom AB. All Rights Reserved. %%% %%% Contributor(s): %%% : suggested #xmlDocument{} %%% %%%---------------------------------------------------------------------- %%% #0. BASIC INFORMATION %%%---------------------------------------------------------------------- %%% File: xmerl.hrl %%% Author : Ulf Wiger %%% Date : 00-09-22 %%% Description : Record and macro definitions for xmerl %%%---------------------------------------------------------------------- %% records generated by the scanner %% -------------------------------- %% XML declaration -record(xmlDecl, { vsn, encoding, attributes }). %% Attribute -record(xmlAttribute, { name, parents = [], pos, language = [], % inherits the element's language expanded_name = [], nsinfo = [], % {Prefix, Local} | [] namespace = [], % inherits the element's namespace value }). %% namespace record -record(xmlNamespace, { default = [], nodes = [] }). %% namespace node - i.e. a {Prefix, URI} pair -record(xmlNsNode, { prefix, uri = [] }). %% XML Element -record(xmlElement, { name, parents = [], pos, attributes = [], content = [], language = [], expanded_name = [], nsinfo = [], % {Prefix, Local} | [] namespace = #xmlNamespace{} }). %% plain text -record(xmlText, { parents = [], pos, language = [], % inherits the element's language value }). %% plain text -record(xmlComment, { parents = [], pos, language = [], % inherits the element's language value }). %% processing instruction -record(xmlPI, { name, pos, value }). -record(xmlDocument, { content }). %% XPATH (xmerl_xpath, xmerl_pred_funcs) records -record(xmlContext, { axis_type = forward, context_node, context_position = 1, nodeset = [], bindings = [], functions = [], namespace = [], whole_document }). -record(xmlNode, { type = element, node, parents = [], pos = 1 }). -record(xmlObj, { type, value }). -record(xmerl_fun_states, {event, hook, rules, fetch, cont}). %% scanner state record -record(xmerl_scanner, { encoding = "ISO-8859-1", declarations = [], % [{Name, Attrs}] doctype_name, doctype_DTD = internal, % internal | DTDId rules, keep_rules = false, % delete (ets) tab if false namespace_conformant = false, % true | false event_fun, hook_fun, acc_fun, fetch_fun, close_fun, continuation_fun, rules_read_fun, rules_write_fun, directory, fetch_path = [], user_state, fun_states = #xmerl_fun_states{}, col = 1, line = 1 }). %% scanner events %% event : start | end -record(xmerl_event, { event, line, col, pos, data }). %% useful scanner macros %% --------------------- -ifdef(debug). -define(dbg(Fmt, Args), ok=io:format("~p: " ++ Fmt, [?LINE|Args])). -define(DBG, ok=io:format("<<~p:~p>>~n", [?MODULE, ?LINE])). -else. -define(dbg(Fmt, Args), no_debug). -define(DBG, no_debug). -endif. -define(space, 16#20). -define(cr, 16#9). -define(lf, 16#D). -define(tab, 16#A). %% whitespace consists of 'space', 'carriage return', 'line feed' or 'tab' -define(whitespace(H), H==?space ; H==?cr ; H==?lf ; H==?tab). -define(strip1, {_, T1, S1} = strip(T, S)). -define(strip2, {_, T2, S2} = strip(T1, S1)). -define(strip3, {_, T3, S3} = strip(T2, S2)). -define(strip4, {_, T4, S4} = strip(T3, S3)). -define(strip5, {_, T5, S5} = strip(T4, S4)). -define(strip6, {_, T6, S6} = strip(T5, S5)). -define(strip7, {_, T7, S7} = strip(T6, S6)). -define(strip8, {_, T8, S8} = strip(T7, S7)). -define(strip9, {_, T9, S9} = strip(T8, S8)). -define(strip10, {_, T10, S10} = strip(T9, S9)). -define(bump_col(N), ?dbg("bump_col(~p), US = ~p~n", [N, S0#xmerl_scanner.user_state]), S = S0#xmerl_scanner{col = S0#xmerl_scanner.col + N}). manderlbot-0.9.2/inc/xmerl_xlink.hrl0100644000175000000620000000071307516267324016263 0ustar acidstaff %% The following is a brief summary of the element types (columns) on %% which the global attributes are allowed: %% %% simple extended locator arc resource title %% type X X X X X X %% href X X %% role X X X X %% title X X X X %% show X X X %% actuate X X X %% from X %% to X %% -record(xlink, { type, % simple | extended | locator | arc | resource | title href, role title, show, actuate, from, to }). manderlbot-0.9.2/doc/0040755000175000000620000000000010000342175013172 5ustar acidstaffmanderlbot-0.9.2/doc/manderlbot.lyx0100644000175000000620000003446307770621626016113 0ustar acidstaff#LyX 1.3 created this file. For more info see http://www.lyx.org/ \lyxformat 221 \textclass report \language english \inputencoding latin1 \fontscheme default \graphics default \float_placement !htbp \paperfontsize default \spacing onehalf \papersize a4paper \paperpackage a4wide \use_geometry 0 \use_amsmath 0 \use_natbib 0 \use_numerical_citations 0 \paperorientation portrait \secnumdepth 3 \tocdepth 3 \paragraph_separation indent \defskip medskip \quotes_language english \quotes_times 2 \papercolumns 1 \papersides 1 \paperpagestyle fancy \layout Title Manderlbot, an erlang irc bot \layout Author Dimitri Fontaine \layout Standard \begin_inset LatexCommand \tableofcontents{} \end_inset \layout Chapter Introduction \layout Standard This document provides some helpfull information (or I hope so) about the manderlbot irc bot, on how to use and configure it. \layout Section* A bit of history \layout Standard But why did we wrote this software ? \layout Standard Well, I wanted an irc bot to play with, in order to have it say some silly things automatically on answer to our own idioties. I did not want an eggdrop or whatever controlling channel bot. I saw that manderlbot \begin_inset Foot collapsed true \layout Standard You can see it on \begin_inset LatexCommand \url[manderlbot SF page]{http://manderlbot.sourceforge.net} \end_inset \end_inset project existing, was already familiar with erlang developpment, so I began using it. \layout Standard The existing project was on early stage of development, and I wanted the bot to do more and more things. So I wrote some code to make it fit my needs. As the original authors would not consider my patches, I forked the project, keeping the name (they seemed not to work on their version at all), and hosting it on the TuxFamily services \begin_inset Foot collapsed true \layout Standard Tuxfamily services are found here\SpecialChar ~ : \begin_inset LatexCommand \url{http://tuxfamily.org} \end_inset \end_inset . \layout Chapter Using manderlbot \layout Standard This is the user documentation of manderlbot. \layout Section Install \layout Standard The better would be to run a debian GNU/Linux system. In that case, a simple \family typewriter apt-get install manderlbot \family default get you with all the necessary stuff. \layout Standard Otherwise, you'll have to get the last available manderlbot release on \family typewriter manderlbot.tuxfamily.org \family default , and the necessary tools, that is: \layout Itemize erlang system, see \family typewriter erlang.org \family default or your distribution vendor for some packages \layout Itemize \family typewriter xmerl-0.15 \family default library (which will allow for configuration file reading), available as a user contribution on the \family typewriter erlang.org \family default web site. \layout Standard If you are using \family typewriter windows \family default , as erlang is known to work even on such a \emph on system \emph default , you should be abble to install and run \family typewriter manderlbot \family default . But I did never try that, and do not plan to do it ever. Good luck! \layout Standard \SpecialChar ~ \layout Standard So you have installed erlang system and placed \family typewriter xmerl \family default library (version \family typewriter 0.15 \family default ) at the right place (on my debian system, it is found in \family typewriter /usr/lib/erlang/lib/xmerl-0.15 \family default ). You now can type the usual following: \layout LyX-Code $ make \layout LyX-Code $ sudo make install \layout Standard And manderlbot will be ready to be launched! \layout Section Launch and stop \layout Standard As of the 0.9.1 release of manderlbot, you can control manderlbot using the given \family typewriter /usr/bin/manderlbot \family default script: \layout LyX-Code $ manderlbot \layout LyX-Code manderlbot start|stop|restart|status \layout LyX-Code $ manderlbot start \layout Standard The stop option will kill the running manderlbot, and if started the status will list all the running bots, indicating the servers and channels where it is connected. \layout Standard If you want to redirect outputs, you'll then prefer that way: \layout LyX-Code $ manderlbot start >/var/log/manderlbot.log 2>&1 & \layout Standard Please make sure you are allowed to write in your log file! \layout Section Configure \layout Standard The \family typewriter manderlbot \family default configuration file is to be found in \family typewriter /etc/manderlbot/config.xml \family default , and has to be edited to fit your needs. By default it contains some basic rules as examples. \layout Subsection Configuration file \layout Standard The configuration file is an XML file containing the following elements: \layout Description manderlbot is the opening XML element of the configuration, and contains the properties \family typewriter name \family default and \family typewriter controler \family default . The name will be shown as the bot fullname, the controller property may contain one or more nicknames separated by spaces, only those people will then be allowed to operate on the running bot from irc channel. \layout Quote All the following sections, otherwise stated, are to be found under this one. \layout Description dict is the section where to define the dictionnary server you may want to use. See \family typewriter dict.org \family default for details about the protocol and servers. Be aware that you can run a dict server locally, and download some useful dictionnaries. \layout Quote The properties to define here are the dict server \family typewriter host \family default , the \family typewriter port \family default , and the \family typewriter default \family default dictionnary to use. \layout Description server allows you to define which servers manderlbot should connect to. The properties are \family typewriter host \family default and \family typewriter port \family default . You have to define a server section for each and every irc server you want \family typewriter manderlbot \family default to connect to. \layout Description channel section is where to configure the \family typewriter manderlbot \family default behaviour and name. This section has to be embedded in the server one. You have to define a channel section per channel you want \family typewriter manderlbot \family default to join on a server. \layout Quote The properties of channel section are \family typewriter name \family default , the channel name, \family typewriter botname \family default , the manderlbot nickname on that channel, and \family typewriter behaviours \family default , a list of behaviours name you want to activate for that channel. \layout Description behaviours will just contain your behaviour list \layout Description behaviour have to be found under the \family typewriter behaviours \family default section. You define here your behaviour, which properties are \family typewriter name \family default , the name to use in the channel definition, the \family typewriter action \family default , defining what will be done, and one or more of the followings pattern elements: \family typewriter pattern \family default , \family typewriter op \family default , \family typewriter to, option \family default and \family typewriter from \family default . You can even prefix those properties with ' \family typewriter exl_ \family default ' to get an exclude pattern match (see section \begin_inset LatexCommand \ref{sub:Behaviour-matching} \end_inset ). \layout Quote This element contains data wich will be used as the \emph on action \emph default parameter, as explain in section \begin_inset LatexCommand \ref{sub:Implemented-actions} \end_inset . \layout Subsection Behaviour matching \begin_inset LatexCommand \label{sub:Behaviour-matching} \end_inset \layout Standard So when you define a behaviour, you want \family typewriter manderlbot \family default to react on some event on irc channel it is connected to, and take some action. Here we see how to define the event you want it to react to. As on irc all you do is sending lines of text, an event as to be text line oriented. \layout Standard So \family typewriter manderlbot \family default configuration allows you to define some regexp \begin_inset Foot collapsed true \layout Standard Regular Expressions, see \begin_inset LatexCommand \url{http://www.regular-expressions.info} \end_inset \end_inset to match the lines received. If the line is matched, the associated action is done. Please note the regexp are all considered case insensitive ones. \layout Standard You can define some \emph on regexp \emph default on the following parts of the received line (containing some server information s relative to IRC protocol \begin_inset Foot collapsed true \layout Standard See the \begin_inset LatexCommand \url[RFC 1459]{http://www.faqs.org/rfcs/rfc1459.html} \end_inset \end_inset ): \layout Description pattern will try to match the user input, that is what your ordinary irc client will show you \layout Description op will try to match the irc operation, see the RFC for complete list (op can be \begin_inset Quotes eld \end_inset kick \begin_inset Quotes erd \end_inset or \begin_inset Quotes eld \end_inset join \begin_inset Quotes erd \end_inset for example) \layout Description to irc protocol \family typewriter to \family default field, will probably contain the channel name, so you won't need that... \layout Description option irc protocol \family typewriter option \family default field \layout Description from the nickname of the one who typed the current line, on the form \family typewriter nick!~user@host.domain.tld \layout Standard And in order to make it even more powerful and readable, you can define the same patterns with an ' \family typewriter exl_ \family default ' prefix, this will prevent the \family typewriter action \family default to being taken if it matches. So you can define the parameters \family typewriter exl_pattern \family default , \family typewriter exl_op \family default , \family typewriter exl_to \family default , \family typewriter exl_option \family default and \family typewriter exl_from \family default . \layout Standard Of course, you can use any combination of the listed parameters, thus being quite precise on what you want to react to. \layout Subsection Implemented actions \begin_inset LatexCommand \label{sub:Implemented-actions} \end_inset \layout Standard The \family typewriter action \family default parameter of the behaviour configuration element defines the manderlbot behaviour on matching a line. Here is a list of provided actions you can use. If you want \family typewriter manderlbot \family default to take an action not described here, you will have to write some erlang code to teach him what you want! \layout Standard The argument of the \family typewriter action \family default is the xml data given enclosed in the \family typewriter behaviour \family default element. \layout Description action send the given argument as if manderlbot had typed it after the \family typewriter /me \family default irc command. \layout Description answer send the line prefixed with the sender name and a colon. \layout Description bloto this will count the matched lines per user, and first obtaining 5 points has won the business loto game. Just define your buzzwords set and make it a regexp! \layout Description debian_file will search the irc given file using the debian web site cgi. The argument is not used. \layout Description debian_pkg will search the irc given package using the debian web site cgi. The argument is not used. \layout Description dict will ask your defined dict server for the given word. The argument may be the dictionnary name to use in the query, but defaults to the ' \family typewriter default \family default ' entry of the dict config element. \layout Description google will ask google for the rest of the irc line. The argument is not used. \layout Description mute will mute the bot, you have to be a controler to use that. The argument is not used. \layout Description pyramid is a game named after a french TV game. You have to make guess a world to an irc fellow on that channel, in a given number or tries. The argument is not used. \layout Description random will say one of the sentences listed in the arguments randomly. The sentences have to be separated by ' \family typewriter % \family default ' signs. \layout Description reconf will ask the bot to re-read its configuration. It allows you to handle dynamically your configuration, no need to restart the bot, and irc control! You have to be in the controler list to use this action. \layout Description rejoin allows you te rejoin a channel (useful on kick, just add a \family typewriter op= \begin_inset Quotes erd \end_inset kick \begin_inset Quotes erd \end_inset \family default parameter to the behaviour element definition). \layout Description say will say the arguments. \layout Description timer will say the first argument, then wait for a random time, and say the other arguments. The args have to be separated bu a ' \family typewriter % \family default ' char. \layout Section Interacting with the running bot, from irc \layout Standard You can use the \family typewriter reconf \family default and \family typewriter mute \family default actions (see section \begin_inset LatexCommand \ref{sub:Implemented-actions} \end_inset ) to control the bot from irc, and you define who can do that in the first configuration element, with the \family typewriter controler \family default property. \layout Chapter Conclusion \layout Standard You should now be abble to install, run and configure your manderlbot, and have it play with you on your prefered IRC channel. \layout Standard If you miss some action, please consider playing with the code (in erlang) or sending us some feature request, we may or may not implement your ideas! \layout Standard If you want to contribute, send a patch, and you may obtain a write access on the CVS (you may want to create a user account on tuxfamily.org services first). \layout Standard Enjoy manderlbot, enjoy free software! \the_end manderlbot-0.9.2/doc/Makefile0100644000175000000620000000077207773311177014662 0ustar acidstaff# $Id: Makefile,v 1.7 2003/12/27 14:09:03 acid_ Exp $ MANPAGE = manderlbot.1.sgml DOCUMENT = $(basename $(wildcard *.lyx)) all: man $(DOCUMENT).tex $(DOCUMENT).html $(DOCUMENT).ps $(DOCUMENT).pdf man: $(MANPAGE) docbook2man $< >/dev/null 2>&1 %.ps:%.lyx lyx -e ps $< %.pdf:%.lyx lyx -e pdf $< %.tex:%.lyx lyx -e latex $< %.html:%.tex hevea -fix url-fix.hva $< -rm *.htoc *.haux clean: -rm -f *~ -rm -f $(DOCUMENT).tex $(DOCUMENT).html $(DOCUMENT).ps $(DOCUMENT).pdf manderlbot.1 manpage.* manderlbot-0.9.2/doc/url-fix.hva0100644000175000000620000000357007773311071015300 0ustar acidstaff\usepackage{url} \input{urlhref.hva} % add css \let\oldmeta=\@meta \renewcommand{\@meta}{% \oldmeta \begin{rawhtml} \end{rawhtml}} \fi manderlbot-0.9.2/doc/manderlbot.1.sgml0100644000175000000620000000510307770630031016353 0ustar acidstaff
dim@tuxfamily.org
Dimitri Fontaine Décembre 2003 2003 Dimitri Fontaine
manderlbot 1 manderlbot An erlang IRC bot manderlbot configuration file log file log level description manderlbot is an irc bot aimed at saying idioties or doing some little searches on the internet. It is not an eggdrop bot. The default config file can be found in /etc/manderlbot/config.xml. It can includes some fortune files, wich you can provide in the same directory. options specifies the configuration file to use. specifies the log file to use. specifies the log_level from wich you want manderlbot to log in the log_file. You can specify one of the following entries : Bugs There should be none... :) Authors manderlbot was writen by Dimitri Fontaine dim@tuxfamily.org and Nicolas Niclausse nico@niclux.org.
manderlbot-0.9.2/LICENSE0100644000175000000620000004313107516272325013452 0ustar acidstaff GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. manderlbot-0.9.2/README0100644000175000000620000000145507720722067013330 0ustar acidstaffThis is manderlbot, an attempt to write an irc bot in erlang, with a XML based configuration. To use it, install erlang, then xmerl-0.15 http://www.erlang.org/user.html#xmerl-0.15 On my system this means install the compiled files in /usr/lib/erlang/lib/xmerl-0.15 make install will provide you /usr/bin/manderlbot to start the bot. If you have installed the debian package, you don't have to make install. Once manderlbot is running, you can obtain a shell on the erlang VM it is running in, by launching another VM and connecting to the former. erl -setcookie mdb -sname control (control@node)1> Then you hit ^G and connect to the manderlbot VM: User switch command --> r manderlbot@node --> c 3 If you want to stop manderlbot, you can now type in (manderlbot@node)1> application:stop(manderlbot). manderlbot-0.9.2/TODO0100644000175000000620000000110507756431251013131 0ustar acidstaff$Id: TODO,v 1.12 2003/11/18 14:54:33 dim Exp $ Behaviours Mood Add some mood elements in config, and provides mood switchers in the config. Then allow each behaviour to be choosen on a mood base. Prefix To prefix the commands to be given to manderlbot. To be done on a per channel base. Prefix example : '!' Serializer And we could have a behaviour:add() function to play with, and we could even add behaviours from irc ! Config DTD Behaviours lists, inheritance Add a fortune include, allowing to manage separately traditionnal fortune filesmanderlbot-0.9.2/conf/0040755000175000000620000000000010000342175013352 5ustar acidstaffmanderlbot-0.9.2/conf/config.xml0100644000175000000620000000377707770616634015404 0ustar acidstaff rejoin /etc/manderlbot/config.xml Hello world mute debian_pkg debian_file pyramid google wn jargon poh plic plic plic %R.T.F.M. %I do not like newbies %http://www.tuxedo.org/~esr/faqs/smart-questions.html %http://www.gnurou.org/documents/smart-questions-fr.html %http://michel.arboi.free.fr/humeur/nioubis.html %http://www.readthefuckingmanual.com /etc/manderlbot/bofh.fortune manderlbot-0.9.2/conf/bofh.fortune0100644000175000000620000004034307766333734015727 0ustar acidstaff% clock speed % solar flares % electromagnetic radiation from satellite debris % static from nylon underwear % static from plastic slide rules % global warming % poor power conditioning % static buildup % doppler effect % hardware stress fractures % magnetic interferance from money/credit cards % dry joints on cable plug % we're waiting for [the phone company] to fix that line % sounds like a Windows problem, try calling Microsoft support % temporary routing anomoly % somebody was calculating pi on the server % fat electrons in the lines % excess surge protection % floating point processor overflow % divide-by-zero error % POSIX complience problem % monitor resolution too high % improperly oriented keyboard % network packets travelling uphill (use a carrier pigeon) % Decreasing electron flux % first Saturday after first full moon in Winter % radiosity depletion % CPU radiator broken % It works the way the Wang did, what's the problem % positron router malfunction % cellular telephone interference % techtonic stress % pizeo-electric interference % (l)user error % working as designed % dynamic software linking table corrupted % heavy gravity fluctuation, move computer to floor rapidly % secretary plugged hairdryer into UPS % terrorist activities % not enough memory, go get system upgrade % interrupt configuration error % spaghetti cable cause packet failure % boss forgot system password % bank holiday - system operating credits not recharged % virus attack, luser responsible % waste water tank overflowed onto computer % Complete Transient Lockout % bad ether in the cables % Bogon emissions % Change in Earth's rotational speed % Cosmic ray particles crashed through the hard disk platter % Smell from unhygenic janitorial staff wrecked the tape heads % Evil dogs hypnotized the night shift % Plumber mistook routing panel for decorative wall fixture % Electricians made popcorn in the power supply % Groundskeepers stole the root password % high pressure system failure % failed trials, system needs redesigned % system has been recalled % not approved by the FCC % need to wrap system in aluminum foil to fix problem % not properly grounded, please bury computer % CPU needs recalibration % bit bucket overflow % descramble code needed from software company % only available on a need to know basis % knot in cables caused data stream to become twisted and kinked % nesting roaches shorted out the ether cable % The file system is full of it % Satan did it % Daemons did it % You're out of memory % There isn't any problem % Unoptimized hard drive % Typo in the code % Yes, yes, its called a desgin limitation % Look, buddy: Windows 3.1 IS A General Protection Fault. % Support staff hung over, send aspirin and come back LATER. % Someone is standing on the ethernet cable, causeing a kink in the cable % Windows 95 undocumented "feature" % Runt packets % Password is too complex to decrypt % Boss' kid fucked up the machine % Electromagnetic energy loss % Budget cuts % Mouse chewed through power cable % Stale file handle (next time use Tupperware(tm)!) % Feature not yet implimented % Internet outage % Pentium FDIV bug % Vendor no longer supports the product % Small animal kamikaze attack on power supplies % The vendor put the bug there. % SIMM crosstalk. % IRQ dropout % Collapsed Backbone % Power company testing new voltage spike (creation) equipment % operators on strike due to broken coffee machine % backup tape overwritten with copy of system manager's favourite CD % UPS interrupted the server's power % The electrician didn't know what the yellow cable was so he yanked the ethernet out. % The keyboard isn't plugged in % The air conditioning water supply pipe ruptured over the machine room % The electricity substation in the car park blew up. % The rolling stones concert down the road caused a brown out % The salesman drove over the CPU board. % Root nameservers are out of sync % electro-magnetic pulses from French above ground nuke testing. % your keyboard's space bar is generating spurious keycodes. % the real ttys became pseudo ttys and vice-versa. % the printer thinks its a router. % the router thinks its a printer. % we just switched to FDDI. % halon system went off and killed the operators. % user to computer ratio too high. % user to computer ration too low. % Sticky bits on disk. % Power Company having EMP problems with their reactor % The ring needs another token % SCSI Chain overterminated % because of network lag due to too many people playing deathmatch % Daemons loose in system. % BNC (brain not (user brain not connected) % UBNC (user brain not connected) % LBNC (luser brain not connected) % disks spinning backwards - toggle the hemisphere jumper. % new guy cross-connected phone lines with ac power bus. % had to use hammer to free stuck disk drive heads. % Too few computrons available. % Flat tire on station wagon with tapes. ("Never underestimate the bandwidth of a station wagon full of tapes hurling down the highway" Andrew S. Tanenbaum) % Communications satellite used by the military for star wars. % Party-bug in the Aloha protocol. % Insert coin for new game % Dew on the telephone lines. % Arcserve crashed the server again. % Some one needed the powerstrip, so they pulled the switch plug. % My pony-tail hit the on/off switch on the power strip. % Big to little endian conversion error % You can tune a file system, but you can't tune a fish (from most tunefs man pages) % Dumb terminal % Zombie processes haunting the computer % Incorrect time syncronization % Defunct processes % Stubborn processes % non-redundant fan failure % monitor VLF leakage % bugs in the RAID % no "any" key on keyboard % root rot % Backbone Scoliosis % mv /pub/lunch % excessive collisions and not enough packet ambulances % le0: no carrier: transceiver cable problem? % broadcast packets on wrong frequency % popper unable to process jumbo kernel % NOTICE: alloc: /dev/null: filesystem full % pseudo-user on a pseudo-terminal % Recursive traversal of loopback mount points % Backbone adjustment % OS swapped to disk % vapors from evaporating sticky-note adhesives % sticktion % short leg on process table % multicasts on broken packets % ether leak % Atilla the Hub % endothermal recalibration % filesystem not big enough for Jumbo Kernel Patch % loop found in loop in redundant loopback % system consumed all the paper for paging % permission denied % Reformatting Page. Wait... % SCSI's too wide. % Proprietary Information. % Just type 'mv * /dev/null'. % runaway cat on system. % We only support a 1200 bps connection. % Me no internet, only janitor, me just wax floors. % I'm sorry a pentium won't do, you need an SGI to connect with us. % Post-it Note Sludge leaked into the monitor. % the curls in your keyboard cord are losing electricity. % The monitor needs another box of pixels. % RPC_PMAP_FAILURE % kernel panic: write-only-memory (/dev/wom0) capacity exceeded. % Write-only-memory subsystem too slow for this machine. Contact your local dealer. % Quantum dynamics are affecting the transistors % Police are examining all internet packets in the search for a narco-net-traficer % We are currently trying a new concept of using a live mouse. Unfortuantely, one has yet to survive being hooked up to the computer.....please bear with us. % We didn't pay the Internet bill and it's been cut off. % Lightning strikes. % Of course it doesn't work. We've performed a software upgrade. % Change your language to Finnish. % Flourescent lights are generating negative ions. If turning them off doesn't work, take them out and put tin foil on the ends. % High nuclear activity in your area. % The MGs ran out of gas. % The UPS doesn't have a battery backup. % Recursivity. Call back if it happens again. % Someone thought The Big Red Button was a light switch. % The mainframe needs to rest. % Try calling the Internet's head office -- it's in the book. % The lines are all busy (busied out, that is -- why let them in to begin with?). % A star wars satellite accidently blew up the WAN. % Fatal error right in front of screen % wrong polarity of neutron flow % Lusers learning curve appears to be fractal % We had to turn off that service to comply with the CDA Bill. % Ionisation from the air-conditioning % TCP/IP UDP alarm threshold is set too low. % Someone is broadcasting pigmy packets and the router dosn't know how to deal with them. % The new frame relay network hasn't bedded down the software loop transmitter yet. % Fanout dropping voltage too much, try cutting some of those little traces % Plate voltage too low on demodulator tube % CPU needs bearings repacked % Too many little pins on CPU confusing it, bend back and forth until 10-20% are neatly removed. Do _not_ leave metal bits visible! % Software uses US measurements, but the OS is in metric... % The computer fletely, mouse and all. % Too much radiation coming from the soil. % Program load too heavy for processor to lift. % Processes running slowly due to weak power supply % Our ISP is having {switching,routing,SMDS,frame relay} problems % We've run out of licenses % Interference from lunar radiation % Standing room only on the bus. % You need to install an RTFM interface. % That would be because the software doesn't work. % That's easy to fix, but I can't be bothered. % Someone's tie is caught in the printer, and if anything else gets printed, he'll be in it too. % We're upgrading /dev/null % The Usenet news is out of date % Our POP server was kidnapped by a weasel. % It's stuck in the Web. % Your modem doesn't speak English. % The mouse escaped. % All of the packets are empty. % The UPS is on strike. % Neutrino overload on the nameserver % Melting hard drives % Someone has messed up the kernel pointers % The kernel license has expired % The cord jumped over and hit the power switch. % Bit rot % The Dilithium Cyrstals need to be rotated. % The static electricity routing is acting up... % Traceroute says that there is a routing problem in the backbone. It's not our problem. % The co-locator cannot verify the frame-relay gateway to the ISDN server. % Lawn mower blade in your fan need sharpening % Electrons on a bender % Telecommunications is upgrading. % Telecommunications is downgrading. % Telecommunications is downshifting. % Hard drive sleeping. Let it wake up on it's own... % Interference between the keyboard and the chair. % The CPU has shifted, and become decentralized. % Due to the CDA, we no longer have a root account. % We ran out of dial tone and we're and waiting for the phone company to deliver another bottle. % You must've hit the wrong anykey. % PCMCIA slave driver % The Token fell out of the ring. Call us when you find it. % The hardware bus needs a new token. % Too many interrupts % Not enough interrupts % The data on your hard drive is out of balance. % Digital Manipulator exceeding velocity parameters % appears to be a Slow/Narrow SCSI-0 Interface problem % microelectronic Riemannian curved-space fault in write-only file system % fractal radiation jamming the backbone % routing problems on the neural net % IRQ-problems with the Un-Interruptable-Power-Supply % CPU-angle has to be adjusted because of vibrations coming from the nearby road % emissions from GSM-phones % CD-ROM server needs recalibration % firewall needs cooling % asynchronous inode failure % transient bus protocol violation % incompatible bit-registration operators % your process is not ISO 9000 compliant % You need to upgrade your VESA local bus to a MasterCard local bus. % The recent proliferation of Nuclear Testing % Elves on strike. (Why do they call EMAG Elf Magic) % Internet exceeded Luser level, please wait until a luser logs off before attempting to log back on. % Your EMAIL is now being delivered by the USPS. % Your computer hasn't been returning all the bits it gets from the Internet. % You've been infected by the Telescoping Hubble virus. % Scheduled global CPU outage % processor has processed too many intructions. % packets were eaten by the terminator % The POP server is out of Coke % Fiber optics caused gas main leak % Server depressed, needs Prozak % quatnum decoherence % those damn racoons! % suboptimal routing experience % A plumber is needed, the network drain is clogged % 50% of the manual is in .pdf readme files % the AA battery in the wallclock sends magnetic interference % the xy axis in the trackball is coordinated with the summer soltice % the butane lighter causes the pincushioning % old inkjet cartridges emanate barium-based fumes % manager in the cable duct % HTTPD Error 666 : BOFH was here % HTTPD Error 4004 : very old Intel cpu - insufficient processing power % Network failure - call NBC % Having to manually track the satellite. % The rubber band broke % We're on Token Ring, and it looks like the token got loose. % Stray Alpha Particles from memory packaging caused Hard Memory Error on Server. % paradigm shift...without a clutch % PEBKAC (Problem Exists Between Keyboard And Chair) % The cables are not the same length. % Second-sytem effect. % Chewing gum on /dev/sd3c % Boredom in the Kernel. % struck by the Good Times virus % YOU HAVE AN I/O ERROR -> Incompetent Operator error % Your parity check is overdrawn and you're out of cache. % Plasma conduit breach % Out of cards on drive D: % Sand fleas eating the Internet cables % parallel processors running perpendicular today % ATM cell has no roaming feature turned on, notebooks can't connect % Webmasters kidnapped by evil cult. % Failure to adjust for daylight savings time. % Virus transmitted from computer to sysadmins. % Virus due to computers having unsafe sex. % Incorrectly configured static routes on the corerouters. % Forced to support NT servers; sysadmins quit. % Suspicious pointer corrupted virtual machine % Its the InterNIC's fault. % Root name servers corrupted. % Budget cuts forced us to sell all the power cords for the servers. % Someone hooked the twisted pair wires into the answering machine. % Operators killed by year 2000 bug bite. % We've picked COBOL as the language of choice. % Operators killed when huge stack of backup tapes fell over. % Robotic tape changer mistook operator's tie for a backup tape. % t's an ID-10-T error % Dyslexics retyping hosts file on servers % The Internet is being scanned for viruses. % Your computer's union contract is set to expire at midnight. % Bad user karma. % /dev/clue was linked to /dev/null % Increased sunspot activity. % It's union rules. There's nothing we can do about it. Sorry. % Interferance from the Van Allen Belt. % Jupiter is aligned with Mars. % Redundant ACLs. % Mail server hit by UniSpammer. % T-1's congested due to porn traffic to the news server. % Data for intranet got routed through the extranet and landed on the internet. % What you are experiencing is not a problem; it is an undocumented feature. % Secretary sent chain letter to all 5000 employees. % Sysadmin accidentally destroyed pager with a large hammer. % Bad cafeteria food landed all the sysadmins in the hospital. % Route flapping at the NAP. % Computers under water due to SYN flooding. % The vulcan-death-grip ping has been applied. % Electrical conduits in machine room are melting. % Traffic jam on the Information Superhighway. % Radial Telemetry Infiltration % Cow-tippers tipped a cow onto the server. % tachyon emissions overloading the system % Maintence window broken % Computer room being moved. Our systems are down for the weekend. % Sysadmins busy fighting SPAM. % Repeated reboots of the system failed to solve problem % Domain controler not responding % Someone stole your IP address, call the Internet detectives. % operation failed because: there is no message for this error (#1014) % stop bit received % internet is needed to catch the etherbunny % network down, IP packets delivered via UPS % Firmware update in the coffee machine % Mouse has out-of-cheese-error % Borg implants are failing % Borg nanites have infested the server % error: one bad user found in front of screen % Please state the nature of the technical emergency % Internet shut down due to maintainance % Daemon escaped from pentagram % crop circles in the corn shell % sticky bit has come loose % Hot Java has gone cold % Cache miss - please take better aim next time % Hash table has woodworm % Trojan horse ran out of hay % Zombie processess detected, machine is haunted. % overflow error in /dev/null % Browser's cookie is corrupted - someone's been nibbling on it. % Mailer-daemon is busy burning your message in hell. % vi needs to be upgraded to vii manderlbot-0.9.2/Makefile0100644000175000000620000000644010000342005014056 0ustar acidstaff# Build the .beam erlang VM files OPT = -W INC = ./inc CC = erlc ERL = erl SED = $(shell which sed) ESRC = ./src EBIN = ./ebin RAW_INSTALL_DIR = /usr/lib/erlang/ ERLANG_INSTALL_DIR = $(DESTDIR)/$(RAW_INSTALL_DIR)/lib APPLICATION = manderlbot VERSION = 0.9.2 TARGETDIR = $(ERLANG_INSTALL_DIR)/$(APPLICATION)-$(VERSION) BINDIR = $(DESTDIR)/usr/bin CONFDIR = $(DESTDIR)/etc/manderlbot LOGFILE = $(DESTDIR)/var/log/manderlbot.log TMP = $(wildcard *~) $(wildcard src/*~) $(wildcard inc/*~) INC_FILES = $(wildcard $(INC)/*.hrl) SRC = $(wildcard $(ESRC)/*.erl) CONFFILES = conf/config.xml $(wildcard conf/*fortune) TARGET = $(addsuffix .beam, $(basename \ $(addprefix $(EBIN)/, $(notdir $(SRC))))) EMAKE = $(addsuffix \'., $(addprefix \'../, $(SRC))) SRC_APPFILES = $(ESRC)/$(APPLICATION).app.src $(ESRC)/$(APPLICATION).rel.src TGT_APPFILES_E = $(EBIN)/$(APPLICATION).app TGT_APPFILES_P = priv/$(APPLICATION)* SCRIPT = $(BINDIR)/manderlbot BUILD_OPTIONS = '[{systools, [{variables,[{"ROOT","$(RAW_INSTALL_DIR)"}]}]}, \ {sh_script, none}, {report, verbose}, {app_vsn, "$(VERSION)"}, \ {make_app, true }, {make_rel, true}].' BUILD_OPTIONS_FILE = ./BUILD_OPTIONS .PHONY: doc manderlbot: $(TARGET) all: clean manderlbot # used to generate the erlang Emakefile emake: @echo $(EMAKE) | tr -s ' ' '\n' > ebin/Emakefile clean: -cd priv && rm -f $(shell ls priv | grep -v builder\.erl) && cd .. -rm -f $(TARGET) $(TMP) $(BUILD_OPTIONS_FILE) builder.beam -rm -f $(TGT_APPFILES) -rm -f ebin/* -rm -f manderlbot.sh -make -C doc clean install: doc build manderlbot.sh -rm -f $(TMP) install -d $(TARGETDIR)/priv install -d $(TARGETDIR)/ebin install -d $(TARGETDIR)/src install -d $(TARGETDIR)/include cp $(INC_FILES) $(TARGETDIR)/include cp $(TARGET) $(TARGETDIR)/ebin cp $(TGT_APPFILES_E) $(TARGETDIR)/ebin cp $(TGT_APPFILES_P) $(TARGETDIR)/priv cp $(SRC) $(SRC_APPFILES) $(TARGETDIR)/src # install the man page install -d $(DESTDIR)/usr/share/man/man1 install doc/manderlbot.1 $(DESTDIR)/usr/share/man/man1 # create startup script cp manderlbot.sh $(SCRIPT) chmod +x $(SCRIPT) # added for debian mkdir -p $(CONFDIR) cp $(CONFFILES) $(CONFDIR) uninstall: rm -rf $(TARGETDIR) $(SCRIPT) build: manderlbot builder.beam $(SRC_APPFILES) # use builder to make boot file mkdir -p temp ln -sf `pwd` temp/$(APPLICATION)-$(VERSION) (cd temp/$(APPLICATION)-$(VERSION) \ && echo $(BUILD_OPTIONS) > $(BUILD_OPTIONS_FILE) \ && erl -s builder go -s init stop \ ) rm -rf temp doc: make -C doc release: rm -fr $(APPLICATION)-$(VERSION) mkdir -p $(APPLICATION)-$(VERSION) tar zcf tmp.tgz $(SRC) $(SRC_APPFILES) $(INC_FILES) \ doc/*.lyx doc/Makefile doc/*.hva doc/*sgml \ LICENSE README TODO $(CONFFILES) Makefile \ priv/builder.erl manderlbot.sh.in bofh.fortune tar -C $(APPLICATION)-$(VERSION) -zxf tmp.tgz mkdir $(APPLICATION)-$(VERSION)/ebin tar zvcf $(APPLICATION)-$(VERSION).tar.gz $(APPLICATION)-$(VERSION) rm -fr $(APPLICATION)-$(VERSION) rm -fr tmp.tgz builder.beam: priv/builder.erl $(CC) $(OPT) -I $(INC) $< ebin/%.beam: src/%.erl $(CC) $(OPT) -I $(INC) -o ebin $< manderlbot.sh: manderlbot.sh.in Makefile @$(SED) \ -e 's;%INSTALL_DIR%;${RAW_INSTALL_DIR};g' \ -e 's;%VERSION%;${VERSION};g' < $< > $@ %:%.sh # Override makefile default implicit rule manderlbot-0.9.2/priv/0040755000175000000620000000000010000342175013405 5ustar acidstaffmanderlbot-0.9.2/priv/builder.erl0100644000175000000620000013213207720702434015553 0ustar acidstaff%%%---------------------------------------------------------------------- %%% File : builder.erl %%% Author : Mats Cronqvist %%% : Ulf Wiger %%% Purpose : Simplify build of OTP applications & boot scripts %%% Created : 2 Jan 2001 by Mats Cronqvist %%%---------------------------------------------------------------------- %%% @doc OTP release script builder. %%% %%%

This program compiles .rel, .script, .boot and sys.config files %%% for erlang applications. It supports incremental and recursive builds, %%% and is intended to be (much) easier and safer to use than doing it all %%% manually. This program does not generate beam code from Erlang source %%% files.

%%% %%%

The program makes some assumptions:

%%%
  • The application is store in a directory whose name is %%% composed with the name of the application '-' the version %%% number of the application (i.e. myapp-1.0)
  • %%%
  • The release and app file used in input are simplified subset %%% of the final Erlang release and app file. Some differences: %%% Versions are handled automatically; You do not need to add %%% the erts version tuple in the rel.src file; the application, %%% currently being built is mandatory in the list of apps in the %%% rel.src file; etc.
  • %%%
%%% %%%

The program (henceforth called 'builder') can be customized %%% using a number of options. The options are prioritized in %%% the following order: (1) those given as arguments to the %%% builder:go/1 function, those in the BUILD_OPTIONS %%% file, and (3) the hard-coded defaults.

%%% %%%

Valid options are

%%%
%%%
{app_dir, dirname()}
%%%
The application directory of the application being built. %%% The default value is Current Working Directory.
%%% %%%
{build_options, filename()}
%%%
A filename pointing to a BUILD_OPTIONS file. This is a file %%% containing erlang expressions, each terminated by "." -- just %%% like a file processed using file:eval/1. The last expression %%% in the file should result in a list of %%% {Key,Value} options. %%% If this option is not given, builder will look in [AppDir] and %%% [AppDir]/src for a file called BUILD_OPTIONS.
%%% Pre-bound variables are: %%%
    %%%
  • ScriptName : filename(), %%% the name of the script file.
  • %%%
%%%
{report, Level : atom()}
%%%
Specifies the reporting level. The following levels are recognized: %%% none, progress, verbose, debug.
%%% %%%
{out_dir, dirname()}
%%%
Specifies where [AppName].script, [AppName].boot, and sys.config %%% should be written. Default is [AppDir]/priv.
%%% %%%
{sys_config, filename()}
%%%
Specifies the location of the sys.config file. Default is %%% [OutDir]/sys.config.
%%% %%%
{rel_file, filename()}
%%%
Specifies which .rel file should be used as input for the %%% build process. Default is %%% [AppDir]/src/[AppName].rel.src.
%%% %%%
{rel_name, string()}
%%%
This option can be used if the release should be called something %%% other than [AppName] or whatever is in the %%% .rel file.
%%% %%%
{app_vsn, string()}
%%%
This option can be used to assign a version to the current %%% application. If the application directory has a version suffix, %%% the version suffix will override this option. If the directory %%% has no suffix, the default vsn will be "BLDR", unless 'app_vsn' %%% has been specified.
%%% %%%
{apps, [App : AppName | {AppName,Vsn} | {AppName,Vsn,EbinDir}]}
%%%
This is a way to identify which applications should be included %%% in the build. The way builder determines which applications should %%% be included is this:
    %%%
  • If there is a .rel.src file, the applications %%% listed in this file are included first, in the order in which %%% they are listed.
  • %%%
  • After this, all applications listed in the 'applications' %%% attribute of the .app.src file are added (if not %%% already listed), also in the order in which they are listed.
  • %%%
  • Then, all remaining applications in the 'apps' list are added %%% in order.
  • %%%
  • Finally, any included applications listed in the %%% .rel.src file, not already listed, are added. %%% (note that applications listed in 'included_applications' of %%% the .app.src file are not included here. The %%% .rel file can be used to override this definition, %%% so it would be an error to include it at this point.)
  • %%%
%%%
%%% %%%
{path, [PathExpr]}
%%%
Specifies the search path when locating applications. Default is %%% code:get_path(). PathExpr can contain wildcards, e.g. %%% "/OTP/lib/kernel*/ebin" (basically anything that %%% regexp:sh_to_awk/1 understands.)
%%% %%%
{skip, [ModuleName : atom()]}
%%%
Lists modules that should not be included in the generated .app %%% file for the current application. If not specified, builder will %%% locate all modules under [AppDir]/src, extract %%% their module names, and include these module names in the %%% 'modules' list of the .app file.
%%% %%%
{make_app, true | false | auto}
%%%
If true, builder will generate an .app file from an .app.src %%% file; if false, it will try to use an existing .app file; if %%% auto, it will build an .app file if no .app file exists %%% Default is 'auto'.
%%% %%%
{make_rel, true | false}
%%%
If true, builder will generate a .rel file from a .rel.src file; %%% if false, it will assume that there is a working .rel file. %%% Default: false if the rel_file option has been specified; true %%% otherwise.
%%% %%%
{make_boot, true | false}
%%%
If true, builder will run systools:make_script() %%% to generate .script and .boot files in [OutDir]. If false, it %%% will assume that there is a working .boot file. Default is true.
%%%
%%% %%%
{systools, Options : [Opt]}
%%%
If specified, Options will be passed to the %%% systools:make_script() command for building the %%% .boot script. Note that the 'path' option %%% is generated by builder. It cannot be overridden. %%% See erl -man systools for available options.
%%% %%%
{sh_script, auto | none}
%%%
If 'auto', builder will generate a small sh script %%% that makes it easier to start the system; If 'none', %%% no script will be generated. Default is 'none' on %%% non-UNIX systems and 'auto' on UNIX systems.
%%% %%%
{erl_opts, Opts : string()}
%%%
If specified, and if a sh_script is generated (see above), %%% Opts will be appended to the erl %%% command line. The builder will automatically put in options to %%% identify the boot script, sys.config, and possible boot variables.
%%% %%%
{config, Config : {file, filename()} | {M,F,A} | Data}
%%%
If present, this option should either point to a file that %%% contains a subset of data for a sys.config file (same format as %%% sys.config), or give a {M,F,A} tuple, where %%% apply(M,F,A) results in a list, %%% [{AppName, [{Key, Value}]}], or finally just specify %%% the data in place. The builder will look for similar config %%% directives in any BUILD_OPTIONS files of other applications that %%% are part of the build, and all inputs will be merged %%% into a common sys.config.
%%% %%%
{{config,OtherApp}, Config}
%%%
This is a way to specify environment variables for another %%% application in the build options. Config in this case %%% is the same as Config above. The difference between %%% this option and specifying environment variables for the other %%% application using the above option, is that with a simple %%% {config, ...} directive, builder will also check %%% for a similar directive in the other application, and all %%% different inputs will be merged. Using a {{config,App},...} %%% option, builder will not look into that application further.
%%% @end %%% ===================================================================== -module(builder). -author('mats.cronquist@etx.ericsson.se'). -author('ulf.wiger@ericsson.com'). -export([go/0, go/1, find_app/2]). -export([early_debug_on/0]). -compile(export_all). -include_lib("kernel/include/file.hrl"). -import(dict, [fetch/2]). -define(report(Level, Format, Args), case do_report(Level) of true -> io:format("[~p:~p] " ++ Format, [?MODULE,?LINE|Args]); false -> ok end). -define(f(D, Expr), fun(D) -> Expr end). -define(herewith(D), fun(D) -> ?report(debug, "herewith~n", []), D end). early_debug_on() -> put(builder_debug, true). go() -> go([]). go(Options) -> %% We use a dictionary for the options. The options are prioritized in %% the following order: (1) those given as arguments to the go/1 function, %% those in the BUILD_OPTIONS file, and (3) the hard-coded defaults. %% The options stored last in the dictionary take precedence, but since %% we want to allow for the Options list to guide us in finding the %% BUILD_OPTIONS file (under certain circumstances), we store the defaults %% and Options list in the dictionary, then locate and read the file, then %% store the Options list again. Dict0 = mk_dict(default_options() ++ Options), Dict = with(Dict0, [fun(D) -> read_options_file(D) end, ?herewith(D), % to help debugging fun(D) -> store_options(Options, D) end, ?herewith(D), fun(D) -> post_process_options(D) end, ?herewith(D), fun(D) -> {Path, [App, Vsn]} = get_app(D), store_options([{app_name, list_to_atom(App)}, {app_vsn, Vsn}], D) end, ?herewith(D), fun(D) -> [Descr, Id, Reg, Apps, Env, Mod, Phases] = read_app_file(D), Vsn = fetch(app_vsn, D), store_options([{app, [{vsn,Vsn}, Descr, Id, Reg, Apps, Env, Mod, Phases]}], D) end, ?herewith(D), fun(D) -> case dict:find(rel_name, D) of {ok,_} -> D; error -> RelName = atom_to_list( fetch(app_name, D)), dict:store(rel_name, RelName, D) end end, ?herewith(D), fun(D) -> RelFname = get_rel_filename(D), case file:consult(RelFname) of {ok, [{release,{_Name,_RelVsn}, _RelApps}= Rel]} -> ?report(debug,"rel_src = ~p~n",[Rel]), dict:store(rel_src,Rel, D); _ -> D end end, ?herewith(D), fun(D) -> AppInfo = find_apps(D), ?report(debug, "find_apps(D) -> ~p~n", [AppInfo]), dict:store(app_info, AppInfo, D) end, ?herewith(D), fun(D) -> BootVars = boot_vars(D), dict:store(boot_vars, BootVars, D) end]), case lists:member({ok,true}, [dict:find(make_boot,Dict), dict:find(make_rel,Dict), dict:find(make_config, Dict)]) of true -> verify_dir(fetch(out_dir,Dict)); false -> ok end, Dict1 = make_rel(Dict), make_app(Dict1), Res = make_boot(Dict1), make_config(Dict1), make_sh_script(Dict1), cleanup(Dict1), Res. find_app(App, Path) -> {Found,_} = expand_path(Path, [App], [], []), Found. post_process_options(Dict) -> D = with(Dict, [fun(D) -> case dict:find(out_dir, D) of {ok,_} -> D; error -> dict:store(out_dir, out_dir(D), D) end end, fun(D) -> case dict:find(rel_file, D) of {ok,_} -> dict:store(make_rel, false); error -> dict:store(make_rel, true, D) end end, fun(D) -> case dict:find(config, D) of {ok,_} -> dict:store(make_config, true, D); _ -> D end end, fun(D) -> case dict:find(make_boot, D) of {ok, _} -> D; error -> dict:store(make_boot, true, D) end end, fun(D) -> case dict:find(make_app, D) of {ok,_} -> D; error -> dict:store(make_app, auto, D) end end, fun(D) -> case dict:find(sh_script, D) of {ok, _} -> D; error -> case os:type() of {unix,_} -> dict:store(sh_script,auto,D); _ -> dict:store(sh_script,none,D) end end end, fun(D) -> case dict:find(systools, D) of {ok, _} -> D; error -> dict:store(systools, [], D) end end]), ?report(debug, "Options = ~p~n", [dict:to_list(D)]), D. with(Dict, Actions) -> lists:foldl(fun(F,D) -> F(D) end, Dict, Actions). cleanup(Dict) -> pop_report_level(). default_options() -> [{app_dir, cwd()}, {skip, []}, {path, code:get_path()}]. read_options_file(Dict) -> case dict:find(build_options, Dict) of {ok, BuildOptsF} -> case script(BuildOptsF) of {error, empty_script} -> %% We accept this Dict; {ok, Terms} -> ?report(debug, "Stored terms (~s) =~n ~p~n", [BuildOptsF, Terms]), store_options(Terms, Dict); {error, Reason} -> exit({bad_options_file, {BuildOptsF, Reason}}) end; error -> Path = case dict:find(app_dir, Dict) of {ok, AppDir} -> [AppDir, filename:join(AppDir, "src")]; error -> [".", "src"] end, case path_script(Path, "BUILD_OPTIONS") of {ok, Terms, Fullname} -> Dict1 = store_options(Terms, Dict), ?report(debug, "Stored terms (~s) =~n ~p~n", [Fullname, Terms]), Dict1; {error, enoent} -> Dict; Error -> exit({build_options, Error}) end end. mk_dict(Options) -> %% pust a unique ref onto the dictionary. This will allow us to %% redefine e.g. the report_level (for the same instance of %% the dictionary, and stack the same (for new instances of the %% dictionary -- recursive build.) store_options([{'#ref#', make_ref()}|Options], dict:new()). store_options(Options, Dict) -> ?report(debug, "store_options(~p)~n", [Options]), lists:foldl( fun({report, Level}, D) -> push_report_level(Level, D), dict:store(report, Level, D); ({Key,Value}, D) -> dict:store(Key,Value,D) end, Dict, Options). make_rel(Dict) -> case dict:find(make_rel, Dict) of {ok,true} -> ?report(debug, "will make rel~n", []), App = atom_to_list(fetch(app_name, Dict)), Vsn = fetch_key(vsn, fetch(app, Dict)), Apps = fetch(app_info, Dict), AppInfo = [{A,V} || {A,V,F} <- Apps], ?report(debug,"AppInfo = ~p~n", [AppInfo]), {RelName, Rel} = case dict:find(rel_src, Dict) of %% mremond: Added this case clause: %% Without it you get an error if you define %% the erts version in your release file subset. %% The ERTS version is anyway replace by the %% system ERTS {ok, {release,{Name,RelVsn}, {erts, ErtsVsn}, RelApps}} -> ?report(debug,"found rel_src: RelApps=~p~n", [RelApps]), {Name, {release, {Name,RelVsn}, {erts, get_erts_vsn()}, AppInfo}}; {ok, {release,{Name,RelVsn},RelApps}} -> %% here we should check that Apps is a subset of %% RelApps; if so, use RelApps; otherwise exit ?report(debug,"found rel_src: RelApps=~p~n", [RelApps]), {Name, {release, {Name,RelVsn}, {erts, get_erts_vsn()}, AppInfo}}; error -> {App, {release, {App, Vsn}, {erts, get_erts_vsn()}, AppInfo}} end, RelFileTarget = filename:join(fetch(out_dir,Dict), RelName ++ ".rel"), out(RelFileTarget, Rel), store_options([{rel_name, RelName}, {rel_file, RelFileTarget}], Dict); _ -> ?report(debug, "will NOT make rel~n", []), Dict end. make_config(Dict) -> AppInfo = fetch(app_info, Dict), AppName = fetch(app_name, Dict), ForeignData = lists:foldl( fun({A,V,Ebin}, Acc) when A =/= AppName -> Acc ++ try_get_config(A,Ebin,Dict); (_, Acc) -> Acc end, [], AppInfo), Data = case dict:find(config, Dict) of {ok, Type} -> get_config_data(Type); error -> [] end, merge_config(Data ++ ForeignData, Dict). try_get_config(AppName, Ebin, Dict) -> case dict:find({config, AppName}, Dict) of {ok, Type} -> get_config_data(Type); error -> BuildOpts = filename:join( filename:join(filename:dirname(Ebin), "src"), "BUILD_OPTIONS"), case script(BuildOpts) of {error, enoent} -> []; {ok, Result} -> ?report(debug, "script(~p) ->~n ~p~n", [BuildOpts, Result]), case lists:keysearch(config, 1, Result) of {value, {_, Type}} -> get_config_data(Type); false -> [] end end end. get_config_data({file,F}) -> ?report(debug, "get_config_data({file, ~p})~n", [F]), case file:consult(F) of {ok, [Terms]} when list(Terms) -> Terms; {error, Reason} -> exit({config, {F, Reason}}) end; get_config_data({M,F,A}) -> ?report(debug, "get_config_data(~p)~n", [{M,F,A}]), apply(M,F,A); get_config_data(Data) when list(Data) -> Data. merge_config([], Dict) -> ok; merge_config(Data, Dict) -> ConfigF = sys_config(Dict), case file:consult(ConfigF) of {error, enoent} -> out(ConfigF, Data); {ok, [Terms]} -> ?report(debug, "merging terms (~p,~p), F = ~p~n", [Data,Terms, ConfigF]), NewData = merge_terms(Data, Terms), out(ConfigF, NewData) end. merge_terms([{App,Vars}|Data], Old) -> case lists:keysearch(App, 1, Old) of false -> merge_terms(Data, Old ++ [{App,Vars}]); {value, {_, OldVars}} -> ?report(debug, "merging vars (~p,~p) for ~p~n", [Vars,OldVars, App]), NewVars = merge_vars(Vars, OldVars, App), merge_terms(Data, lists:keyreplace(App,1,Old,{App,NewVars})) end; merge_terms([], Data) -> Data. merge_vars([{Key,Vals}|Data], Old, App) -> case lists:keysearch(Key, 1, Old) of {value, {_, OldVals}} when OldVals =/= Vals -> exit({config_conflict, {App, Key}}); {value, {_, OldVals}} when OldVals == Vals -> merge_vars(Data, Old, App); false -> merge_vars(Data, Old ++ [{Key,Vals}], App) end; merge_vars([], Data, _App) -> Data. fetch_key(Key, L) -> {value, {_, Value}} = lists:keysearch(Key, 1, L), Value. make_app(Dict) -> case dict:find(make_app, Dict) of {ok,true} -> do_make_app(Dict); {ok,auto} -> AppName = fetch(app_name, Dict), AppF = filename:join(ebin_dir(Dict), atom_to_list(AppName) ++ ".app"), case file:read_file_info(AppF) of {ok, _} -> ok; {error, enoent} -> do_make_app(AppF, AppName, Dict) end; _ -> ok end. do_make_app(Dict) -> AppName = fetch(app_name, Dict), AppF = filename:join(ebin_dir(Dict), atom_to_list(AppName) ++ ".app"), do_make_app(AppF, AppName, Dict). do_make_app(AppF, AppName, Dict) -> [Vsn, Descr, Id, Reg, Apps, Env, Mod, Phases] = fetch(app, Dict), Modules = modules(Dict), Remove = case {Mod, Phases} of {{_,[]},{_,undefined}} -> [Mod, Phases]; {{_,{_,_}},{_,undefined}} -> [Phases]; {{_,[]}, {_,_}} -> [Mod]; _ -> [] end, Options = [Vsn, Descr, Id, Modules, Reg, Apps, Env, Mod, Phases] -- Remove, out(AppF, {application, AppName, Options}). make_boot(Dict) -> case dict:find(make_boot, Dict) of {ok,true} -> ensure_rel_file(Dict), App = fetch(app_name, Dict), CWD = cwd(), ok = file:set_cwd(fetch(out_dir,Dict)), SystoolsOpts0 = merge_opts([{path, systools_path(Dict)}], fetch(systools, Dict)), SystoolsOpts = maybe_local(SystoolsOpts0, Dict), ?report(debug, "SystoolsOpts = ~p~n", [SystoolsOpts]), RelName = fetch(rel_name, Dict), Res = systools:make_script(RelName, SystoolsOpts), ?report(progress, "systools:make_script() -> ~p~n", [Res]), make_load_script(RelName), ok = file:set_cwd(CWD), case Res of error -> exit(error_in_make_script); ok -> ok end; _ -> ok end. maybe_local(Opts, Dict) -> case lists:keymember(variables, 1, Opts) of true -> Opts; false -> [local|Opts -- [local]] end. make_load_script(RelName) -> {ok, Bin} = file:read_file(RelName ++ ".boot"), {script,Id,Cmds} = binary_to_term(Bin), Keep = lists:filter( fun({apply,{application,start_boot,[kernel,Type]}}) -> true; ({apply,{application,start_boot,[stdlib,Type]}}) -> true; ({apply,{application,start_boot,[sasl,Type]}}) -> true; ({apply,{application,start_boot,[_,Type]}}) -> false; (_) -> true end, Cmds), NewScript = {script,Id,Keep}, file:write_file(RelName ++ "_load.boot", term_to_binary(NewScript)), out(RelName ++ "_load.script", NewScript). boot_vars(Dict) -> case dict:find(systools, Dict) of {ok, Opts} when Opts =/= [] -> ?report(debug, "systools opts = ~p~n", [Opts]), case lists:keysearch(variables, 1, Opts) of {value, {_, Vars}} -> Vars; false -> [] end; _ -> ?report(debug,"will make boot_vars~n", []), {ok,[[Root]]} = init:get_argument(root), Apps = dict:fetch(app_info, Dict), prefixes(Apps, [{"ROOT",Root}]) end. prefixes([{A,_,D}|Apps], Prefixes) -> case [P || {_,P} <- Prefixes, lists:prefix(P, D)] of [] -> prefixes(Apps, guess_root(D, Prefixes)); [_|_] -> prefixes(Apps, Prefixes) end; prefixes([], Prefixes) -> Prefixes. guess_root(D, Pfxs) -> case lists:reverse(filename:split(D)) of ["ebin",App,"lib",Dir|T] -> guess(to_upper(Dir), 0, filename:join(lists:reverse([Dir|T])), Pfxs); ["ebin",App,Dir|T] -> guess(to_upper(Dir), 0, filename:join(lists:reverse([Dir|T])), Pfxs) end. guess(Guess,N,RootDir, Pfxs) -> case lists:keymember(Guess,1,Pfxs) of false -> ?report(debug, "manufactured boot_var {~p,~p}~n", [Guess,RootDir]), [{Guess,RootDir}|Pfxs]; true -> ?report(debug, "duplicate Guess = ~p~n", [Guess]), guess(increment(Guess,N), N+1, RootDir, Pfxs) end. increment(Guess,0) -> Guess ++ "-1"; increment(Guess,N) when N < 10 -> [_,$-|T] = lists:reverse(Guess), lists:reverse(T) ++ [$-|integer_to_list(N+1)]. to_upper([H|T]) when H >= $a, H =< $z -> [$A+(H-$a)|to_upper(T)]; to_upper([H|T]) -> [H|T]; to_upper([]) -> []. make_sh_script(Dict) -> case dict:find(sh_script, Dict) of {ok, auto} -> OutDir = fetch(out_dir, Dict), RelName = fetch(rel_name, Dict), StartF = filename:join(OutDir, RelName ++ ".start"), LoadF = filename:join(OutDir, RelName ++ ".load"), do_make_sh_script(StartF,start,Dict), do_make_sh_script(LoadF,load,Dict); _ -> ok end. do_make_sh_script(ScriptF, Type, Dict) -> ok = file:write_file(ScriptF, list_to_binary(sh_script(Type,Dict))), {ok,FI} = file:read_file_info(ScriptF), Mode = FI#file_info.mode bor 8#00100, ok = file:write_file_info(ScriptF, FI#file_info{mode = Mode}). sh_script(Mode,Dict) -> OutDir = fetch(out_dir, Dict), AppDir = fetch(app_dir, Dict), XOpts = case dict:find(erl_opts, Dict) of {ok, Opts} -> [" ", Opts]; error -> "" end, %% mremond: The path it need to be sure the script is predictible % Path = [" -pa ", filename:join(AppDir, "ebin")], RelName = fetch(rel_name, Dict), Boot = case Mode of start -> [" -boot ", filename:join(OutDir, RelName)]; load -> [" -boot ", filename:join(OutDir, RelName++"_load")] end, Sys = case dict:find(make_config, Dict) of {ok, true} -> [" -config ", filename:join(OutDir, "sys")]; _ -> [] end, BootVars = [[" -boot_var ", Var, " ", Dir] || {Var,Dir} <- dict:fetch(boot_vars, Dict)], % ["#/bin/sh\n" % "erl ", Path, Boot, Sys, BootVars, XOpts, "\n"]. ["#/bin/sh\n" "erl ", Boot, Sys, BootVars, XOpts, "\n"]. ensure_rel_file(Dict) -> OutDir = fetch(out_dir, Dict), case dict:find(rel_file, Dict) of {ok, F} -> case filename:dirname(F) of D when D == OutDir -> ok; _ -> %% This doesn't necessarily mean that it's not there RelName = fetch(rel_name, Dict), {ok, [{release, {N,Vsn}, Erts, Apps}]} = file:consult(F), RelF = filename:join(OutDir, fetch(rel_name,Dict) ++ ".rel"), out(RelF, {release, {RelName,Vsn}, Erts, Apps}) end; error -> exit(no_rel_file) end. systools_path(Dict) -> AppInfo = fetch(app_info, Dict), [D || {_A,_V,D} <- AppInfo]. merge_opts(Opts, [{path,P}|Opts2]) -> exit({not_allowed, {systools, {path,P}}}); merge_opts(Opts, [Opt|Opts2]) -> [Opt|merge_opts(Opts, Opts2)]; merge_opts(Opts, []) -> Opts. cwd() -> {ok, CWD} = file:get_cwd(), CWD. out_dir(Dict) -> case dict:find(out_dir, Dict) of {ok, OutDir} -> OutDir; error -> filename:join(fetch(app_dir,Dict), "priv") end. ebin_dir(Dict) -> AppDir = fetch(app_dir, Dict), filename:join(AppDir, "ebin"). sys_config(Dict) -> case dict:find(sys_config, Dict) of {ok, Filename} -> Filename; error -> filename:join(fetch(out_dir,Dict), "sys.config") end. get_app(Dict) -> AppDir = fetch(app_dir, Dict), %% mremond: The program was crashing when the directory where not %% containing the application version %% Now we assume that version number is "BLDR" by default. case string:tokens(hd(lists:reverse(string:tokens(AppDir, "/"))), "-") of [App, Vsn] -> {AppDir, [App, Vsn]}; [App] -> case dict:find(app_vsn, Dict) of {ok, Vsn} -> {AppDir, [App, Vsn]}; error -> {AppDir, [App, "BLDR"]} end end. get_app_filename(App, Dict) -> AppDir = fetch(app_dir, Dict), filename:join([AppDir, "src", App++".app.src"]). get_rel_filename(Dict) -> App = atom_to_list(dict:fetch(app_name, Dict)), AppDir = fetch(app_dir, Dict), filename:join([AppDir, "src", App++".rel.src"]). modules(Dict) -> Ebin = ebin_dir(Dict), Ext = code:objfile_extension(), L = recursive_list_dir(Ebin), ?report(debug, "L = ~p~n", [L]), Skip = fetch(skip, Dict), ?report(debug, "Skip = ~p~n", [Skip]), Modules = lists:foldr( fun(F, Acc) -> case make_mod(F) of {ok, M} -> [M|Acc]; error -> Acc end end, [], [F || F <- L, check_f(F, Ext)]), {modules, Modules -- Skip}. recursive_list_dir(Dir) -> {ok, Fs} = file:list_dir(Dir), lists:concat([f_expand(F,Dir) || F <- Fs]). f_expand(Name,Dir) -> Fname = filename:join(Dir,Name), case file:read_file_info(Fname) of {ok, #file_info{type=regular}} -> [Fname]; {ok, #file_info{type=directory}} -> ?report(debug, "~p is a directory under ~p~n", [Fname, Dir]), {ok, Fs} = file:list_dir(Fname), lists:concat([f_expand(F,Fname) || F <- Fs]); {ok, Finfo} -> ?report(debug, "~p not a directory or a regular file. Ignoring~n", [Fname]), []; {error, Reason} -> ?report(error, "*** read_file_info(~p) -> {error, ~p}~n", [Fname, Reason]), [] end. make_mod(F) -> case beam_lib:version(F) of {ok, {Module, Vsn}} -> {ok, Module}; _ -> error end. % {ok,Fd} = file:open(F, [read]), % parse_for_mod(Fd, F). % {ok,Bin} = file:read_file(F), % Text = binary_to_list(Bin), % {match,Start,Len} = % regexp:first_match(Text, % "^[ ]*-[ ]*module[ ]*[(][ ]*.*[ ]*[)][ ]*\."), % Name = case string:substr(Text, Start+8,Len-10) of % "'" ++ Quoted -> % "'" ++ Rest = lists:reverse(Quoted), % list_to_atom(lists:reverse(Rest)); % Str -> % list_to_atom(Str) % end. parse_for_mod(Fd, F) -> parse_for_mod(Fd, io:parse_erl_exprs(Fd,''), F). parse_for_mod(Fd, {ok,[{op,_,'-',{call,_,{atom,_,module},[Mod]}}],_}, F) -> {ok,parse_mod_expr(Mod)}; % parse_for_mod(Fd, {ok,[{op,_,'-', % {call,_,{atom,_,module},[{atom,_,Mod}]}}],_}, F) -> % {ok,Mod}; parse_for_mod(Fd, {ok,_,_}, F) -> parse_for_mod(Fd, io:parse_erl_exprs(Fd,''), F); parse_for_mod(Fd, {error,_,_}=Error, F) -> io:format("*** Error parsing ~s: ~p~n", [F, Error]), error; parse_for_mod(Fd, {eof,_}, F) -> io:format("*** No module attribute found in ~s~n", [F]), error. parse_mod_expr(Expr) -> list_to_atom(parse_mod_expr1(Expr)). parse_mod_expr1({atom,_,A}) -> atom_to_list(A); parse_mod_expr1({record_field,_,R,{atom,_,A}}) -> parse_mod_expr1(R) ++ "." ++ atom_to_list(A). check_f(F, Ext) -> case regexp:match(F, ".*"++Ext++"\$") of {match, _, _} -> true; _ -> false end. find_apps(Dict) -> App = fetch(app, Dict), {value, {_, NeededApps}} = lists:keysearch(applications, 1, App), AppInfo = case dict:find(apps, Dict) of {ok, As} -> app_info(As, Dict); error -> [] end, RelApps = case dict:find(rel_src, Dict) of {ok, {release, _, RA}} -> RA; error -> [] end, AppName = dict:fetch(app_name, Dict), Apps = merge_apps(RelApps, NeededApps, AppInfo) -- [AppName], ?report(debug, "Apps = ~p~n", [Apps]), KnownApps = [{A,V,D} || {A,V,D} <- AppInfo, V =/= unknown, D =/= unknown], search_apps(Apps, Dict, AppInfo, KnownApps). merge_apps(RelApps, AppApps, AppInfo) -> ?report(debug, "merge_apps(~p, ~p, ~p)~n", [RelApps, AppApps, AppInfo]), Acc0 = lists:map(fun({App,Vsn,Incl}) -> App; ({App,Vsn}) -> App; (App) when atom(App) -> App end, RelApps), with(Acc0, [fun(Acc) -> Acc ++ [A || {A,_,_} <- AppInfo, not(lists:member(A, Acc))] end, fun(Acc) -> Acc ++ [A || A <- AppApps, not(lists:member(A, Acc))] end, fun(Acc) -> InclApps = lists:foldl( fun({_,_,Incls}, AccX) -> AccX ++ Incls; (_, AccX) -> AccX end, [], RelApps), Acc ++ [A || A <- InclApps, not(lists:member(A, Acc))] end]). app_info(Apps, Dict) -> MyName = dict:fetch(app_name, Dict), MyVsn = dict:fetch(app_vsn, Dict), MyEbin = ebin_dir(Dict), Info = lists:map( fun(A) when atom(A) -> {A,unknown,unknown}; ({A,V}) -> {A,V,unknown}; ({A,V,D}) -> {A,V,D} end, Apps), AppName = dict:fetch(app_name, Dict), case lists:keysearch(AppName, 1, Info) of {value, {Ai,Vi,Di}} -> [Version,Dir] = lists:foldr( fun({unknown,Vx}, Acc) -> [Vx|Acc]; ({Vx,Vx}, Acc) -> [Vx|Acc]; ({V1,V2},_) -> exit({conflicting_info_in_apps, {{Ai,Vi,Di}, {MyName,MyVsn,MyEbin}}}) end, [], [{Vi,MyVsn},{Di,MyEbin}]), lists:keyreplace(AppName, 1, Info, {AppName, Version, Dir}); false -> Info ++ [{AppName, dict:fetch(app_vsn, Dict), ebin_dir(Dict)}] end. search_apps(Apps, Dict, AppInfo, InitAcc) -> ?report(debug,"search_apps(~p,Dict,~p,~p)~n", [Apps,AppInfo,InitAcc]), Path = fetch(path, Dict), Ebin = ebin_dir(Dict), Path1 = Path ++ [Ebin], MyAppName = dict:fetch(app_name, Dict), MyApp = {MyAppName, dict:fetch(app_vsn,Dict), ebin_dir(Dict)}, ?report(debug, "Search Path = ~p~n", [Path1]), Found = case expand_path(Path ++ [Ebin], Apps, AppInfo, InitAcc) of {FoundInfo, []} -> ?report(debug, "LeftApps = [], FoundInfo = ~p~n", [FoundInfo]), check_found(FoundInfo, Dict); {FoundInfo, LeftApps} -> %% LeftApps may still include applications for which we've %% found versions (but didn't know if they were the right ones) ?report(debug, "LeftApps = ~p,~nFoundInfo = ~p~n", [LeftApps, FoundInfo]), case [A || A <- LeftApps, not(lists:keymember(A, 1, FoundInfo))] of [] -> check_found(FoundInfo, Dict); NotFound -> exit({not_found, NotFound}) end end, replaceadd(MyAppName,1,Found,MyApp). check_found(FoundInfo, Dict) -> case lists:foldr( fun({A,unknown,D}, {Found,Left}) -> AppF = filename:join(D,atom_to_list(A) ++ ".app"), case vsn_from_app_file(AppF) of unknown -> {Found,[{A,unknown,D}|Left]}; FoundVsn -> {[{A,FoundVsn,D}|Found], Left} end; (GoodApp, {Found,Left}) -> {[GoodApp|Found], Left} end, {[],[]}, FoundInfo) of {Found, []} -> ?report(debug, "Found apps:~n ~p~n", [FoundInfo]), Found; {Found, UnknownVsns} -> %% These app directories didn't have a version %% suffix. This is allowed, but we must then %% extract the version some other way.... FFS ?report(debug, "Should find the versions of" " these apps:~n ~p~n", [UnknownVsns]), Found end. replaceadd(Key,Pos,[H|T],Obj) when element(Pos,H) == Key -> [Obj|T]; replaceadd(Key,Pos,[H|T],Obj) -> [H|replaceadd(Key,Pos,T,Obj)]; replaceadd(Key,Pos,[],Obj) -> [Obj]. vsn_from_app_file(AppF) -> case file:consult(AppF) of {ok, [{application,_,Opts}]} -> case lists:keysearch(vsn,1,Opts) of {value,{_,Vsn}} -> Vsn; false -> unknown end; Other -> ?report(debug, "file:consult(~p) -> ~p~n", [AppF,Other]), unknown end. %%% expand_path/2 takes a list of path expressions, where each expression %%% may contain wildcards (sh expressions) expand_path([Dir|Ds], Apps, AppInfo, Acc) -> ?report(debug, "Dir = ~p~n", [Dir]), SplitDir = filename:split(Dir), case file:read_file_info(Dir) of {ok, #file_info{type = directory}} -> case lists:reverse(SplitDir) of ["ebin",AppDir|_] -> case string:tokens(AppDir, "-") of [App,Vsn] -> ?report(debug, "App = ~p, Vsn = ~p~n", [App,Vsn]), AppA = list_to_atom(App), {Acc1,Apps1} = case lists:member(AppA, Apps) of true -> maybe_save_app(AppA, Vsn, Dir, Apps, AppInfo, Acc); false -> {Acc, Apps} end, expand_path(Ds, Apps1, AppInfo, Acc1); [App] -> %% App dir without version suffix -- legal, %% but we must extract the version somehow AppA = list_to_atom(App), AppF = filename:join(Dir, App ++ ".app"), Vsn = vsn_from_app_file(AppF), case lists:member(AppA, Apps) of true -> {Acc1,Apps1} = maybe_save_app(AppA, Vsn, Dir, Apps, AppInfo, Acc), expand_path(Ds, Apps1, AppInfo, Acc1); false -> expand_path(Ds, Apps, AppInfo, Acc) end; Other -> ?report(debug, "*** strange app dir ~p~n", [AppDir]), expand_path(Ds, Apps, AppInfo, Acc) end; _ -> expand_path(Ds, Apps, AppInfo, Acc) end; {error, enoent} -> %% assume it is a regexp {Acc1, RestApps} = expand_path( d_expand(SplitDir), Apps, AppInfo, Acc), expand_path(Ds, RestApps, AppInfo, Acc1); _ -> %% something else -- ignore expand_path(Ds, Apps, AppInfo, Acc) end; expand_path([], Apps, AppInfo, Acc) -> {Acc, Apps}. maybe_save_app(AppA, Vsn, Dir, Apps, AppInfo, Acc) -> ?report(debug, ("maybe_save_app(~p,~p,~p,_,~n" " ~p,~n" " ~p)~n"), [AppA,Vsn,Dir,AppInfo, Acc]), case lists:keysearch(AppA, 1, Acc) of {value, {_,unknown,unknown}} -> {lists:keyreplace( AppA,1,Acc,{AppA,Vsn,Dir}), Apps}; {value, {_,Vsn,_}} -> %% We already have this version {Acc, Apps}; Result -> case lists:keysearch(AppA, 1, AppInfo) of {value, {_,unknown,_}} -> case Result of {value, {_,OtherVsn,_}} -> case later_vsn(Vsn, OtherVsn) of true -> {lists:keyreplace( AppA,1,Acc,{AppA,Vsn,Dir}), Apps}; false -> {Acc, Apps} end; false -> {[{AppA,Vsn,Dir}|Acc], Apps} end; {value, {_, Vsn, unknown}} -> %% This is the version we want. Look no further for %% this app. {[{AppA,Vsn,Dir}|Acc], Apps -- [AppA]}; {value, {_, OtherVsn, _}} -> %% This is not the version we want {Acc, Apps}; false -> %% We have no prior knowledge of this app, other than %% that it should be included case Result of {value, {_,OtherVsn,_}} -> case later_vsn(Vsn, OtherVsn) of true -> {[{AppA,Vsn,Dir}|Acc], Apps}; false -> {Acc, Apps} end; false -> {[{AppA,Vsn,Dir}|Acc], Apps} end end end. %%% later_vsn(VsnA, VsnB) -> true | false. %%% true if VsnA > VsnB, false otherwise later_vsn(unknown,_) -> false; later_vsn(_,unknown) -> true; later_vsn(Vsn, Vsn) -> false; later_vsn(VsnA, VsnB) -> case get_newest([{VsnB,[]},{VsnA,[]}]) of {VsnB,_} -> false; {VsnA,[]} -> true end. %%% get_newest([{Vsn,Info}]) -> {NewestVsn,RelatedInfo}. %%% get_newest([{Vsn,_}] =X) -> X; get_newest([{Vsn1,_}=X1, {Vsn2,_}=X2 | Rest]) -> V1 = split(Vsn1), V2 = split(Vsn2), Newest = get_newest1(V1, V2, X1, X2), get_newest([Newest | Rest]). get_newest1([H1|T1], [H2|T2], X1, X2) -> if H1 > H2 -> X1; H2 > H1 -> X2; H1 == H2 -> get_newest1(T1, T2, X1, X2) end; get_newest1([], [], X1, X2) -> X1; get_newest1([H1|_], [], X1, X2) -> X1; get_newest1([], [H2|_], X1, X2) -> X2. split(VStr) -> split(VStr, undefined, [], []). split([], Mode, Acc1, Acc) -> lists:reverse([subv(Mode, lists:reverse(Acc1)) | Acc]); split("." ++ T, Mode, Acc1, Acc) -> split(T, Mode, [], [subv(Mode, lists:reverse(Acc1)) | Acc]); split([I|T], string, Acc1, Acc) when $0 =< I, I =< $9 -> split(T, integer, [I], [subv(string, lists:reverse(Acc1)) | Acc]); split([I|T], integer, Acc1, Acc) when $0 =< I, I =< $9 -> split(T, integer, [I|Acc1], Acc); split([C|T], integer, Acc1, Acc) -> split(T, string, [C], [subv(integer, lists:reverse(Acc1)) | Acc]); split([I|T], undefined, [], Acc) when $0 =< I, I =< $9 -> split(T, integer, [I], Acc); split([C|T], undefined, [], Acc) -> split(T, string, [C], Acc). subv(integer, SubV) -> list_to_integer(SubV); subv(string, SubV) -> SubV. %%% end later_vsn() member_of(AppName, [AppName|As]) -> {true, AppName}; member_of(AppName, [{AppName,Vsn}|As]) -> {true, {AppName, Vsn}}; member_of(AppName, [_|As]) -> member_of(AppName, As); member_of(_, []) -> false. d_expand(["/"|Ds]) -> d_expand(Ds, ["/"]); d_expand(Ds) -> {ok,Cwd} = file:get_cwd(), d_expand(Ds, [Cwd]). d_expand([D|Ds], Cur) -> Pat = regexp:sh_to_awk(D), Cur1 = lists:foldl( fun(C, Acc) -> case list_dir(C) of [] -> Acc; Fs -> Matches = [F || F <- Fs, matches(F, Pat)], Acc ++ [filename:join(C,F) || F <- Matches, is_dir(C,F)] end end, [], Cur), d_expand(Ds, Cur1); d_expand([], Cur) -> Cur. matches(Str, Pat) -> case regexp:match(Str,Pat) of {match,_,_} -> true; nomatch -> false end. is_dir(Parent, Dir) -> case file:read_file_info(filename:join(Parent, Dir)) of {ok, #file_info{type=directory}} -> true; _ -> false end. list_dir(Dir) -> case file:read_file_info(Dir) of {ok, #file_info{type=directory}} -> case file:list_dir(Dir) of {ok, Fs} -> Fs; _ -> [] end; _ -> [] end. apps(Apps, Dict) -> Path = fetch(path, Dict), Pat = [filename:basename(filename:dirname(Dir))||Dir<-get_path(Dict)], Vss = [string:tokens(D,"-") || D <- Pat, app(D)], do_apps(Apps, Vss). app(D) -> case regexp:match(D,".*-[0-9].*") of nomatch -> false; _ -> true end. do_apps([], Vss) -> []; do_apps([App|Apps], Vss) when list(App) -> do_apps([list_to_atom(App)|Apps], Vss); do_apps([App|Apps], Vss) when atom(App) -> [{App, get_vsn(atom_to_list(App), Vss)}|do_apps(Apps, Vss)]. get_vsn(App, [[App, Vsn]|_]) -> Vsn; get_vsn(App, [_|T]) -> get_vsn(App, T). get_erts_vsn() -> {ok, [[Root]]} = init:get_argument(root), {ok, Fs} = file:list_dir(Root), get_erts_vsn(Fs). get_erts_vsn([[$e,$r,$t,$s,$-|Vsn]|_]) -> Vsn; get_erts_vsn([_|T]) -> get_erts_vsn(T). out(Dir, App, Ext, Term) -> FN = filename:join(Dir, App++Ext), out(FN, Term). out(FN, Term) -> ?report(debug, "out(~p,~p)~n", [FN,Term]), case file:open(FN, [write]) of {ok, FD} -> io:fwrite(FD, "~p.~n", [Term]), file:close(FD); {error, R} -> exit({bad_filename, {FN, R}}) end. read_app_file(Dict) -> AppName = fetch(app_name, Dict), AppNameS = atom_to_list(AppName), AppF = get_app_filename(AppNameS, Dict), App = case file:consult(AppF) of {ok, [{application, _Name, Options}]} -> app_options(Options); {ok, [Options]} when list(Options) -> app_options(Options); {error, enoent} -> RealAppF = filename:join(ebin_dir(Dict), AppNameS ++ ".app"), case file:consult(RealAppF) of {ok, [{application, _Name, Options}]} -> app_options(Options); {error, enoent} -> ?report(debug, "no app file (~p) -- must generate one.~n", [AppName]), Options = app_options( [{description, "auto-generated app file for " ++ AppNameS}, {applications, [kernel,stdlib]}]) end; Other -> exit({error_reading_app_file, {AppF, Other}}) end, ?report(debug, "App = ~p~n", [App]), App. app_options(Opts) -> [opt(description,Opts,""), opt(id,Opts,""), opt(registered,Opts,[]), opt(applications,Opts,[kernel,stdlib]), opt(env,Opts,[]), opt(mod,Opts,[]), opt(start_phases,Opts,undefined)]. opt(Key,List,Default) -> case lists:keysearch(Key,1,List) of {value, {_, Value}} -> {Key, Value}; false -> {Key, Default} end. get_path(Dict) -> AppPath = ebin_dir(Dict), Path = code:get_path(), case lists:member(AppPath, Path) of true -> Path; false -> Path ++ [AppPath] end. % otp_path(Dict) -> % case dict:find(otp_path, Dict) of % {ok, Path} -> % Path; % error -> % {ok,[[Root]]} = init:get_argument(root), % filename:join([Root,"lib","*","ebin"]) % end. script(File) -> script(File, erl_eval:new_bindings()). script(File, Bs) -> case file:open(File, [read]) of {ok, Fd} -> Bs1 = erl_eval:add_binding('ScriptName',File,Bs), R = case eval_stream(Fd, Bs1) of {ok, Result} -> {ok, Result}; Error -> Error end, file:close(Fd), R; Error -> Error end. path_script(Path, File) -> path_script(Path, File, erl_eval:new_bindings()). path_script(Path, File, Bs) -> case file:path_open(Path, File, [read]) of {ok,Fd,Full} -> Bs1 = erl_eval:add_binding('ScriptName',Full,Bs), case eval_stream(Fd, Bs1) of {error,E} -> file:close(Fd), {error, {E, Full}}; {ok, R} -> file:close(Fd), {ok,R,Full} end; Error -> Error end. eval_stream(Fd, Bs) -> eval_stream(Fd, undefined, Bs). eval_stream(Fd, Last, Bs) -> eval_stream(io:parse_erl_exprs(Fd, ''), Fd, Last, Bs). eval_stream({ok,Form,EndLine}, Fd, Last, Bs0) -> case catch erl_eval:exprs(Form, Bs0) of {value,V,Bs} -> eval_stream(Fd, {V}, Bs); {'EXIT',Reason} -> {error, Reason} end; eval_stream({error,What,EndLine}, Fd, L, Bs) -> {error, {parse_error, {What,EndLine}}}; eval_stream({eof,EndLine}, Fd, Last, Bs) -> case Last of {Val} -> {ok, Val}; undefined -> %% empty script {error, empty_script} end. do_report(error) -> %% always report errors true; do_report(Level) -> LevelN = rpt_level(Level), case get(builder_debug) of true -> true; false -> false; undefined -> case get(builder_report_level) of undefined -> false; [{AtLevel,_}|_] when AtLevel >= LevelN -> true; _ -> false end end. rpt_level(none) -> 0; rpt_level(progress) -> 1; rpt_level(verbose) -> 2; rpt_level(debug) -> 3; rpt_level(N) when integer(N), N >= 0 -> N. push_report_level(Level, Dict) -> Ref = fetch('#ref#', Dict), N = rpt_level(Level), case get(builder_report_level) of undefined -> put(builder_report_level, [{N,Ref}]), io:format("pushed 1st report level ~p(~p)~n",[Level,N]); [{Prev,Ref}|Tail] -> put(builder_report_level, [{N,Ref}|Tail]), io:format("redefined report level ~p(~p)~n",[Level,N]); [_|_] = Levels -> put(builder_report_level, [{N,Ref}|Levels]), io:format("pushed next report level ~p(~p)~n",[Level,N]) end. pop_report_level() -> Level = get(builder_report_level), case Level of [_] -> erase(builder_report_level); [_|Tail] -> put(builder_report_level, Tail); undefined -> ok end. verify_dir(Dir) -> case file:read_file_info(Dir) of {ok, FI} when FI#file_info.type == directory, FI#file_info.access == read_write -> ok; {ok, FI} when FI#file_info.type == directory -> exit({access, Dir}); {error, enoent} -> try_create(Dir) end. try_create("/") -> exit({create, "/"}); try_create(".") -> exit({create, "/"}); try_create(Dir) -> case file:make_dir(Dir) of {error, Reason} -> try_create(filename:dirname(Dir)), try_create(Dir); ok -> ok end. manderlbot-0.9.2/manderlbot.sh.in0100644000175000000620000000251207740576553015544 0ustar acidstaff#!/bin/bash # # This script allows to launch manderlbot and control it while running # # $Id: manderlbot.sh.in,v 1.2 2003/10/07 17:52:43 nico Exp $ INSTALL_DIR=%INSTALL_DIR% VERSION=%VERSION% BOOT_SCRIPT="$INSTALL_DIR/lib/manderlbot-$VERSION/priv/manderlbot" RUN_OPTIONS="-detached -setcookie mdb" usage() { prog=`basename $1` echo "$prog start|stop|restart|status" } start() { echo "erl -sname manderlbot $RUN_OPTIONS -boot $BOOT_SCRIPT $CONF_OPT $LOG_OPT \ $LOG_LEVEL_OPT" erl -sname manderlbot $RUN_OPTIONS -boot $BOOT_SCRIPT $CONF_OPT $LOG_OPT \ $LOG_LEVEL_OPT } stop() { erl -sname control $RUN_OPTIONS -s mdb_control stop } status() { pid=`ps -edaf | awk '/-[s]name manderlbot/ {print $2}'` if [ "zz$pid" != "zz" ]; then erl -sname control $RUN_OPTIONS -s mdb_control status echo $LOG_OPT echo $CONF_OPT else echo "manderlbot is not running" echo $LOG_OPT echo $CONF_OPT fi } while getopts ":c:l:f:" Option do case $Option in c) CONF_OPT="-manderlbot config_file \"$OPTARG\" ";; f) LOG_OPT="-manderlbot log_file \"$OPTARG\" ";; l) LOG_LEVEL_OPT="-manderlbot log_level '$OPTARG' ";; *) usage ;; esac done shift $(($OPTIND - 1)) case $1 in start) start;; stop) stop;; status) status;; restart) stop && start;; *) usage $0 esac manderlbot-0.9.2/bofh.fortune0100644000175000000620000004034307764117701014772 0ustar acidstaff% clock speed % solar flares % electromagnetic radiation from satellite debris % static from nylon underwear % static from plastic slide rules % global warming % poor power conditioning % static buildup % doppler effect % hardware stress fractures % magnetic interferance from money/credit cards % dry joints on cable plug % we're waiting for [the phone company] to fix that line % sounds like a Windows problem, try calling Microsoft support % temporary routing anomoly % somebody was calculating pi on the server % fat electrons in the lines % excess surge protection % floating point processor overflow % divide-by-zero error % POSIX complience problem % monitor resolution too high % improperly oriented keyboard % network packets travelling uphill (use a carrier pigeon) % Decreasing electron flux % first Saturday after first full moon in Winter % radiosity depletion % CPU radiator broken % It works the way the Wang did, what's the problem % positron router malfunction % cellular telephone interference % techtonic stress % pizeo-electric interference % (l)user error % working as designed % dynamic software linking table corrupted % heavy gravity fluctuation, move computer to floor rapidly % secretary plugged hairdryer into UPS % terrorist activities % not enough memory, go get system upgrade % interrupt configuration error % spaghetti cable cause packet failure % boss forgot system password % bank holiday - system operating credits not recharged % virus attack, luser responsible % waste water tank overflowed onto computer % Complete Transient Lockout % bad ether in the cables % Bogon emissions % Change in Earth's rotational speed % Cosmic ray particles crashed through the hard disk platter % Smell from unhygenic janitorial staff wrecked the tape heads % Evil dogs hypnotized the night shift % Plumber mistook routing panel for decorative wall fixture % Electricians made popcorn in the power supply % Groundskeepers stole the root password % high pressure system failure % failed trials, system needs redesigned % system has been recalled % not approved by the FCC % need to wrap system in aluminum foil to fix problem % not properly grounded, please bury computer % CPU needs recalibration % bit bucket overflow % descramble code needed from software company % only available on a need to know basis % knot in cables caused data stream to become twisted and kinked % nesting roaches shorted out the ether cable % The file system is full of it % Satan did it % Daemons did it % You're out of memory % There isn't any problem % Unoptimized hard drive % Typo in the code % Yes, yes, its called a desgin limitation % Look, buddy: Windows 3.1 IS A General Protection Fault. % Support staff hung over, send aspirin and come back LATER. % Someone is standing on the ethernet cable, causeing a kink in the cable % Windows 95 undocumented "feature" % Runt packets % Password is too complex to decrypt % Boss' kid fucked up the machine % Electromagnetic energy loss % Budget cuts % Mouse chewed through power cable % Stale file handle (next time use Tupperware(tm)!) % Feature not yet implimented % Internet outage % Pentium FDIV bug % Vendor no longer supports the product % Small animal kamikaze attack on power supplies % The vendor put the bug there. % SIMM crosstalk. % IRQ dropout % Collapsed Backbone % Power company testing new voltage spike (creation) equipment % operators on strike due to broken coffee machine % backup tape overwritten with copy of system manager's favourite CD % UPS interrupted the server's power % The electrician didn't know what the yellow cable was so he yanked the ethernet out. % The keyboard isn't plugged in % The air conditioning water supply pipe ruptured over the machine room % The electricity substation in the car park blew up. % The rolling stones concert down the road caused a brown out % The salesman drove over the CPU board. % Root nameservers are out of sync % electro-magnetic pulses from French above ground nuke testing. % your keyboard's space bar is generating spurious keycodes. % the real ttys became pseudo ttys and vice-versa. % the printer thinks its a router. % the router thinks its a printer. % we just switched to FDDI. % halon system went off and killed the operators. % user to computer ratio too high. % user to computer ration too low. % Sticky bits on disk. % Power Company having EMP problems with their reactor % The ring needs another token % SCSI Chain overterminated % because of network lag due to too many people playing deathmatch % Daemons loose in system. % BNC (brain not (user brain not connected) % UBNC (user brain not connected) % LBNC (luser brain not connected) % disks spinning backwards - toggle the hemisphere jumper. % new guy cross-connected phone lines with ac power bus. % had to use hammer to free stuck disk drive heads. % Too few computrons available. % Flat tire on station wagon with tapes. ("Never underestimate the bandwidth of a station wagon full of tapes hurling down the highway" Andrew S. Tanenbaum) % Communications satellite used by the military for star wars. % Party-bug in the Aloha protocol. % Insert coin for new game % Dew on the telephone lines. % Arcserve crashed the server again. % Some one needed the powerstrip, so they pulled the switch plug. % My pony-tail hit the on/off switch on the power strip. % Big to little endian conversion error % You can tune a file system, but you can't tune a fish (from most tunefs man pages) % Dumb terminal % Zombie processes haunting the computer % Incorrect time syncronization % Defunct processes % Stubborn processes % non-redundant fan failure % monitor VLF leakage % bugs in the RAID % no "any" key on keyboard % root rot % Backbone Scoliosis % mv /pub/lunch % excessive collisions and not enough packet ambulances % le0: no carrier: transceiver cable problem? % broadcast packets on wrong frequency % popper unable to process jumbo kernel % NOTICE: alloc: /dev/null: filesystem full % pseudo-user on a pseudo-terminal % Recursive traversal of loopback mount points % Backbone adjustment % OS swapped to disk % vapors from evaporating sticky-note adhesives % sticktion % short leg on process table % multicasts on broken packets % ether leak % Atilla the Hub % endothermal recalibration % filesystem not big enough for Jumbo Kernel Patch % loop found in loop in redundant loopback % system consumed all the paper for paging % permission denied % Reformatting Page. Wait... % SCSI's too wide. % Proprietary Information. % Just type 'mv * /dev/null'. % runaway cat on system. % We only support a 1200 bps connection. % Me no internet, only janitor, me just wax floors. % I'm sorry a pentium won't do, you need an SGI to connect with us. % Post-it Note Sludge leaked into the monitor. % the curls in your keyboard cord are losing electricity. % The monitor needs another box of pixels. % RPC_PMAP_FAILURE % kernel panic: write-only-memory (/dev/wom0) capacity exceeded. % Write-only-memory subsystem too slow for this machine. Contact your local dealer. % Quantum dynamics are affecting the transistors % Police are examining all internet packets in the search for a narco-net-traficer % We are currently trying a new concept of using a live mouse. Unfortuantely, one has yet to survive being hooked up to the computer.....please bear with us. % We didn't pay the Internet bill and it's been cut off. % Lightning strikes. % Of course it doesn't work. We've performed a software upgrade. % Change your language to Finnish. % Flourescent lights are generating negative ions. If turning them off doesn't work, take them out and put tin foil on the ends. % High nuclear activity in your area. % The MGs ran out of gas. % The UPS doesn't have a battery backup. % Recursivity. Call back if it happens again. % Someone thought The Big Red Button was a light switch. % The mainframe needs to rest. % Try calling the Internet's head office -- it's in the book. % The lines are all busy (busied out, that is -- why let them in to begin with?). % A star wars satellite accidently blew up the WAN. % Fatal error right in front of screen % wrong polarity of neutron flow % Lusers learning curve appears to be fractal % We had to turn off that service to comply with the CDA Bill. % Ionisation from the air-conditioning % TCP/IP UDP alarm threshold is set too low. % Someone is broadcasting pigmy packets and the router dosn't know how to deal with them. % The new frame relay network hasn't bedded down the software loop transmitter yet. % Fanout dropping voltage too much, try cutting some of those little traces % Plate voltage too low on demodulator tube % CPU needs bearings repacked % Too many little pins on CPU confusing it, bend back and forth until 10-20% are neatly removed. Do _not_ leave metal bits visible! % Software uses US measurements, but the OS is in metric... % The computer fletely, mouse and all. % Too much radiation coming from the soil. % Program load too heavy for processor to lift. % Processes running slowly due to weak power supply % Our ISP is having {switching,routing,SMDS,frame relay} problems % We've run out of licenses % Interference from lunar radiation % Standing room only on the bus. % You need to install an RTFM interface. % That would be because the software doesn't work. % That's easy to fix, but I can't be bothered. % Someone's tie is caught in the printer, and if anything else gets printed, he'll be in it too. % We're upgrading /dev/null % The Usenet news is out of date % Our POP server was kidnapped by a weasel. % It's stuck in the Web. % Your modem doesn't speak English. % The mouse escaped. % All of the packets are empty. % The UPS is on strike. % Neutrino overload on the nameserver % Melting hard drives % Someone has messed up the kernel pointers % The kernel license has expired % The cord jumped over and hit the power switch. % Bit rot % The Dilithium Cyrstals need to be rotated. % The static electricity routing is acting up... % Traceroute says that there is a routing problem in the backbone. It's not our problem. % The co-locator cannot verify the frame-relay gateway to the ISDN server. % Lawn mower blade in your fan need sharpening % Electrons on a bender % Telecommunications is upgrading. % Telecommunications is downgrading. % Telecommunications is downshifting. % Hard drive sleeping. Let it wake up on it's own... % Interference between the keyboard and the chair. % The CPU has shifted, and become decentralized. % Due to the CDA, we no longer have a root account. % We ran out of dial tone and we're and waiting for the phone company to deliver another bottle. % You must've hit the wrong anykey. % PCMCIA slave driver % The Token fell out of the ring. Call us when you find it. % The hardware bus needs a new token. % Too many interrupts % Not enough interrupts % The data on your hard drive is out of balance. % Digital Manipulator exceeding velocity parameters % appears to be a Slow/Narrow SCSI-0 Interface problem % microelectronic Riemannian curved-space fault in write-only file system % fractal radiation jamming the backbone % routing problems on the neural net % IRQ-problems with the Un-Interruptable-Power-Supply % CPU-angle has to be adjusted because of vibrations coming from the nearby road % emissions from GSM-phones % CD-ROM server needs recalibration % firewall needs cooling % asynchronous inode failure % transient bus protocol violation % incompatible bit-registration operators % your process is not ISO 9000 compliant % You need to upgrade your VESA local bus to a MasterCard local bus. % The recent proliferation of Nuclear Testing % Elves on strike. (Why do they call EMAG Elf Magic) % Internet exceeded Luser level, please wait until a luser logs off before attempting to log back on. % Your EMAIL is now being delivered by the USPS. % Your computer hasn't been returning all the bits it gets from the Internet. % You've been infected by the Telescoping Hubble virus. % Scheduled global CPU outage % processor has processed too many intructions. % packets were eaten by the terminator % The POP server is out of Coke % Fiber optics caused gas main leak % Server depressed, needs Prozak % quatnum decoherence % those damn racoons! % suboptimal routing experience % A plumber is needed, the network drain is clogged % 50% of the manual is in .pdf readme files % the AA battery in the wallclock sends magnetic interference % the xy axis in the trackball is coordinated with the summer soltice % the butane lighter causes the pincushioning % old inkjet cartridges emanate barium-based fumes % manager in the cable duct % HTTPD Error 666 : BOFH was here % HTTPD Error 4004 : very old Intel cpu - insufficient processing power % Network failure - call NBC % Having to manually track the satellite. % The rubber band broke % We're on Token Ring, and it looks like the token got loose. % Stray Alpha Particles from memory packaging caused Hard Memory Error on Server. % paradigm shift...without a clutch % PEBKAC (Problem Exists Between Keyboard And Chair) % The cables are not the same length. % Second-sytem effect. % Chewing gum on /dev/sd3c % Boredom in the Kernel. % struck by the Good Times virus % YOU HAVE AN I/O ERROR -> Incompetent Operator error % Your parity check is overdrawn and you're out of cache. % Plasma conduit breach % Out of cards on drive D: % Sand fleas eating the Internet cables % parallel processors running perpendicular today % ATM cell has no roaming feature turned on, notebooks can't connect % Webmasters kidnapped by evil cult. % Failure to adjust for daylight savings time. % Virus transmitted from computer to sysadmins. % Virus due to computers having unsafe sex. % Incorrectly configured static routes on the corerouters. % Forced to support NT servers; sysadmins quit. % Suspicious pointer corrupted virtual machine % Its the InterNIC's fault. % Root name servers corrupted. % Budget cuts forced us to sell all the power cords for the servers. % Someone hooked the twisted pair wires into the answering machine. % Operators killed by year 2000 bug bite. % We've picked COBOL as the language of choice. % Operators killed when huge stack of backup tapes fell over. % Robotic tape changer mistook operator's tie for a backup tape. % t's an ID-10-T error % Dyslexics retyping hosts file on servers % The Internet is being scanned for viruses. % Your computer's union contract is set to expire at midnight. % Bad user karma. % /dev/clue was linked to /dev/null % Increased sunspot activity. % It's union rules. There's nothing we can do about it. Sorry. % Interferance from the Van Allen Belt. % Jupiter is aligned with Mars. % Redundant ACLs. % Mail server hit by UniSpammer. % T-1's congested due to porn traffic to the news server. % Data for intranet got routed through the extranet and landed on the internet. % What you are experiencing is not a problem; it is an undocumented feature. % Secretary sent chain letter to all 5000 employees. % Sysadmin accidentally destroyed pager with a large hammer. % Bad cafeteria food landed all the sysadmins in the hospital. % Route flapping at the NAP. % Computers under water due to SYN flooding. % The vulcan-death-grip ping has been applied. % Electrical conduits in machine room are melting. % Traffic jam on the Information Superhighway. % Radial Telemetry Infiltration % Cow-tippers tipped a cow onto the server. % tachyon emissions overloading the system % Maintence window broken % Computer room being moved. Our systems are down for the weekend. % Sysadmins busy fighting SPAM. % Repeated reboots of the system failed to solve problem % Domain controler not responding % Someone stole your IP address, call the Internet detectives. % operation failed because: there is no message for this error (#1014) % stop bit received % internet is needed to catch the etherbunny % network down, IP packets delivered via UPS % Firmware update in the coffee machine % Mouse has out-of-cheese-error % Borg implants are failing % Borg nanites have infested the server % error: one bad user found in front of screen % Please state the nature of the technical emergency % Internet shut down due to maintainance % Daemon escaped from pentagram % crop circles in the corn shell % sticky bit has come loose % Hot Java has gone cold % Cache miss - please take better aim next time % Hash table has woodworm % Trojan horse ran out of hay % Zombie processess detected, machine is haunted. % overflow error in /dev/null % Browser's cookie is corrupted - someone's been nibbling on it. % Mailer-daemon is busy burning your message in hell. % vi needs to be upgraded to vii manderlbot-0.9.2/ebin/0040755000175000000620000000000010000342175013342 5ustar acidstaff