478 lines
No EOL
17 KiB
Java
478 lines
No EOL
17 KiB
Java
package main;
|
|
|
|
import com.mashape.unirest.http.HttpResponse;
|
|
import com.mashape.unirest.http.JsonNode;
|
|
import com.mashape.unirest.http.Unirest;
|
|
import com.mashape.unirest.http.exceptions.UnirestException;
|
|
import elements.Chat;
|
|
import elements.Persistence;
|
|
import elements.exceptions.*;
|
|
import elements.messages.ChannelMessage;
|
|
import elements.messages.GroupMessage;
|
|
import elements.messages.Message;
|
|
import elements.messages.PrivateMessage;
|
|
import org.json.JSONArray;
|
|
import org.json.JSONObject;
|
|
|
|
import java.time.LocalDate;
|
|
import java.util.*;
|
|
|
|
/**
|
|
* Created by Guillermo Serrahima on 4/1/16.
|
|
*/
|
|
|
|
public class Geiserbot {
|
|
private static final String TOKEN = "267229954:AAHx49MXLmT1nT0QkccrSIzgmRVCQbjbJaQ";
|
|
private static final String USERNAME = "gserbot";
|
|
private static final String BASE_URL = "https://api.telegram.org/bot" + TOKEN;
|
|
private static final String PARSE_MARKDOWN = "Markdown";
|
|
private static final String PARSE_HTML = "HTML";
|
|
|
|
private static final int WARNING_COUNT_START = 0;
|
|
private static final int START_MESSAGE = WARNING_COUNT_START;
|
|
private static final int HELP_MESSAGE = START_MESSAGE + 1;
|
|
private static final int WARNING_UNKNOWN_ERROR = HELP_MESSAGE + 1;
|
|
private static final int WARNING_NO_USERNAME = WARNING_UNKNOWN_ERROR + 1;
|
|
private static final int WARNING_NOT_IMPLEMENTED = WARNING_NO_USERNAME + 1;
|
|
private static final int WARNING_NO_GROUP = WARNING_NOT_IMPLEMENTED + 1;
|
|
private static final int WARNING_COUNT_END = WARNING_NO_GROUP + 1;
|
|
|
|
//private List<String> commands;
|
|
private Map<Long, Chat> chats;
|
|
|
|
public Geiserbot() throws ReadErrorListException, ReadErrorChatException {
|
|
chats = Persistence.getInstance().readChats();
|
|
}
|
|
|
|
public String getUsername() {
|
|
return USERNAME;
|
|
}
|
|
|
|
public String getToken() {
|
|
return TOKEN;
|
|
}
|
|
|
|
public String myName() {
|
|
return "R. Geiser Pont";
|
|
}
|
|
|
|
//--- Http handling
|
|
|
|
public HttpResponse<JsonNode> sendMessage(Long chatId, String text) throws UnirestException {
|
|
System.out.println("Sending message: \n" + text + "\n");
|
|
|
|
return Unirest.post(BASE_URL + "/sendMessage")
|
|
.field("chat_id", chatId)
|
|
.field("text", text)
|
|
.asJson();
|
|
}
|
|
|
|
public HttpResponse<JsonNode> sendMessage(Long chatId, String text, String parseMode) throws UnirestException {
|
|
System.out.println("Sending message: \n" + text + "\n");
|
|
|
|
return Unirest.post(BASE_URL + "/sendMessage")
|
|
.field("chat_id", chatId)
|
|
.field("text", text)
|
|
.field("parse_mode", parseMode)
|
|
.asJson();
|
|
}
|
|
|
|
public HttpResponse<JsonNode> sendMessage(String channel, String text) throws UnirestException {
|
|
System.out.println("Sending message to " + channel + ": \n" + text + "\n");
|
|
|
|
return Unirest.post(BASE_URL + "/sendMessage")
|
|
.field("chat_id", channel)
|
|
.field("text", text)
|
|
.asJson();
|
|
}
|
|
|
|
public HttpResponse<JsonNode> sendMessage(String channel, String text, String parseMode) throws UnirestException {
|
|
System.out.println("Sending message to " + channel + ": \n" + text + "\n");
|
|
|
|
return Unirest.post(BASE_URL + "/sendMessage")
|
|
.field("chat_id", channel)
|
|
.field("text", text)
|
|
.field("parse_mode", parseMode)
|
|
.asJson();
|
|
}
|
|
|
|
public HttpResponse<JsonNode> replyMessage(Long chatId, Integer repliedMessage, String text) throws UnirestException {
|
|
System.out.println("Sending message: \n" + text + "\n");
|
|
|
|
return Unirest.post(BASE_URL + "/sendMessage")
|
|
.field("chat_id", chatId)
|
|
.field("text", text)
|
|
.field("reply_to_message_id", repliedMessage)
|
|
.asJson();
|
|
}
|
|
|
|
public HttpResponse<JsonNode> getUpdates(Integer offset) throws UnirestException {
|
|
return Unirest.post(BASE_URL + "/getUpdates")
|
|
.field("offset", offset)
|
|
.asJson();
|
|
}
|
|
|
|
public HttpResponse<JsonNode> getUpdates(Integer offset, Integer timeout) throws UnirestException {
|
|
return Unirest.post(BASE_URL + "/getUpdates")
|
|
.field("offset", offset)
|
|
.field("timeout", timeout)
|
|
.asJson();
|
|
}
|
|
|
|
//--- bot thinking
|
|
|
|
public void run() throws UnirestException {
|
|
int last_upd_id = 0;
|
|
HttpResponse<JsonNode> response;
|
|
System.out.println("Listening.\n");
|
|
|
|
while (true) {
|
|
response = getUpdates(last_upd_id++);
|
|
if (response.getStatus() == 200) {
|
|
JSONArray responses = response.getBody().getObject().getJSONArray("result");
|
|
if (responses.isNull(0)) continue;
|
|
else last_upd_id = responses.getJSONObject(responses.length() - 1).getInt("update_id") + 1;
|
|
|
|
for (int i = 0; i < responses.length(); i++) {
|
|
if(responses.getJSONObject(i).has("message")) {
|
|
JSONObject message = responses.getJSONObject(i).getJSONObject("message");
|
|
|
|
processMessage(message);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void processMessage(JSONObject message) throws UnirestException {
|
|
//Process message into a nice and cozy java object
|
|
Message m = null;
|
|
try {
|
|
m = Message.getMessage(message);
|
|
} catch (MessageException e) {
|
|
System.out.println(e.getMessage());
|
|
e.printStackTrace();
|
|
}
|
|
|
|
//Console log
|
|
printLog(true, m);
|
|
|
|
//Actually read the message
|
|
handleInput(m);
|
|
}
|
|
|
|
private void printLog(boolean full_log, Message m) {
|
|
if (full_log) System.out.println("\n" + m);
|
|
else {
|
|
String log = "\nMessage";
|
|
|
|
if(m.isPrivate()) log += " received from a private chat";
|
|
else log += " received from chat " + m.getChatName();
|
|
|
|
log += " (chat id: " + m.getCid() + ")";
|
|
try {
|
|
log += " by user @" + m.getUsername();
|
|
} catch (NoUsernameMessageException e) {
|
|
log += " by user";
|
|
} catch (NoAuthorMessageException ignored) {}
|
|
|
|
try {
|
|
log += " named " + m.getAuthor();
|
|
} catch (NoAuthorMessageException ignored) {}
|
|
|
|
System.out.println(log + ".");
|
|
}
|
|
}
|
|
|
|
//--- Command handling
|
|
|
|
private void handleInput(Message m) throws UnirestException {
|
|
if (m.isChannel())
|
|
handleInputChannel((ChannelMessage) m);
|
|
|
|
else if (m.isGroup())
|
|
handleInputGroup((GroupMessage) m);
|
|
|
|
else if (m.isPrivate())
|
|
handleInputPrivate((PrivateMessage) m);
|
|
}
|
|
|
|
private boolean isCommand(String m, String command) {
|
|
return (m.startsWith(command) &&
|
|
(!m.substring(command.length()).startsWith("@") ||
|
|
m.substring(command.length()).startsWith("@" + USERNAME)));
|
|
}
|
|
|
|
private String removeCommandHead(String m, String command) {
|
|
String m2 = m.substring(command.length()).trim();
|
|
String uname = "@" + USERNAME;
|
|
if(m2.startsWith(uname)) m2 = m2.substring(uname.length());
|
|
|
|
return m2;
|
|
}
|
|
|
|
private void handleInputGroup(GroupMessage m) throws UnirestException {
|
|
try {
|
|
if (m.isEvent()) {
|
|
System.out.println("\nDetected group event.");
|
|
handleGroupEvent(m);
|
|
} else if (isCommand(m.getText(), "/start"))
|
|
handleStart(m.getCid());
|
|
|
|
else if (isCommand(m.getText(), "/help"))
|
|
handleHelp(m.getCid());
|
|
|
|
else if (isCommand(m.getText(), "/whoami"))
|
|
handleWhoami(m);
|
|
|
|
else if (isCommand(m.getText(), "/list"))
|
|
handleList(m);
|
|
|
|
else if (isCommand(m.getText(), "/foldl")) {
|
|
if(removeCommandHead(m.getText(), "/foldl").startsWith("-v"))
|
|
explainFoldHaskellVerbose(m.getCid());
|
|
else explainFoldHaskell(m.getCid());
|
|
}
|
|
|
|
else if (isCommand(m.getText(), "/foldr")) {
|
|
if(removeCommandHead(m.getText(), "/foldr").startsWith("-v"))
|
|
explainFoldHaskellVerbose(m.getCid());
|
|
else explainFoldHaskell(m.getCid());
|
|
}
|
|
} catch (NoTextMessageException ignored) {}
|
|
}
|
|
|
|
private void handleInputPrivate(PrivateMessage m) throws UnirestException {
|
|
try {
|
|
if (isCommand(m.getText(), "/start"))
|
|
handleStart(m.getCid());
|
|
|
|
else if (isCommand(m.getText(), "/help"))
|
|
handleHelp(m.getCid());
|
|
|
|
else if (isCommand(m.getText(), "/whoami"))
|
|
handleWhoami(m);
|
|
|
|
else if (isCommand(m.getText(), "/list"))
|
|
handleList(m);
|
|
|
|
else if (isCommand(m.getText(), "/foldl")) {
|
|
if(removeCommandHead(m.getText(), "/foldl").startsWith("-v"))
|
|
explainFoldHaskellVerbose(m.getCid());
|
|
else explainFoldHaskell(m.getCid());
|
|
}
|
|
|
|
else if (isCommand(m.getText(), "/foldr")) {
|
|
if(removeCommandHead(m.getText(), "/foldr").startsWith("-v"))
|
|
explainFoldHaskellVerbose(m.getCid());
|
|
else explainFoldHaskell(m.getCid());
|
|
}
|
|
} catch (NoTextMessageException ignored) {}
|
|
}
|
|
|
|
private void handleInputChannel(ChannelMessage m) throws UnirestException {
|
|
}
|
|
|
|
private void handleStart(long chat_id) throws UnirestException {
|
|
sendMessage(chat_id, "Hola. Soy el bot personal de Guillermo Serrahima.");
|
|
return;
|
|
}
|
|
|
|
private void handleHelp(long chat_id) throws UnirestException {
|
|
String help = "Aquí tienes una lista de comandos que acepto:\n\n" +
|
|
"/start - Repite el saludo inicial\n" +
|
|
"/help - Saca esta lista\n" +
|
|
"/whoami - Muestra información visible sobre ti mismo\n" +
|
|
"/list [nombre de la lista] - Permite crear, mostrar o " +
|
|
"modificar una lista\n";
|
|
|
|
sendMessage(chat_id, help.trim());
|
|
sendMessage(chat_id, "_" + myName() + " v1.0.0_", PARSE_MARKDOWN);
|
|
}
|
|
|
|
private void handleWhoami(Message m) throws UnirestException {
|
|
String s = "Estamos en";
|
|
|
|
if(m.isPrivate()) s += " un chat privado";
|
|
else s += " el chat " + m.getChatName();
|
|
|
|
s += " de ID " + m.getCid() + ".\n";
|
|
|
|
try {
|
|
s += "Eres " + m.getAuthor() + ", con";
|
|
try {
|
|
s += " nombre de usuario @" + m.getUsername() +" e";
|
|
} catch (NoUsernameMessageException ignored) {}
|
|
|
|
s += " ID de usuario " + m.getUid() + ".\n";
|
|
} catch (NoAuthorMessageException ignored) {}
|
|
|
|
s += "En el PC que me mantiene, la fecha actual es " + LocalDate.now() +
|
|
" en el formato ISO-8601.\n";
|
|
|
|
s+= "Tu mensaje tiene hora de " + m.getTimestamp() + "\n";
|
|
|
|
replyMessage(m.getCid(), m.getMid(), s.trim());
|
|
}
|
|
|
|
private void handleGroupEvent(GroupMessage m) throws NoTextMessageException, UnirestException {
|
|
System.out.print("Bot @" + USERNAME + " detects event: ");
|
|
if(m.userJoined()) {
|
|
System.out.println("JOINING");
|
|
System.out.println("New chat participant: " + m.getText());
|
|
if(m.getText().equals("@" + USERNAME)) handleStart(m.getCid());
|
|
}
|
|
else if(m.userLeft()) {
|
|
System.out.println("LEAVING");
|
|
System.out.println("Leaving chat participant: " + m.getText());
|
|
}
|
|
else if(m.chatNameChanged()) {
|
|
System.out.println("CHANGING CHAT NAME");
|
|
System.out.println("New chat name: " + m.getText());
|
|
|
|
try {
|
|
chats.get(m.getCid()).setName(m.getText());
|
|
saveChatIndex();
|
|
} catch (SaveErrorChatException e) {
|
|
sendMessage(m.getCid(), "Ha habido un error guardando el nuevo nombre de este chat.");
|
|
}
|
|
}
|
|
else {
|
|
System.out.println("UNKNOWN");
|
|
}
|
|
}
|
|
|
|
private void handleList(GroupMessage m) throws UnirestException, NoTextMessageException {
|
|
String[] reading = m.getText().split(" ", 2);
|
|
if (reading.length < 2) {
|
|
sendMessage(m.getCid(), "Formato incorrecto. " +
|
|
"Debes poner un espacio entre el comando /list y el nombre de la lista.");
|
|
return;
|
|
}
|
|
String list = reading[1];
|
|
|
|
if(!exists(m.getCid())) {
|
|
try {
|
|
chats.put(m.getCid(), new Chat(m.getChatName(), m.getCid()));
|
|
saveChatIndex();
|
|
} catch (SaveErrorChatException e) {
|
|
sendMessage(m.getCid(), "Ha habido un error guardando los datos de este chat.");
|
|
return;
|
|
}
|
|
}
|
|
if(!exists(m.getCid(), list)) {
|
|
try {
|
|
chats.get(m.getCid()).add(list);
|
|
saveChat(m.getCid());
|
|
} catch (SaveErrorChatException e) {
|
|
sendMessage(m.getCid(), "Ha habido un error creando la lista.");
|
|
return;
|
|
}
|
|
}
|
|
|
|
sendMessage(m.getCid(), getWarning(WARNING_NOT_IMPLEMENTED));
|
|
}
|
|
|
|
private void handleList(PrivateMessage m) throws UnirestException, NoTextMessageException {
|
|
String[] reading = m.getText().split(" ", 2);
|
|
if(reading.length < 2) {
|
|
sendMessage(m.getCid(), "Formato incorrecto. " +
|
|
"Debes poner un espacio entre el comando /list y el nombre de la lista.");
|
|
return;
|
|
}
|
|
String list = reading[1];
|
|
|
|
if(!exists(m.getCid())) {
|
|
try {
|
|
chats.put(m.getCid(), new Chat(m.getChatName(), m.getCid()));
|
|
saveChatIndex();
|
|
} catch (SaveErrorChatException e) {
|
|
sendMessage(m.getCid(), "Ha habido un error guardando los datos de este chat.");
|
|
return;
|
|
}
|
|
}
|
|
if(!exists(m.getCid(), list)) {
|
|
try {
|
|
chats.get(m.getCid()).add(list);
|
|
saveChat(m.getCid());
|
|
} catch (SaveErrorChatException e) {
|
|
sendMessage(m.getCid(), "Ha habido un error creando la lista.");
|
|
return;
|
|
}
|
|
}
|
|
|
|
sendMessage(m.getCid(), getWarning(WARNING_NOT_IMPLEMENTED));
|
|
}
|
|
|
|
private void explainFoldHaskell(long chat_id) throws UnirestException {
|
|
|
|
|
|
String fold = "The recursion for `foldr f x ys` where `ys = [y1,y2,...,yk]` looks like\n" +
|
|
"\n" +
|
|
"`f y1 (f y2 (... (f yk x) ...))`\n" +
|
|
"\n" +
|
|
"whereas the recursion for `foldl f x ys` looks like\n" +
|
|
"\n" +
|
|
"`f (... (f (f x y1) y2) ...) yk`";
|
|
sendMessage(chat_id, fold, PARSE_MARKDOWN);
|
|
}
|
|
|
|
private void explainFoldHaskellVerbose(long chat_id) throws UnirestException {
|
|
String fold = "The recursion for `foldr f x ys` where `ys = [y1,y2,...,yk]` looks like\n" +
|
|
"\n" +
|
|
"`f y1 (f y2 (... (f yk x) ...))`\n" +
|
|
"\n" +
|
|
"whereas the recursion for `foldl f x ys` looks like\n" +
|
|
"\n" +
|
|
"`f (... (f (f x y1) y2) ...) yk`\n" +
|
|
"\n" +
|
|
"An important difference here is that if the result of `f x y` can be computed using only the value of `x`, then `foldr` doesn't need to examine the entire list. For example:\n" +
|
|
"\n" +
|
|
"`foldr (&&) False (repeat False)`\n" +
|
|
"returns `False` whereas\n" +
|
|
"`foldl (&&) False (repeat False)`\n" +
|
|
"never terminates. (Note: `repeat False` creates an infinite list where every element is `False`.)\n" +
|
|
"\n" +
|
|
"On the other hand, `foldl'` is tail recursive and strict. If you know that you'll have to traverse the whole list no matter what (e.g., summing the numbers in a list), then `foldl'` is more space- (and probably time-) efficient than `foldr`.";
|
|
sendMessage(chat_id, fold, PARSE_MARKDOWN);
|
|
}
|
|
|
|
private boolean exists(long chat_id) {
|
|
return chats.containsKey(chat_id);
|
|
}
|
|
|
|
private boolean exists(long chat_id, String list) {
|
|
return chats.containsKey(chat_id) && chats.get(chat_id).exists(list);
|
|
}
|
|
|
|
private void saveChat(long chat_id) throws SaveErrorChatException {
|
|
Persistence.getInstance().saveChat(chats.get(chat_id));
|
|
}
|
|
|
|
private void saveChatIndex() throws SaveErrorChatException {
|
|
Persistence.getInstance().saveChatIndex(chats);
|
|
}
|
|
|
|
private String getWarning(int warning_id) {
|
|
switch (warning_id) {
|
|
case WARNING_UNKNOWN_ERROR:
|
|
return "Algo ha ido MUY mal.";
|
|
|
|
case WARNING_NOT_IMPLEMENTED:
|
|
return "Aún no está implementado.";
|
|
|
|
case WARNING_NO_GROUP:
|
|
return "Debes estar en un grupo para poder hacer eso.";
|
|
|
|
case WARNING_NO_USERNAME:
|
|
return "No tienes nombre de usuario. Regístrate con uno en la " +
|
|
"configuración de Telegram para poder utilizarme.";
|
|
|
|
default: return "Entrada incorrecta.";
|
|
}
|
|
}
|
|
|
|
private String getFormatExample(int command) {
|
|
return "Entrada incorrecta.";
|
|
}
|
|
} |