From 5af964e05a79e64f02ebcfb3e1b19f109e32deaf Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Fri, 18 Nov 2016 09:47:24 +0300 Subject: juick-api: Telegram bot refactoring --- juick-api/src/main/java/com/juick/api/Main.java | 4 +- juick-api/src/main/java/com/juick/api/TGBot.java | 231 +++++++++++++++++++++ .../main/java/com/juick/api/TelegramBotHook.java | 221 -------------------- 3 files changed, 233 insertions(+), 223 deletions(-) create mode 100644 juick-api/src/main/java/com/juick/api/TGBot.java delete mode 100644 juick-api/src/main/java/com/juick/api/TelegramBotHook.java diff --git a/juick-api/src/main/java/com/juick/api/Main.java b/juick-api/src/main/java/com/juick/api/Main.java index d5851c12..6d7deeac 100644 --- a/juick-api/src/main/java/com/juick/api/Main.java +++ b/juick-api/src/main/java/com/juick/api/Main.java @@ -71,7 +71,7 @@ public class Main extends HttpServlet { Others others; Subscriptions subscriptions; Notifications notifications; - TelegramBotHook tgb; + TGBot tgb; SkypeEndpoint sep; String tmpDir, imgDir; @@ -92,7 +92,7 @@ public class Main extends HttpServlet { others = new Others(jdbc); subscriptions = new Subscriptions(jdbc); notifications = new Notifications(jdbc); - tgb = new TelegramBotHook(jdbc, conf.getProperty("telegram_token", "")); + tgb = new TGBot(jdbc, conf.getProperty("telegram_token", "")); sep = new SkypeEndpoint(); setupXmppComponent(conf.getProperty("xmpp_host", "localhost"), Integer.parseInt(conf.getProperty("xmpp_port", "5347")), conf.getProperty("xmpp_jid", "api.localhost"), conf.getProperty("xmpp_password")); diff --git a/juick-api/src/main/java/com/juick/api/TGBot.java b/juick-api/src/main/java/com/juick/api/TGBot.java new file mode 100644 index 00000000..9f573d34 --- /dev/null +++ b/juick-api/src/main/java/com/juick/api/TGBot.java @@ -0,0 +1,231 @@ +package com.juick.api; + +import com.juick.User; +import com.juick.json.MessageSerializer; +import com.juick.server.MessagesQueries; +import com.juick.server.UserQueries; +import com.juick.server.protocol.JuickProtocol; +import com.neovisionaries.ws.client.*; +import com.pengrad.telegrambot.BotUtils; +import com.pengrad.telegrambot.Callback; +import com.pengrad.telegrambot.TelegramBot; +import com.pengrad.telegrambot.TelegramBotAdapter; +import com.pengrad.telegrambot.model.Message; +import com.pengrad.telegrambot.model.Update; +import com.pengrad.telegrambot.model.request.InlineKeyboardButton; +import com.pengrad.telegrambot.model.request.InlineKeyboardMarkup; +import com.pengrad.telegrambot.request.SendMessage; +import com.pengrad.telegrambot.response.SendResponse; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; + +import javax.servlet.http.HttpServletRequest; +import java.io.BufferedReader; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import static com.juick.formatters.PlainTextFormatter.formatPost; +import static com.juick.formatters.PlainTextFormatter.formatUrl; + +/** + * Created by vt on 12/05/16. + */ +public class TGBot implements AutoCloseable { + private static final Logger logger = LoggerFactory.getLogger(TGBot.class); + + TelegramBot bot; + WebSocket ws; + MessageSerializer ms = new MessageSerializer(); + JdbcTemplate jdbc; + JuickProtocol protocol; + + + public TGBot(JdbcTemplate jdbc, String token) { + this.jdbc = jdbc; + bot = TelegramBotAdapter.build(token); + protocol = new JuickProtocol(jdbc, "https://juick.com/"); + try { + bot.setWebhook("https://api.juick.com/tlgmbtwbhk"); + ws = new WebSocketFactory().createSocket("wss://ws.juick.com/"); + ws.setPingInterval(60 * 1000); + ws.addListener(new WebSocketAdapter() { + @Override + public void onDisconnected(WebSocket websocket, WebSocketFrame serverCloseFrame, WebSocketFrame clientCloseFrame, boolean closedByServer) throws Exception { + logger.info("ws disconnected"); + ws.recreate(); + } + + @Override + public void onConnected(WebSocket websocket, Map> headers) { + logger.info("ws connected"); + } + + @Override + public void onTextMessage(WebSocket websocket, String text) throws Exception { + super.onTextMessage(websocket, text); + com.juick.Message jmsg = ms.deserialize(new JSONObject(text)); + logger.info("got jmsg: " + ms.serialize(jmsg).toString()); + String msgUrl = formatUrl(jmsg); + if (jmsg.getRid() == 0) { + String msg = formatPost(jmsg); + + List users = getSubscribers(jmsg.getUser().getUid()); + List chats = getChats(); + // registered subscribed users + + users.forEach(c -> telegramNotify(c, msg, msgUrl)); + // anonymous + chats.stream().filter(u -> getUser(u) == 0).forEach(c -> telegramNotify(c, msg, msgUrl)); + } else { + // get quote + com.juick.Message msg = MessagesQueries.getReply(jdbc, jmsg.getMid(), jmsg.getRid()); + String fmsg = formatPost(msg); + getSubscribersToComments(jmsg.getMid(), jmsg.getUser().getUid()).forEach(c -> telegramNotify(c, fmsg, msgUrl)); + } + } + }); + ws.connect(); + } catch (Exception e) { + logger.warn("couldn't initialize telegram bot", e); + } + } + + private void telegramNotify(Long c, String msg, String msgUrl) { + SendMessage telegramMessage = new SendMessage(c, msg); + if (msgUrl != null) { + telegramMessage.replyMarkup( + new InlineKeyboardMarkup( + new InlineKeyboardButton[]{ + new InlineKeyboardButton("See on Juick").url(msgUrl) + } + )); + } + bot.execute(telegramMessage, new Callback() { + @Override + public void onResponse(SendMessage request, SendResponse response) { + logger.info("got response: " + response.message().toString()); + } + + @Override + public void onFailure(SendMessage request, IOException e) { + logger.warn("telegram failure", e); + } + }); + } + + List getChats() { + return jdbc.queryForList("SELECT chat_id FROM telegram_chats", Long.class); + } + + void addChat(Long id) { + jdbc.update("INSERT IGNORE INTO telegram_chats(chat_id) VALUES(?)", id); + } + + public void doPost(HttpServletRequest request) throws IOException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { + try (BufferedReader reader = request.getReader()) { + Update update = BotUtils.parseUpdate(reader); + Message message = update.message(); + if (update.message() == null) { + message = update.editedMessage(); + if (message == null) { + logger.error("error parsing telegram update: " + update.toString()); + return; + } + } + logger.info(String.format("got telegram msg %s", message.toString())); + User user_from = UserQueries.getUserByUID(jdbc, getUser(message.chat().id())).orElse(new User()); + logger.info(String.format("Found juick user %d", user_from.getUid())); + + List chats = getChats(); + String username = message.from().username(); + if (username == null) { + username = message.from().firstName(); + } + if (!chats.contains(message.chat().id())) { + addChat(message.chat().id()); + logger.info("added chat with " + username); + createTelegramUser(message.from().id(), username); + telegramSignupNotify(message.from().id().longValue(), UserQueries.getSignUpHashByTelegramID(jdbc, message.from().id().longValue(), username)); + } else { + if (user_from.getUid() == 0) { + telegramSignupNotify(message.from().id().longValue(), UserQueries.getSignUpHashByTelegramID(jdbc, message.from().id().longValue(), username)); + } else { + String text = message.text(); + if (text != null) { + if (text.equalsIgnoreCase("/login")) { + String msg = String.format("Hi, %s!\nTap to log in", user_from.getName()); + String msgUrl = "http://juick.com/login?" + UserQueries.getHashByUID(jdbc, user_from.getUid()); + telegramNotify(message.from().id().longValue(), msg, msgUrl); + } /* else { + ProtocolReply reply = protocol.getReply(user_from, text); + telegramNotify(message.from().id().longValue(), reply.getDescription(), null); + }*/ + } + } + } + } + } + + private void telegramSignupNotify(Long telegramId, String hash) { + bot.execute(new SendMessage(telegramId, + "You are subscribed to all Juick messages. " + + "Create or link an existing Juick account to control " + + "what do you want to receive").replyMarkup( + new InlineKeyboardMarkup( + new InlineKeyboardButton[]{ + new InlineKeyboardButton("SIGNUP").url("http://juick.com/signup?type=durov&hash=" + + hash) + })), new Callback() { + @Override + public void onResponse(SendMessage request, SendResponse response) { + logger.info("got response: " + response.message().toString()); + } + + @Override + public void onFailure(SendMessage request, IOException e) { + logger.warn("telegram failure", e); + } + }); + } + + private boolean createTelegramUser(long tgID, String tgName) { + return jdbc.update("INSERT INTO telegram(tg_id, tg_name, loginhash) VALUES(?,?,?)", + tgID, tgName, UUID.randomUUID().toString()) > 0; + } + + private int getUser(long tgId) { + try { + return jdbc.queryForObject("SELECT id FROM users INNER JOIN telegram " + + "ON telegram.user_id = users.id WHERE telegram.tg_id=?", Integer.class, tgId); + } catch (EmptyResultDataAccessException e) { + return 0; + } + } + + private List getSubscribers(int uid) { + return jdbc.queryForList("SELECT tg_id FROM telegram INNER JOIN subscr_users " + + "ON (subscr_users.user_id=? AND telegram.user_id=subscr_users.suser_id)", Long.class, uid); + } + + private List getSubscribersToComments(int mid, int ignore_uid) { + return jdbc.queryForList("SELECT tg_id FROM telegram INNER JOIN subscr_messages " + + "ON (telegram.user_id=subscr_messages.suser_id) WHERE message_id=? AND suser_id!=?", Long.class, mid, ignore_uid); + } + + @Override + public void close() throws Exception { + try { + if (ws != null) { + ws.disconnect(); + } + } catch (Exception e) { + logger.warn("websocket disconnection exception", e); + } + } +} diff --git a/juick-api/src/main/java/com/juick/api/TelegramBotHook.java b/juick-api/src/main/java/com/juick/api/TelegramBotHook.java deleted file mode 100644 index e089250b..00000000 --- a/juick-api/src/main/java/com/juick/api/TelegramBotHook.java +++ /dev/null @@ -1,221 +0,0 @@ -package com.juick.api; - -import com.juick.User; -import com.juick.json.MessageSerializer; -import com.juick.server.MessagesQueries; -import com.juick.server.UserQueries; -import com.juick.server.protocol.JuickProtocol; -import com.neovisionaries.ws.client.*; -import com.pengrad.telegrambot.BotUtils; -import com.pengrad.telegrambot.Callback; -import com.pengrad.telegrambot.TelegramBot; -import com.pengrad.telegrambot.TelegramBotAdapter; -import com.pengrad.telegrambot.model.Message; -import com.pengrad.telegrambot.model.Update; -import com.pengrad.telegrambot.model.request.InlineKeyboardButton; -import com.pengrad.telegrambot.model.request.InlineKeyboardMarkup; -import com.pengrad.telegrambot.request.SendMessage; -import com.pengrad.telegrambot.response.SendResponse; -import org.json.JSONObject; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.dao.EmptyResultDataAccessException; -import org.springframework.jdbc.core.JdbcTemplate; - -import javax.servlet.http.HttpServletRequest; -import java.io.BufferedReader; -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -import static com.juick.formatters.PlainTextFormatter.formatPost; -import static com.juick.formatters.PlainTextFormatter.formatUrl; - -/** - * Created by vt on 12/05/16. - */ -public class TelegramBotHook { - private static final Logger logger = LoggerFactory.getLogger(TelegramBotHook.class); - - TelegramBot bot; - WebSocket ws; - MessageSerializer ms = new MessageSerializer(); - JdbcTemplate jdbc; - JuickProtocol protocol; - - - public TelegramBotHook(JdbcTemplate jdbc, String token) { - this.jdbc = jdbc; - bot = TelegramBotAdapter.build(token); - protocol = new JuickProtocol(jdbc, "https://juick.com/"); - try { - bot.setWebhook("https://api.juick.com/tlgmbtwbhk"); - ws = new WebSocketFactory().createSocket("wss://ws.juick.com/"); - ws.setPingInterval(60 * 1000); - ws.addListener(new WebSocketAdapter() { - @Override - public void onDisconnected(WebSocket websocket, WebSocketFrame serverCloseFrame, WebSocketFrame clientCloseFrame, boolean closedByServer) throws Exception { - logger.info("ws disconnected"); - ws.connect(); - } - - @Override - public void onConnected(WebSocket websocket, Map> headers) { - logger.info("ws connected"); - } - - @Override - public void onTextMessage(WebSocket websocket, String text) throws Exception { - super.onTextMessage(websocket, text); - com.juick.Message jmsg = ms.deserialize(new JSONObject(text)); - logger.info("got jmsg: " + ms.serialize(jmsg).toString()); - String msgUrl = formatUrl(jmsg); - if (jmsg.getRid() == 0) { - String msg = formatPost(jmsg); - - List users = getSubscribers(jmsg.getUser().getUid()); - List chats = getChats(); - // registered subscribed users - - users.forEach(c -> telegramNotify(c, msg, msgUrl)); - // anonymous - chats.stream().filter(u -> getUser(u) == 0).forEach(c -> telegramNotify(c, msg, msgUrl)); - } else { - // get quote - com.juick.Message msg = MessagesQueries.getReply(jdbc, jmsg.getMid(), jmsg.getRid()); - String fmsg = formatPost(msg); - getSubscribersToComments(jmsg.getMid(), jmsg.getUser().getUid()).stream() - .forEach(c -> telegramNotify(c, fmsg, msgUrl)); - } - } - }); - ws.connect(); - } catch (Exception e) { - logger.warn("couldn't initialize telegram bot", e); - } - } - - private void telegramNotify(Long c, String msg, String msgUrl) { - SendMessage telegramMessage = new SendMessage(c, msg); - if (msgUrl != null) { - telegramMessage.replyMarkup( - new InlineKeyboardMarkup( - new InlineKeyboardButton[]{ - new InlineKeyboardButton("See on Juick").url(msgUrl) - } - )); - } - bot.execute(telegramMessage, new Callback() { - @Override - public void onResponse(SendMessage request, SendResponse response) { - logger.info("got response: " + response.message().toString()); - } - - @Override - public void onFailure(SendMessage request, IOException e) { - logger.warn("telegram failure", e); - } - }); - } - - List getChats() { - return jdbc.queryForList("SELECT chat_id FROM telegram_chats", Long.class); - } - - void addChat(Long id) { - jdbc.update("INSERT IGNORE INTO telegram_chats(chat_id) VALUES(?)", id); - } - - public void doPost(HttpServletRequest request) throws IOException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { - try (BufferedReader reader = request.getReader()) { - Update update = BotUtils.parseUpdate(reader); - Message message = update.message(); - if (update.message() == null) { - message = update.editedMessage(); - if (message == null) { - logger.error("error parsing telegram update: " + update.toString()); - return; - } - } - logger.info(String.format("got telegram msg %s", message.toString())); - User user_from = UserQueries.getUserByUID(jdbc, getUser(message.chat().id())).orElse(new User()); - logger.info(String.format("Found juick user %d", user_from.getUid())); - - List chats = getChats(); - String username = message.from().username(); - if (username == null) { - username = message.from().firstName(); - } - if (!chats.contains(message.chat().id())) { - addChat(message.chat().id()); - logger.info("added chat with " + username); - createTelegramUser(message.from().id(), username); - telegramSignupNotify(message.from().id().longValue(), UserQueries.getSignUpHashByTelegramID(jdbc, message.from().id().longValue(), username)); - } else { - if (user_from.getUid() == 0) { - telegramSignupNotify(message.from().id().longValue(), UserQueries.getSignUpHashByTelegramID(jdbc, message.from().id().longValue(), username)); - } else { - String text = message.text(); - if (text != null) { - if (text.equalsIgnoreCase("/login")) { - String msg = String.format("Hi, %s!\nTap to log in", user_from.getName()); - String msgUrl = "http://juick.com/login?" + UserQueries.getHashByUID(jdbc, user_from.getUid()); - telegramNotify(message.from().id().longValue(), msg, msgUrl); - } /* else { - ProtocolReply reply = protocol.getReply(user_from, text); - telegramNotify(message.from().id().longValue(), reply.getDescription(), null); - }*/ - } - } - } - } - } - - private void telegramSignupNotify(Long telegramId, String hash) { - bot.execute(new SendMessage(telegramId, - "You are subscribed to all Juick messages. " + - "Create or link an existing Juick account to control " + - "what do you want to receive").replyMarkup( - new InlineKeyboardMarkup( - new InlineKeyboardButton[]{ - new InlineKeyboardButton("SIGNUP").url("http://juick.com/signup?type=durov&hash=" + - hash) - })), new Callback() { - @Override - public void onResponse(SendMessage request, SendResponse response) { - logger.info("got response: " + response.message().toString()); - } - - @Override - public void onFailure(SendMessage request, IOException e) { - logger.warn("telegram failure", e); - } - }); - } - - private boolean createTelegramUser(long tgID, String tgName) { - return jdbc.update("INSERT INTO telegram(tg_id, tg_name, loginhash) VALUES(?,?,?)", - tgID, tgName, UUID.randomUUID().toString()) > 0; - } - - private int getUser(long tgId) { - try { - return jdbc.queryForObject("SELECT id FROM users INNER JOIN telegram " + - "ON telegram.user_id = users.id WHERE telegram.tg_id=?", Integer.class, tgId); - } catch (EmptyResultDataAccessException e) { - return 0; - } - } - - private List getSubscribers(int uid) { - return jdbc.queryForList("SELECT tg_id FROM telegram INNER JOIN subscr_users " + - "ON (subscr_users.user_id=? AND telegram.user_id=subscr_users.suser_id)", Long.class, uid); - } - - private List getSubscribersToComments(int mid, int ignore_uid) { - return jdbc.queryForList("SELECT tg_id FROM telegram INNER JOIN subscr_messages " + - "ON (telegram.user_id=subscr_messages.suser_id) WHERE message_id=? AND suser_id!=?", Long.class, mid, ignore_uid); - } -} -- cgit v1.2.3