agirbot/src/org/april/hebdobot/model/Hebdobot.java

665 lines
24 KiB
Java
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Copyright (C) 2011-2013,2017 Nicolas Vinot <aeris@imirhil.fr>
* Copyright (C) 2017 Christian Pierre MOMON <cmomon@april.org>
*
* This file is part of (April) Hebdobot.
*
* Hebdobot is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Hebdobot 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Hebdobot. If not, see <http://www.gnu.org/licenses/>
*/
package org.april.hebdobot.model;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.april.hebdobot.HebdobotException;
import org.april.hebdobot.cron.CronManager;
import org.april.hebdobot.cron.CronSettings;
import org.april.hebdobot.identica.IdenticaSettings;
import org.april.hebdobot.pastebin.PastebinClient;
import org.april.hebdobot.pastebin.PastebinSettings;
import org.april.hebdobot.pastebin.Private;
import org.april.hebdobot.review.CollectiveTopic;
import org.april.hebdobot.review.IndividualTopic;
import org.april.hebdobot.review.Message;
import org.april.hebdobot.review.Review;
import org.april.hebdobot.review.Topic;
import org.april.hebdobot.twitter.TwitterClient;
import org.april.hebdobot.twitter.TwitterSettings;
import org.april.hebdobot.util.BuildInformation;
import org.jibble.pircbot.IrcException;
import org.jibble.pircbot.NickAlreadyInUseException;
import org.jibble.pircbot.PircBot;
import org.joda.time.DateTime;
import org.joda.time.format.ISODateTimeFormat;
import org.quartz.SchedulerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import fr.devinsy.util.strings.StringsUtils;
import twitter4j.TwitterException;
/**
* The Class Hebdobot.
*/
public class Hebdobot extends PircBot
{
private static final Logger logger = LoggerFactory.getLogger(Hebdobot.class);
private File homeDirectory;
private String host;
private int port;
private String channel;
private File reviewDirectory;
private String reviewFileSuffix;
private Review review;
private IdenticaSettings identicaSettings;
private PastebinSettings pastebinSettings;
private TwitterSettings twitterSettings;
private CronSettings cronSettings;
private UserAliases aliases;
private CronManager cronManager;
/**
* Instantiates a new hebdobot.
*
* @param host
* the host
* @param port
* the port
* @param name
* the name
* @param channel
* the channel
* @param homeDirectory
* the home directory
* @param reviewDirectory
* the review directory
* @param reviewFileSuffix
* the review file suffix
*/
public Hebdobot(final String host, final int port, final String name, final String channel, final File homeDirectory, final File reviewDirectory,
final String reviewFileSuffix)
{
this.homeDirectory = homeDirectory;
this.host = host;
this.port = port;
this.channel = channel;
this.setName(name);
this.reviewDirectory = reviewDirectory;
this.reviewFileSuffix = reviewFileSuffix;
this.review = null;
this.identicaSettings = new IdenticaSettings();
this.pastebinSettings = new PastebinSettings();
this.twitterSettings = new TwitterSettings();
this.cronSettings = new CronSettings();
this.aliases = new UserAliases();
this.cronManager = null;
}
/**
* Close.
*/
public void close()
{
this.disconnect();
this.dispose();
}
/**
* Gets the aliases.
*
* @return the aliases
*/
public UserAliases getAliases()
{
return this.aliases;
}
/**
* Gets the cron settings.
*
* @return the cron settings
*/
public CronSettings getCronSettings()
{
return this.cronSettings;
}
/**
* Gets the home directory.
*
* @return the home directory
*/
public File getHomeDirectory()
{
return this.homeDirectory;
}
/**
* Gets the identica settings.
*
* @return the identica settings
*/
public IdenticaSettings getIdenticaSettings()
{
return this.identicaSettings;
}
/**
* Gets the pastebin settings.
*
* @return the pastebin settings
*/
public PastebinSettings getPastebinSettings()
{
return this.pastebinSettings;
}
/**
* Gets the twitter settings.
*
* @return the twitter settings
*/
public TwitterSettings getTwitterSettings()
{
return this.twitterSettings;
}
/**
* Notify irc.
*
* @param message
* the message
*/
public void notifyIrc(final String message)
{
sendMessage(message);
}
/**
* Notify twitter.
*
* @param message
* the message
*/
public void notifyTwitter(final String message)
{
if (this.twitterSettings.isValid())
{
try
{
TwitterClient twitter = new TwitterClient(this.twitterSettings.getConsumerKey(), this.twitterSettings.getConsumerSecret(),
this.twitterSettings.getAccessToken(), this.twitterSettings.getAccessTokenSecret());
twitter.tweet(message);
}
catch (TwitterException exception)
{
logger.error("Error in tweet", exception);
sendMessage("(pour information, la notification par Tweet a échoué : " + exception.getErrorMessage() + ")");
}
}
}
/* (non-Javadoc)
* @see org.jibble.pircbot.PircBot#onMessage(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String)
*/
@Override
protected void onMessage(final String channel, final String sender, final String login, final String hostname, final String message)
{
logger.debug("Message received - channel : {}, sender : {}, message : {}", channel, sender, message);
if (channel.equalsIgnoreCase(this.channel))
{
String text = message.trim();
if (this.review != null)
{
this.review.addRaw(new Message(sender, text));
}
if ((StringUtils.equalsIgnoreCase(text, "!aide")) || (StringUtils.equalsIgnoreCase(text, "!help")))
{
logger.info("!help caught.");
// Help.
sendMessage(sender, "Bienvenue " + sender);
sendMessage(sender, "Je suis " + getName() + ", le robot de gestion des revues hebdomadaires de l'APRIL.");
sendMessage(sender, "Voici les commandes que je comprends :");
sendMessage(sender, " ");
sendMessage(sender, " !aide  : afficher cette aide");
sendMessage(sender, " !debut : commencer une nouvelle revue");
sendMessage(sender, " # titre  : démarrer un sujet individuel");
sendMessage(sender, " ## titre  : démarrer un sujet collectif");
sendMessage(sender, " % message  : traiter comme un commentaire");
sendMessage(sender, " !courant : afficher le sujet en cours");
sendMessage(sender, " !manquants : afficher les participants qui n'ont pas répondu sur le dernier sujet");
sendMessage(sender, " !fin : terminer la revue en cours");
sendMessage(sender, " !stop  : abandonner la revue en cours");
sendMessage(sender, " !licence   : afficher la licence du logiciel Hebdobot et le lien vers ses sources");
sendMessage(sender, " !version   : afficher la version d'Hebdobot");
}
else if (StringUtils.equalsIgnoreCase(text, "!stop"))
{
logger.info("!stop caught.");
// Stop.
if (this.review == null)
{
sendMessage("Aucune revue en cours, abandon impossible.");
}
else
{
sendMessage("Abandon de la revue en cours.");
this.review = null;
}
}
else if (StringsUtils.equalsAnyIgnoreCase(text, "!debut", "!début"))
{
logger.info("!debut caught.");
// Start.
if (this.cronManager != null)
{
try
{
this.cronManager.shutdown();
}
catch (SchedulerException exception)
{
logger.warn("Scheduler shutdown failed.", exception);
}
}
this.review = new Review(sender, this.aliases);
sendMessage(sender, "Bonjour " + sender + ", vous êtes le conducteur de réunion.");
sendMessage(sender, "Pour terminer la réunion, tapez \"!fin\"");
sendMessage("% Début de la réunion hebdomadaire");
sendMessage("% rappel : toute ligne commençant par % sera considérée comme un commentaire et non prise en compte dans la synthèse");
sendMessage("% pour connaître le point courant, taper !courant");
}
else if (StringUtils.equalsIgnoreCase(text, "!fin"))
{
logger.info("!fin caught.");
// Stop.
if (this.review == null)
{
sendMessage(sender + ", pas de revue en cours.");
}
else if (!this.review.isOwner(sender))
{
sendMessage(sender + ", vous n'êtes pas le conducteur de la réunion");
}
else
{
{
String date = ISODateTimeFormat.basicDate().print(new DateTime());
String textReview = this.review.toString();
if (this.pastebinSettings.isValid())
{
logger.info("Pastebin the review.");
try
{
PastebinClient pastebinClient = new PastebinClient(this.pastebinSettings.getApiKey());
String returnValue = pastebinClient.paste(textReview, "Revue APRIL " + date, Private.UNLISTED);
sendMessage("% Compte-rendu de la revue : " + returnValue);
}
catch (final Exception exception)
{
logger.error("Error during Pastebin submit.", exception);
}
}
if (this.reviewFileSuffix != null)
{
logger.info("Write review file.");
try
{
if (!this.reviewDirectory.exists())
{
logger.info("Create review directory: " + this.reviewDirectory.getAbsolutePath());
this.reviewDirectory.mkdirs();
}
File file = new File(this.reviewDirectory, date + "_" + this.reviewFileSuffix);
FileUtils.writeStringToFile(file, textReview, StandardCharsets.UTF_8);
sendMessage("% Compte-rendu de la revue : " + file.getName());
}
catch (final Exception exception)
{
logger.error("Error during file generation", exception);
}
}
}
sendMessage("% " + this.review.getOwner()
+ ", ne pas oublier d'ajouter le compte-rendu de la revue sur https://agir.april.org/issues/135");
String participants = StringUtils.join(this.review.getParticipants(), " ");
sendMessage("% " + participants + ", pensez à noter votre bénévalo : http://www.april.org/my?action=benevalo");
sendMessage("% Fin de la revue hebdomadaire");
sendMessage(this.review.getOwner(), "Revue finie.");
this.review = null;
}
}
else if (text.matches("\\s*##.*"))
{
logger.info("\\s*##.* caught.");
// Collective topic, must be before individual topic.
if (this.review != null)
{
if (this.review.isOwner(sender))
{
CollectiveTopic topic = new CollectiveTopic(text.replaceFirst("##", "").trim());
if (!this.review.isEmpty())
{
String participants = StringUtils.join(this.review.getParticipants(), " ");
sendMessage(String.format("%% %s, on va passer à la suite : %s", participants, topic.getTitle()));
}
this.review.begin(topic);
sendMessage("Sujet collectif : " + topic.getTitle());
sendMessage("% 1 minute max");
sendMessage("% si rien à signaler vous pouvez écrire % ras");
sendMessage("% quand vous avez fini vous le dites par % fini");
}
else
{
sendMessage(sender + ", vous n'êtes pas le conducteur de la réunion");
}
}
}
else if (text.matches("\\s*#[^#].*"))
{
logger.info("\\s*#[^#].* caught.");
// Individual topic.
if (this.review != null)
{
if (this.review.isOwner(sender))
{
IndividualTopic topic = new IndividualTopic(text.replaceFirst("#", "").trim());
if (!this.review.isEmpty())
{
String participants = StringUtils.join(this.review.getParticipants(), " ");
sendMessage(String.format("%% %s, on va passer à la suite : %s", participants, topic.getTitle()));
}
this.review.begin(topic);
sendMessage("Sujet individuel : " + topic.getTitle());
sendMessage("% si rien à signaler vous pouvez écrire % ras");
sendMessage("% quand vous avez fini vous le dites par % fini");
}
else
{
sendMessage(sender + ", vous n'êtes pas le conducteur de la réunion");
}
}
}
else if (StringUtils.equalsIgnoreCase(text, "!manquants"))
{
logger.info("!manquants caught.");
// Missing.
if (this.review == null)
{
sendMessage("Pas de revue en cours.");
}
else
{
Topic topic = this.review.getCurrentTopic();
if (topic == null)
{
sendMessage("Aucun sujet traité");
}
else
{
Collection<String> participants = this.review.getParticipants();
Collection<String> currentParticipants = topic.getParticipants();
Collection<String> missing = CollectionUtils.subtract(participants, currentParticipants);
if (missing.isEmpty())
{
sendMessage("Aucun participant manquant \\o/");
}
else
{
sendMessage(String.format("Les participants suivants sont manquants : %1s", StringUtils.join(missing, ", ")));
}
}
}
}
else if (StringUtils.equalsIgnoreCase(text, "!courant"))
{
logger.info("!courant caught.");
// Current.
if (this.review == null)
{
sendMessage("Pas de revue en cours.");
}
else
{
Topic current = this.review.getCurrentTopic();
if (current == null)
{
sendMessage("% Pas de sujet en cours");
}
else if (current instanceof IndividualTopic)
{
sendMessage("% Sujet individuel en cours : " + current.getTitle());
}
else if (current instanceof CollectiveTopic)
{
sendMessage("% Sujet collectif en cours : " + current.getTitle());
}
}
}
else if (StringUtils.equalsIgnoreCase(text, "!statut"))
{
logger.info("!status caught.");
sendMessage(sender, sender + ", voici l'état d'Hebdobot :");
sendMessage(sender, " revue en cours : " + (this.review != null));
if (this.review == null)
{
sendMessage(sender, " animateur revue : none");
}
else
{
sendMessage(sender, " animateur revue : " + this.review.getOwner());
}
sendMessage(sender, " Alias settings : " + (this.aliases.size()));
sendMessage(sender, " Identica settings : " + (this.identicaSettings.isValid()));
sendMessage(sender, " Pastebin settings : " + (this.pastebinSettings.isValid()));
sendMessage(sender, " Twitter settings : " + (this.twitterSettings.isValid()));
sendMessage(sender, " Cron settings : " + (this.cronSettings.size()));
}
else if (StringsUtils.equalsAnyIgnoreCase(text, "!licence", "!license"))
{
logger.info("!licence caught.");
sendMessage(sender
+ ", Hebdobot est un logiciel libre de l'April sous licence GNU AGPL (sources : https://agir.april.org/projects/hebdobot/repository).");
}
else if (StringsUtils.equalsAnyIgnoreCase(text, "!version"))
{
logger.info("!version caught.");
sendMessage(new BuildInformation().toString());
}
else if (text.startsWith("%"))
{
logger.info("% caught.");
// Ignore.
}
else if (StringsUtils.equalsAnyIgnoreCase(text, "!salut", "!bonjour", "!hello"))
{
logger.info("!salut caught.");
// Salutation command.
sendMessage(sender + ", bonjour \\o/");
}
else if (text.startsWith("!"))
{
logger.info("!??? caught.");
// Command unknown.
// Do not answer because it can be a command to another bot.
}
else
{
logger.info("All the other caught.");
// Topic message.
if (this.review == null)
{
if ((StringsUtils.containsAnyIgnoreCase(text, "bonjour", "salut", "hello")) && (StringUtils.containsIgnoreCase(text, "hebdobot")))
{
sendMessage(sender + ", bonjour \\o/");
}
}
else
{
this.review.add(new Message(sender, text));
}
}
}
}
/* (non-Javadoc)
* @see org.jibble.pircbot.PircBot#onPrivateMessage(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
*/
@Override
protected void onPrivateMessage(final String sender, final String login, final String hostname, final String message)
{
logger.debug("Private message received - sender : {}, message : {}", sender, message);
String text = message.trim();
if (StringUtils.equalsIgnoreCase(text, "!vaten"))
{
logger.info("!die caught.");
// Die.
if (this.review == null)
{
try
{
sendMessage(sender + ", ok bye.");
sendMessage(sender, "ok bye.");
Thread.sleep(1000);
System.exit(0);
}
catch (InterruptedException exception)
{
logger.warn("Pause abort: " + exception.getMessage());
}
}
else
{
sendMessage("% Une revue est en cours, abandon impossible.");
}
}
else if (text.startsWith("!"))
{
logger.info("!??? caught.");
// Command unknown.
sendMessage(sender + ", command unknown: " + text);
}
else
{
// Nothing to say.
}
}
/**
* Run.
*
* @throws HebdobotException
* the hebdobot exception
*/
public void run() throws HebdobotException
{
try
{
logger.info("Cron initializing.");
{
this.cronManager = new CronManager(this, this.cronSettings);
this.cronManager.start();
}
logger.info("Cron initialized.");
logger.info("Bot connection.");
this.connect(this.host, this.port);
logger.info("Bot connected.");
logger.info("Bot joining channel ({}).", this.channel);
this.joinChannel(this.channel);
logger.info("Bot ready.");
}
catch (NickAlreadyInUseException exception)
{
throw new HebdobotException(exception);
}
catch (IOException exception)
{
throw new HebdobotException(exception);
}
catch (IrcException exception)
{
throw new HebdobotException(exception);
}
catch (SchedulerException exception)
{
throw new HebdobotException("Error in cron settings.", exception);
}
}
/**
* Send message.
*
* @param message
* the message
*/
public void sendMessage(final String message)
{
logger.debug("Send message : {}", message);
this.sendMessage(this.channel, message);
if (this.review != null)
{
this.review.addRaw(new Message("Hebdobot", message));
}
}
/**
* Sets the home directory.
*
* @param homeDirectory
* the new home directory
*/
public void setHomeDirectory(final File homeDirectory)
{
this.homeDirectory = homeDirectory;
}
}