Jabber-бот на базе Яндекса
Некоторое время назад написал я программу-бота, которой можно управлять через XMPP-протокол (Jabber). Были у нее кое-какие-недостатки, из которых главный — требование запущенного на том же хосте Jabber-сервера.
Оценив ситуацию свежим взглядом решил, что держать свой сервер вовсе необязательно: кругом полно бесплатных. Сначала «переселил» бота на GoogleTalk. Всё прекрасно, управлять можно и из браузера, и из любого IM-клиента с поддержкой Jabber. А вот с мобильного телефона нельзя. Ну нет у Google нормального клиента для сервиса GTalk. Ну что-ж, тогда Яндекс. У этих ребят мобильный клиент давно написан: им можно и почту посмотреть, и пообЧАТься. Кроме того, из браузера чат тоже можно вести.
Сам бот тоже поумнел. Теперь он не только выполняет простые bash-команды, но и интерпретирует синтаксические конструкции на языках Java/Groovy, а также… переключает телевизионные каналы (да, такая вот Jabber-«лентяйка» для телевизора ).
От злоумышленников бот защищен тем, что признает только команды, переданные с определенного аккаунта: чтобы управлять им, нужно сначала захватить мою учетную запись на Яндексе.
Привожу основную часть кода. Остальное — по запросу, если кто заинтересуется.
Оценив ситуацию свежим взглядом решил, что держать свой сервер вовсе необязательно: кругом полно бесплатных. Сначала «переселил» бота на GoogleTalk. Всё прекрасно, управлять можно и из браузера, и из любого IM-клиента с поддержкой Jabber. А вот с мобильного телефона нельзя. Ну нет у Google нормального клиента для сервиса GTalk. Ну что-ж, тогда Яндекс. У этих ребят мобильный клиент давно написан: им можно и почту посмотреть, и пообЧАТься. Кроме того, из браузера чат тоже можно вести.
Сам бот тоже поумнел. Теперь он не только выполняет простые bash-команды, но и интерпретирует синтаксические конструкции на языках Java/Groovy, а также… переключает телевизионные каналы (да, такая вот Jabber-«лентяйка» для телевизора ).
От злоумышленников бот защищен тем, что признает только команды, переданные с определенного аккаунта: чтобы управлять им, нужно сначала захватить мою учетную запись на Яндексе.
Привожу основную часть кода. Остальное — по запросу, если кто заинтересуется.
package ru.yababay.chat.server;
import java.io.File;
import java.io.FileInputStream;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.Properties;
import java.util.HashMap;
import org.jivesoftware.smack.Chat;
import org.jivesoftware.smack.ChatManager;
import org.jivesoftware.smack.ChatManagerListener;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.MessageListener;
import org.jivesoftware.smack.SASLAuthentication;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Message;
import net.sf.lab3f.groovy.Groovyable;
import net.sf.lab3f.util.TuttiFruttiable;
public class Main implements ChatManagerListener, MessageListener {
private XMPPConnection connection;
private Properties props = new Properties();
private ChatManagerListener chatMan;
private boolean hasGreeting;
private Groovyable groovy;
private Object groovyObj;
private TuttiFruttiable tuttiFrutti;
private HashMap <String, java.lang.reflect.Method> methods = new HashMap <String, java.lang.reflect.Method> ();
private final String row = "\n------------------\n";
public void processMessage(Chat cht, Message msg){
try{
String s = msg.getBody().trim();
int n = s.indexOf('\n');
if(n > 0 && s.substring(0, n).indexOf("groovy") > -1){
s = s.substring(n).trim();
cht.sendMessage('\n' + props.getProperty("botReplay1") + row + groovy.eval(s).toString() + row + props.getProperty("botReplay2"));
return;
}
n = s.indexOf(':');
if(n > 0 && methods.get(s.substring(0, n).trim()) != null){
Method m = methods.get(s.substring(0, n).trim());
Object obj = m.invoke(groovyObj, s.substring(n + 1).trim());
cht.sendMessage('\n' + props.getProperty("botReplay1") + row + obj.toString().trim() + row + props.getProperty("botReplay2"));
return;
}
else cht.sendMessage(props.getProperty("botReplayr3"));
cht.sendMessage(props.getProperty("botReplay2"));
}
catch(Exception ex){System.out.println(ex);}
}
public void chatCreated(Chat cht, boolean local){
String s = cht.getParticipant();
System.out.println(s);
if(s.indexOf('/') > 0)s = s.substring(0, s.lastIndexOf('/'));
if(!s.equals(props.getProperty("adminJidYa")))return;
cht.addMessageListener(this);
try{
if(!hasGreeting){cht.sendMessage(props.getProperty("botGreeting"));hasGreeting = true;}
}
catch(XMPPException ex){}
}
public final void start() throws Exception {
props.load(new FileInputStream("conf/jabberbot.properties"));
groovyObj = groovy.eval(new File(props.getProperty("path2Script")));
Class grClass = groovyObj.getClass();
Method[] meths = grClass.getMethods();
for(Method m : meths)methods.put(m.getName(), m);
chatMan = this;
ping.start();
hasGreeting = false;
System.out.println("[INFO] Jabber bot started.");
}
public final void stop() throws Exception {
ping.interrupt();
if(connection != null)connection.disconnect();
}
private class YandexTalkConnection extends XMPPConnection {
public YandexTalkConnection() throws XMPPException {
super(new ConnectionConfiguration("xmpp.yandex.ru", 5222, "ya.ru"));
}
}
private class GoogleTalkConnection extends XMPPConnection {
public GoogleTalkConnection() throws XMPPException {
super(new ConnectionConfiguration("talk.google.com", 5222, "gmail.com"));
}
}
private Thread ping = new Thread(new Runnable(){
public void run(){
while(true){
try{
if(connection != null && connection.isConnected()){Thread.sleep(300000);return;}
connection = new YandexTalkConnection();
// System.setProperty("smack.debugEnabled", "true");
// connection.DEBUG_ENABLED = true;
SASLAuthentication.supportSASLMechanism("PLAIN");
connection.connect();
connection.login(props.getProperty("botJidYa"), props.getProperty("botPasswdYa"), "");
ChatManager chMan = connection.getChatManager();
chMan.addChatListener(chatMan);
}
catch(Exception ex){}
}
}
});
}
- —
- 12 июля 2011, 14:57
Комментарии (19)
RSS свернуть / развернутьSergei_T
Sergei_T
yababay
Sergei_T
illuthion
А вообще начинал писать его на чистом ruby но в итоге начались странные проблемы с тем, что не все сервера принимали отправленные сообщения…
illuthion
XMPP-протокол довольно прост и понятен. Достаточно установить отладочную консоль в Pidgin или Psi — и детали общения клиента и сервера станут ясны. Но есть одно но: TLS-аутентификация. Вот она-то и не позволяет, например, с легкостью написать простое мобильное приложение для общения с тем-же Jabber-ботом. Есть, например, мобильный клиент MGtalk. Заглянул в исходный код — там ужас. Да и яндексовский мобильный клиент работает очень нестабильно: постоянно переустанавливает соединение и даже отправляет телефон в аут (внезапно экран гаснет и телефон выключается).
yababay
Но писал действительно на bash, уж больно он мне нравится своей простотой и адекватностью
illuthion
Sergei_T
illuthion
yababay
Теория:
Для работы бот использует freetalk и sendxmpp, соответственно и первое и второе должно быть настроено на JID бота. freetalk запускается перед запуском бота.
Бот(inc) запускается с 2 параметрами: JID бота и JID администратора(того от кого будет принимать команды).
После запуска идет бесконечный цикл на чтение последней стройки из истории freetalk и пределение новая это строка или нет. Если строка новая то JID и содержание передаются на управление другому скрипту(out) который отрезает от содержания команду а все остальное использует как параметры(было нужно для создания заметок и управления торрентами). Собственно потом команда уходит в case и идет выборка действия, ответ отсылается пользователю по sendxmpp. Разделение на 2 скрипта было сделано для того что-бы можно было править список команд не перезапуская бот. В принципе ничто не мешает заставить бота отсылать присланное сообщение в консоль и возвращать результат.
Код:
inc
out
Заключение:
Минусов как видите масса, например демонизировать можно только через screen, думал довести до ума, но не нашел чем можно заменить freetalk, если только мисать отдельный скрипт на ruby/perl но тогда самого бота будет выгоднее переписать. В общем кому интересно можете покопать)
illuthion
Спасибо, я для себя парочку идей почерпнул. Такой камент просится в отдельный топик.
yababay
illuthion
Sergei_T
illuthion
Sergei_T
yababay
yababay
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.