#!/usr/bin/env swipl
/* echobot.pl
Author: Gimenez, Christian.

Copyright (C) 2026 Gimenez, Christian

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 3 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, see <http://www.gnu.org/licenses/>.

2026-05-03
*/

:- module(echobot, []).
/** <module> echobot: Echo bot example.

To run this script, set BOTJID and BOTPASS with the JID and password to connect
to the server.

@author Christian Gimenez
@license GPLv3
*/

:- license(gplv3).

:- use_module(library(xmpp/xmpp)).
:- use_module(library(xmpp/xmpp_core)).
:- use_module(library(xmpp/xmpp_simple)).

:- initialization(main, main).

opt_type(d, debug, boolean).

opt_help(debug, 'Enable debug mode').

% ----------
% Handlers

%! after_connect_handler(+Args: list)
%
% Handler for after_connect hook.
%
% This handler sends the chatty presence of the bot.
%
% @param Args Ignored (no processing to this data).
after_connect_handler(_Args) :-
    send_presence('Echo Bot ready!', [show('chat')]),
    debug(echobot, 'Echobot connected!', []).


% handle_presence(+Args: list)
%
% The logic used when a presence is received.
handle_presence(Args) :-
    member(from(From), Args),
    member(to(To), Args),
    member(status(Text), Args),
    format(show(Show), args),
    format('Presence from ~q to ~q: (~q) ~q', [From, To, Show, Text]), !,
    nl, flush.

%! received_presence_handler(+Args: list)
%
% Handler for received_presence hook.
%
% Executed when a new presence update stanza is received. This handler only
% prints to standard output the update presence.
%
% # Argument
% This is a different from the echobot.pl example. In this case, elements are
% already parsed and stored in Args. See xmpp_simple:presence_handler/1 for
% more information.
%
% # Catcihng errors and exceptions
% To avoid stoping the program, a catch/3 should be use to process the exception
% and avoid propagating it. 
%
% @param Args a list with `[data(XMLParsed), current_state(ClientState)]`. The
%     order of data and currest_state is not garanteed.
received_presence_handler(Args) :-
    debug(echobot,'Handling presence: ~q', [Args]),
    catch(handle_presence(Args), Error,
          debug(chatbot, '!!! Error at handle_presence/1: ~q', [Error])).
    
received_presence_handler(Args) :- !,
    debug(echobot, 'Failed to handle presence: ~q', [Args]).

% receive_message(+Args: list)
%
% The logic used when recieved the message.
receive_message(Args) :-
    member(from(From), Args),
    member(body(Text), Args),

    format(atom(NewText), 'Echo: ~q', [Text]),
    debug(echobot, 'Sending ~q...', [NewText]),
    send_message(From, NewText),

    format('Echo to ~q: ~q', [From, NewText]), nl, flush.

%! received_message_handler(+Args: list)
%
% Handler for the `received_message` hook.
%
% This handlers is executed when the bot receives a message stanza. It extracts
% the from JID and the text message, and sends it back.
%
% # Argument
% This is a different from the echobot.pl example. In this case, elements are
% already parsed and stored in Args. See xmpp_simple:message_handler/1 for
% more information.
%
% # Catcihng errors and exceptions
% To avoid stoping the program, a catch/3 should be use to process the exception
% and avoid propagating it. 
%
% @param Args a list with `[data(XMLParsed), current_state(ClientState)]`. The
%     order of data and currest_state is not garanteed.
received_message_handler(Args) :-
    debug(echobot, 'Received: ~q', [Args]),
    catch(receive_message(Args),
          Error,
          debug(echobot, '!!! Error at receive_message/1: ~q', [Error])).
    
received_message_handler(Args) :- !,
    debug(echobot, 'Message handler failed with ~q', [Args]).

% -----

enable_debug(Options) :-
    option(debug(true), Options, false),
    debug(xmpp), debug(xmpp_auth), debug(xmpp_ping),
    debug(echobot).

get_envdata(JID, Password) :-
    getenv('BOTJID', JID),
    getenv('BOTPASS', Password).
get_envdata(_, _) :-
    throw(error(no_envvar_set,
                context(get_envdata/2, 'Please, set BOTJID and BOTPASS environment variables'))).

main(ArgV) :-
    argv_options(ArgV, _Pos, Options),
    ignore(enable_debug(Options)),

    get_envdata(JID, Password),
    
    add_hook(after_connect, echobot:after_connect_handler),
    % The xmpp_simple hooks are set here.
    add_hook(received_message_simple, echobot:received_message_handler),
    add_hook(received_presence_simple, echobot:received_presence_handler),

    connect(JID, Password),

    write('Press ENTER to quit.'), nl,
    get_char(_),

    disconnect.
