123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786 |
- // Big Screen Bot
- //
- // Douglas Thrift
- //
- // BigScreenBot.java
- /* Copyright 2011 Douglas Thrift
- *
- * This file is part of Big Screen Bot.
- *
- * Big Screen Bot 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.
- *
- * Big Screen Bot 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 Big Screen Bot. If not, see <http://www.gnu.org/licenses/>.
- */
- package net.douglasthrift.bigscreenbot;
- import java.io.IOException;
- import java.net.MalformedURLException;
- import java.net.URL;
- import java.security.GeneralSecurityException;
- import java.util.ArrayList;
- import java.util.HashSet;
- import java.util.List;
- import java.util.Map;
- import java.util.TreeMap;
- import java.util.Set;
- import java.util.regex.Pattern;
- import javax.net.ssl.KeyManager;
- import javax.net.ssl.SSLSocketFactory;
- import com.google.polo.ssl.DummySSLSocketFactory;
- import org.apache.commons.cli.CommandLine;
- import org.apache.commons.cli.GnuParser;
- import org.apache.commons.cli.HelpFormatter;
- import org.apache.commons.cli.Options;
- import org.apache.commons.cli.ParseException;
- import org.apache.commons.lang3.StringUtils;
- import org.jibble.pircbot.Colors;
- import org.jibble.pircbot.IrcException;
- public class BigScreenBot extends Bot
- {
- private static final int CHANNEL = 0x1;
- private static final int PRIVATE = 0x2;
- private static final int BOTH = 0x3;
- private static enum Action { RECONNECT, RESTART, QUIT }
- private abstract class Command
- {
- private boolean admin;
- private int access;
- private String arguments, description;
- protected Command(boolean admin, int access, String arguments, String description)
- {
- this.admin = admin;
- this.access = access;
- this.arguments = arguments;
- this.description = description;
- }
- public abstract void execute(String channel, String sender, boolean admin, String argument);
- public int getAccess()
- {
- return access;
- }
- public String getArguments()
- {
- return arguments;
- }
- public String getDescription()
- {
- return description;
- }
- public boolean isAdmin()
- {
- return admin;
- }
- public boolean isChannel()
- {
- return (access & CHANNEL) != 0;
- }
- public boolean isPrivate()
- {
- return (access & PRIVATE) != 0;
- }
- }
- private boolean verbose;
- private Remote remote;
- private Settings settings = new Settings();
- private Set<String> channels;
- private List<Pattern> admins = new ArrayList<Pattern>();
- private Map<String, Pattern> bans = new TreeMap<String, Pattern>();
- private Map<String, Command> commands = new TreeMap<String, Command>();
- private Action action = Action.RECONNECT;
- private BigScreenBot(boolean verbose)
- {
- super();
- this.verbose = verbose;
- try
- {
- settings.load();
- remote = new Remote(settings);
- }
- catch (IOException exception)
- {
- error(exception, 1);
- }
- setAutoNickChange(true);
- setFinger("Big Screen Bot");
- setMessageDelay(0);
- setVersion(String.format("Big Screen Bot (%1$s)", System.getProperty("os.name")));
- if (settings.getBooleanProperty("ssl", false))
- if (settings.getBooleanProperty("verify", true))
- setSocketFactory(SSLSocketFactory.getDefault());
- else
- try
- {
- setSocketFactory(DummySSLSocketFactory.fromKeyManagers(new KeyManager[] {}));
- }
- catch (GeneralSecurityException exception)
- {
- error(exception, 1);
- }
- setLogin(System.getProperty("user.name"));
- setName(settings.getProperty("nick", "bigscreenbot"));
- setVerbose(verbose);
- channels = new HashSet<String>(settings.getListProperty("channels", new ArrayList<String>()));
- for (String admin : settings.getListProperty("admins"))
- admins.add(compileNickMask(admin));
- for (String ban : settings.getListProperty("bans", new ArrayList<String>()))
- bans.put(ban, compileNickMask(ban));
- commands.put("ban", new Command(true, PRIVATE, "[mask...]", "block nick masks from using commands")
- {
- @Override
- public void execute(String channel, String sender, boolean admin, String argument)
- {
- String[] arguments = StringUtils.split(argument);
- if (arguments.length == 0)
- {
- listBans(channel, sender);
- return;
- }
- synchronized (bans)
- {
- for (String ban : arguments)
- if (bans.put(ban, compileNickMask(ban)) == null)
- sendMessage(channel, sender, String.format("banned nick mask (\"%1$s\")", ban));
- else
- sendMessage(channel, sender, String.format("nick mask (\"%1$s\") already banned", ban));
- storeBans(channel, sender);
- }
- }
- });
- commands.put("googletv", new Command(false, BOTH, "url [device]", "fling url to a Google TV device")
- {
- @Override
- public void execute(final String channel, final String sender, boolean admin, String argument)
- {
- final String[] arguments = StringUtils.split(argument, null, 2);
- if (arguments.length == 0)
- {
- help(channel, sender, admin, "googletv");
- return;
- }
- try
- {
- if (!new URL(arguments[0]).getProtocol().matches("^https?$"))
- {
- invalidURL(channel, sender, arguments[0]);
- return;
- }
- }
- catch (MalformedURLException exception)
- {
- invalidURL(channel, sender, arguments[0]);
- return;
- }
- if (arguments.length == 2)
- sendMessage(channel, sender, String.format("flinging URL (\"%1$s\") to device (\"%2$s\")...", arguments[0], arguments[1]));
- else
- sendMessage(channel, sender, String.format("flinging URL (\"%1$s\") to device(s)...", arguments[0]));
- new Thread()
- {
- @Override
- public void run()
- {
- synchronized (remote)
- {
- if (arguments.length == 2)
- remote.fling(arguments[1], arguments[0]);
- else
- remote.fling(arguments[0]);
- }
- sendMessage(channel, sender, String.format("flung URL (\"%1$s\") to device(s)", arguments[0]));
- }
- }.start();
- }
- private void invalidURL(String channel, String sender, String url)
- {
- sendMessage(channel, sender, String.format("invalid URL (\"%1$s\")", url));
- }
- });
- commands.put("help", new Command(false, PRIVATE, "[command]", "show this help message")
- {
- @Override
- public void execute(String channel, String sender, boolean admin, String arguments)
- {
- String argument = null;
- Command command = null;
-
- try
- {
- argument = StringUtils.split(arguments, null, 2)[0].toLowerCase();
- if (argument.startsWith("!"))
- argument = argument.substring(1);
- command = commands.get(argument);
- }
- catch (ArrayIndexOutOfBoundsException exception) {}
- sendMessage(channel, sender, Colors.BOLD + String.format("%1$-11s %2$-23s %3$-15s %4$s", "command", "arguments", "access", "description") + Colors.NORMAL);
- if (command != null)
- help(channel, sender, admin, argument, command);
- else
- for (Map.Entry<String, Command> nameCommand : commands.entrySet())
- help(channel, sender, admin, nameCommand.getKey(), nameCommand.getValue());
- }
- private void help(String channel, String sender, boolean admin, String name, Command command)
- {
- boolean unavailable = command.isAdmin() && !admin;
- String access;
- switch (command.getAccess())
- {
- case CHANNEL:
- access = "channel"; break;
- case PRIVATE:
- access = "private"; break;
- case BOTH: default:
- access = "channel/private"; break;
- }
- sendMessage(channel, sender, (unavailable ? Colors.UNDERLINE : "") + String.format("%1$-11s %2$-23s %3$-15s %4$s", name, command.getArguments(), access, command.getDescription()) + (unavailable ? Colors.NORMAL : ""));
- }
- });
- commands.put("join", new Command(true, PRIVATE, "channel", "join a channel")
- {
- @Override
- public void execute(String channel, String sender, boolean admin, String arguments)
- {
- String argument;
- try
- {
- argument = StringUtils.split(arguments, null, 2)[0];
- }
- catch (ArrayIndexOutOfBoundsException exception)
- {
- help(channel, sender, admin, "join");
- return;
- }
- joinChannel(argument);
- synchronized (channels)
- {
- channels.add(argument);
- storeChannels(channel, sender);
- }
- sendMessage(channel, sender, String.format("joined channel (\"%1$s\")", argument));
- }
- });
- commands.put("pair", new Command(true, PRIVATE, "[device [code]]", "pair with a Google TV device")
- {
- @Override
- public void execute(final String channel, final String sender, boolean admin, String argument)
- {
- final String[] arguments = StringUtils.split(argument, null, 2);
- switch (arguments.length)
- {
- case 0:
- sendMessage(channel, sender, "searching for devices to pair with...");
- new Thread()
- {
- @Override
- public void run()
- {
- List<String> devices;
- synchronized (remote)
- {
- devices = remote.listDevices();
- }
- if (devices.isEmpty())
- {
- sendMessage(channel, sender, "there are no devices to pair with");
- return;
- }
- sendMessage(channel, sender, Colors.BOLD + "devices" + Colors.NORMAL);
- for (String device : devices)
- sendMessage(channel, sender, device);
- }
- }.start();
- break;
- case 1:
- sendMessage(channel, sender, String.format("starting to pair with device (\"%1$s\")...", arguments[0]));
- new Thread()
- {
- @Override
- public void run()
- {
- synchronized (remote)
- {
- try
- {
- remote.startPairDevice(arguments[0]);
- }
- catch (GeneralSecurityException exception)
- {
- error(channel, sender, exception);
- }
- }
- sendMessage(channel, sender, String.format("enter the code from the device (\"%1$s\") to finish pairing", arguments[0]));
- }
- }.start();
- break;
- default:
- sendMessage(channel, sender, String.format("finishing pairing with device (\"%1$s\")...", arguments[0]));
- new Thread()
- {
- @Override
- public void run()
- {
- synchronized (remote)
- {
- remote.finishPairDevice(arguments[0], arguments[1]);
- }
- sendMessage(channel, sender, String.format("paired with device (\"%1$s\")", arguments[0]));
- }
- }.start();
- }
- }
- });
- commands.put("part", new Command(true, PRIVATE, "channel [message]", "part from a channel")
- {
- @Override
- public void execute(String channel, String sender, boolean admin, String argument)
- {
- String[] arguments = StringUtils.split(argument, null, 2);
- if (arguments.length == 0)
- {
- help(channel, sender, admin, "part");
- return;
- }
- if (arguments.length == 2)
- partChannel(arguments[0], arguments[1]);
- else
- partChannel(arguments[0]);
- synchronized (channels)
- {
- channels.remove(arguments[0]);
- storeChannels(channel, sender);
- }
- sendMessage(channel, sender, String.format("parted channel (\"%1$s\")", arguments[0]));
- }
- });
- commands.put("quit", new Command(true, PRIVATE, "[message]", "quit and do not come back")
- {
- @Override
- public void execute(String channel, String sender, boolean admin, String argument)
- {
- synchronized (action)
- {
- action = Action.QUIT;
- }
- quitServer(!argument.isEmpty() ? argument : "oh no!");
- }
- });
- commands.put("restart", new Command(true, PRIVATE, "", "quit and join running more up to date code")
- {
- @Override
- public void execute(String channel, String sender, boolean admin, String argument)
- {
- synchronized (action)
- {
- action = Action.RESTART;
- }
- quitServer("restarting");
- }
- });
- commands.put("say", new Command(true, PRIVATE, "nick|channel message", "say message to nick or channel")
- {
- @Override
- public void execute(String channel, String sender, boolean admin, String argument)
- {
- String[] arguments = StringUtils.split(argument, null, 2);
- if (arguments.length != 2)
- {
- help(channel, sender, admin, "say");
- return;
- }
- if (arguments[0].equalsIgnoreCase(getNick()))
- {
- sendMessage(channel, sender, "nice try");
- return;
- }
- sendMessage(arguments[0], arguments[1]);
- sendMessage(channel, sender, String.format("successfully sent message (\"%1$s\") to nick/channel (\"%2$s\")", arguments[1], arguments[0]));
- }
- });
- commands.put("unban", new Command(true, PRIVATE, "[mask...]", "allow blocked nick masks to use commands again")
- {
- @Override
- public void execute(String channel, String sender, boolean admin, String argument)
- {
- String[] arguments = StringUtils.split(argument);
- if (arguments.length == 0)
- {
- listBans(channel, sender);
- return;
- }
- synchronized (bans)
- {
- for (String ban : arguments)
- if (bans.remove(ban) != null)
- sendMessage(channel, sender, String.format("unbanned nick mask (\"%1$s\")", ban));
- else
- sendMessage(channel, sender, String.format("nick mask (\"%1$s\") already unbanned", ban));
- storeBans(channel, sender);
- }
- }
- });
- try
- {
- connect(settings.getProperty("server"), settings.getIntegerProperty("port", 6667));
- }
- catch (IOException exception)
- {
- error(exception, 1);
- }
- catch (IrcException exception)
- {
- error(exception, 1);
- }
- }
- public void sendMessage(String channel, String sender, String message)
- {
- sendMessage(channel != null ? channel : sender, message);
- }
- @Override
- protected void onConnect()
- {
- synchronized (channels)
- {
- for (String channel : channels)
- joinChannel(channel);
- }
- }
- @Override
- protected void onDisconnect()
- {
- synchronized (action)
- {
- switch (action)
- {
- case RESTART:
- dispose();
- System.exit(2);
- case QUIT:
- dispose();
- break;
- default:
- while (true)
- {
- try
- {
- reconnect();
- }
- catch (IOException exception)
- {
- continue;
- }
- catch (IrcException exception)
- {
- error(exception, 1);
- }
- break;
- }
- }
- }
- }
- @Override
- protected void onMessage(String channel, String sender, String login, String hostname, String message)
- {
- doCommandFromMessage(channel, sender, login, hostname, message);
- }
- @Override
- protected void onPrivateMessage(String sender, String login, String hostname, String message)
- {
- doCommandFromMessage(null, sender, login, hostname, message);
- }
- private void doCommandFromMessage(String channel, String sender, String login, String hostname, String message)
- {
- boolean admin = matchNickMasks(sender, login, hostname, admins);
- synchronized (bans)
- {
- if (!admin && (matchNickMasks(sender, login, hostname, bans.values()) || !isNickInChannels(sender)))
- return;
- }
- message = Colors.removeFormattingAndColors(message);
- String[] arguments = StringUtils.split(message, null, 2);
- String argument = "";
- Command command = null;
- if (arguments.length != 0)
- {
- if (channel == null)
- try
- {
- if (new URL(arguments[0]).getProtocol().matches("^https?$"))
- {
- commands.get("googletv").execute(channel, sender, admin, StringUtils.join(arguments, ' '));
- return;
- }
- }
- catch (MalformedURLException exception) {}
- argument = arguments[0].toLowerCase();
- if (argument.startsWith("!"))
- argument = argument.substring(1);
- command = commands.get(argument);
- }
- if (channel != null)
- {
- if (command == null)
- return;
- if (!admin && command.isAdmin())
- return;
- if (!command.isChannel())
- return;
- }
- else
- {
- if (command == null)
- {
- sendMessage(channel, sender, String.format("unknown command (\"%1$s\"); try \"help\"", argument));
- return;
- }
- if (!admin && command.isAdmin())
- {
- sendMessage(channel, sender, String.format("unauthorized command (\"%1$s\")", argument));
- return;
- }
- if (!command.isPrivate())
- {
- sendMessage(channel, sender, String.format("inappropriate command (\"%1$s\")", argument));
- return;
- }
- }
- command.execute(channel, sender, admin, arguments.length == 2 ? arguments[1] : "");
- }
- private void error(Exception exception)
- {
- if (verbose)
- exception.printStackTrace();
- else
- System.err.println("bigscreenbot: " + exception.getMessage());
- }
- private void error(Exception exception, int code)
- {
- error(exception);
- System.exit(code);
- }
- private void error(String channel, String sender, Exception exception)
- {
- error(exception);
- sendMessage(channel, sender, "an error occurred");
- }
- private void help(String channel, String sender, boolean admin, String command)
- {
- commands.get("help").execute(channel, sender, admin, command);
- }
- private void listBans(String channel, String sender)
- {
- synchronized (bans)
- {
- if (bans.isEmpty())
- {
- sendMessage(channel, sender, "there are no bans");
- return;
- }
- sendMessage(channel, sender, Colors.BOLD + "ban" + Colors.NORMAL);
- for (String ban : bans.keySet())
- sendMessage(channel, sender, ban);
- }
- }
- private void storeBans(String channel, String sender)
- {
- synchronized (bans)
- {
- synchronized (settings)
- {
- settings.setListProperty("bans", bans.keySet());
- try
- {
- settings.store();
- }
- catch (IOException exception)
- {
- error(channel, sender, exception);
- }
- }
- }
- }
- private void storeChannels(String channel, String sender)
- {
- synchronized (channels)
- {
- synchronized (settings)
- {
- settings.setListProperty("channels", channels);
- try
- {
- settings.store();
- }
- catch (IOException exception)
- {
- error(channel, sender, exception);
- }
- }
- }
- }
- public static void main(String[] args)
- {
- Options options = new Options();
- options.addOption("h", "help", false, "show this help message and exit");
- options.addOption("v", "verbose", false, "");
- CommandLine line = null;
- try
- {
- line = new GnuParser().parse(options, args);
- }
- catch (ParseException exception)
- {
- System.err.println("bigscreenbot: " + exception.getMessage());
- }
- if (line.hasOption('h'))
- {
- new HelpFormatter().printHelp("bigscreenbot", options, true);
- return;
- }
- new BigScreenBot(line.hasOption('v'));
- }
- }
- // vim: expandtab
|