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.juick.server.protocol.ProtocolReply; 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.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 java.util.logging.Level; import java.util.logging.Logger; /** * Created by vt on 12/05/16. */ public class TelegramBotHook { private static final Logger logger = Logger.getLogger(TelegramBotHook.class.getName()); TelegramBot bot; WebSocket ws, wsReply; 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/_all"); ws.addHeader("Origin", "ws.juick.com"); ws.addHeader("Host", "ws.juick.com"); //TODO: remove from server 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()); StringBuilder sb = new StringBuilder(); sb.append("@").append(jmsg.getUser().getUName()).append(":\n") .append(jmsg.getTagsString()).append("\n").append(jmsg.getText()).append("\n"); if (jmsg.Photo != null) { sb.append(jmsg.Photo); } String msg = sb.toString(); List users = getSubscribers(jmsg.getUser().getUID()); List chats = getChats(); // registered subscribed users String msgUrl = "https://juick.com/" + jmsg.getMID(); users.stream().forEach(c -> telegramNotify(c, msg, msgUrl)); // anonymous chats.stream().filter(u -> getUser(u) == 0).forEach(c -> telegramNotify(c, msg, msgUrl)); } }); ws.connect(); wsReply = new WebSocketFactory().createSocket("wss://ws.juick.com/_replies"); wsReply.addHeader("Origin", "ws.juick.com"); wsReply.addHeader("Host", "ws.juick.com"); wsReply.setPingInterval(60 * 1000); wsReply.addListener(new WebSocketAdapter() { @Override public void onDisconnected(WebSocket websocket, WebSocketFrame serverCloseFrame, WebSocketFrame clientCloseFrame, boolean closedByServer) throws Exception { logger.info("ws replies disconnected"); ws.connect(); } @Override public void onConnected(WebSocket websocket, Map> headers) throws Exception { logger.info("ws replies connected"); } @Override public void onTextMessage(WebSocket websocket, String text) throws Exception { com.juick.Message jmsg = ms.deserialize(new JSONObject(text)); logger.info(String.format("got jmsg: %s", ms.serialize(jmsg).toString())); StringBuilder sb = new StringBuilder(); sb.append("Reply by @").append(jmsg.getUser().getUName()).append(":\n") .append(getReplyQuote(jmsg.getMID(), jmsg.ReplyTo)).append("\n").append(jmsg.getText()); if (jmsg.getAttachmentURL() != null) { sb.append("\n").append(jmsg.getAttachmentURL()); } String msg = sb.toString(); String msgUrl = String.format("https://juick.com/%d#%d", jmsg.getMID(), jmsg.getRID()); getSubscribersToComments(jmsg.getMID(), jmsg.getUser().getUID()).stream() .forEach(c -> telegramNotify(c, msg, msgUrl)); } }); wsReply.connect(); } catch (Exception e) { logger.log(Level.WARNING, "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.log(Level.WARNING, "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.log(Level.SEVERE, "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.getUName()); 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.log(Level.WARNING, "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); } private String getReplyQuote(int MID, int ReplyTo) { String quote = ""; if (ReplyTo > 0) { com.juick.Message q = MessagesQueries.getReply(jdbc, MID, ReplyTo); if (q != null) { quote = q.getText(); } } else { com.juick.Message q = MessagesQueries.getMessage(jdbc, MID); if (q != null) { quote = q.getText(); } } if (quote.length() > 50) { quote = ">" + quote.substring(0, 47).replace('\n', ' ') + "...\n"; } else if (quote.length() > 0) { quote = ">" + quote.replace('\n', ' ') + "\n"; } return quote; } }