From 7ed3795da542bab397ee7ed8978bde896abf5cf4 Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Tue, 6 Feb 2018 17:54:49 +0300 Subject: server: merge api and ws --- .../java/com/juick/api/TelegramBotManager.java | 298 +++++++++++++++++++++ 1 file changed, 298 insertions(+) create mode 100644 juick-server/src/main/java/com/juick/api/TelegramBotManager.java (limited to 'juick-server/src/main/java/com/juick/api/TelegramBotManager.java') diff --git a/juick-server/src/main/java/com/juick/api/TelegramBotManager.java b/juick-server/src/main/java/com/juick/api/TelegramBotManager.java new file mode 100644 index 00000000..6f2e50d1 --- /dev/null +++ b/juick-server/src/main/java/com/juick/api/TelegramBotManager.java @@ -0,0 +1,298 @@ +/* + * Copyright (C) 2008-2017, Juick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.juick.api; + +import com.juick.User; +import com.juick.server.component.MessageEvent; +import com.juick.server.util.HttpUtils; +import com.juick.service.MessagesService; +import com.juick.service.SubscriptionService; +import com.juick.service.TelegramService; +import com.juick.service.UserService; +import com.pengrad.telegrambot.BotUtils; +import com.pengrad.telegrambot.Callback; +import com.pengrad.telegrambot.TelegramBot; +import com.pengrad.telegrambot.model.Message; +import com.pengrad.telegrambot.model.MessageEntity; +import com.pengrad.telegrambot.model.PhotoSize; +import com.pengrad.telegrambot.model.Update; +import com.pengrad.telegrambot.model.request.ParseMode; +import com.pengrad.telegrambot.request.GetFile; +import com.pengrad.telegrambot.request.SendMessage; +import com.pengrad.telegrambot.request.SetWebhook; +import com.pengrad.telegrambot.response.GetFileResponse; +import com.pengrad.telegrambot.response.SendResponse; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.ApplicationListener; +import org.springframework.stereotype.Component; +import org.springframework.web.util.UriComponents; +import org.springframework.web.util.UriComponentsBuilder; + +import javax.annotation.Nonnull; +import javax.annotation.PostConstruct; +import javax.inject.Inject; +import java.io.IOException; +import java.net.URL; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.Optional; + +import static com.juick.formatters.PlainTextFormatter.formatPost; +import static com.juick.formatters.PlainTextFormatter.formatUrl; + +/** + * Created by vt on 12/05/16. + */ +@Component +public class TelegramBotManager implements ApplicationListener { + private static final Logger logger = LoggerFactory.getLogger(TelegramBotManager.class); + + private TelegramBot bot; + + @Value("${telegram_token}") + private String telegramToken; + @Inject + private TelegramService telegramService; + @Inject + private MessagesService messagesService; + @Inject + private SubscriptionService subscriptionService; + @Inject + private UserService userService; + @Inject + private ApiServer apiServer; + @Value("${upload_tmp_dir:/var/www/juick.com/i/tmp/}") + private String tmpDir; + + private static final String MSG_LINK = "🔗"; + + @PostConstruct + public void init() { + if (StringUtils.isBlank(telegramToken)) { + logger.info("telegram token is not set, exiting"); + return; + } + bot = new TelegramBot(telegramToken); + try { + SetWebhook webhook = new SetWebhook().url("https://api.juick.com/tlgmbtwbhk"); + if (!bot.execute(webhook).isOk()) { + logger.error("error setting webhook"); + } + } catch (Exception e) { + logger.warn("couldn't initialize telegram bot", e); + } + } + + public void processUpdate(String data) throws Exception { + Update update = BotUtils.parseUpdate(data); + Message message = update.message(); + if (update.message() == null) { + message = update.editedMessage(); + if (message == null) { + logger.error("error parsing telegram update: {}", update); + return; + } + } + User user_from = userService.getUserByUID(telegramService.getUser(message.chat().id())).orElse(new User()); + logger.info("Found juick user {}", user_from.getUid()); + + List chats = telegramService.getChats(); + String username = message.from().username(); + if (username == null) { + username = message.from().firstName(); + } + if (!chats.contains(message.chat().id())) { + telegramService.addChat(message.chat().id()); + logger.info("added chat with {}", username); + telegramService.createTelegramUser(message.from().id(), username); + telegramSignupNotify(message.from().id().longValue(), userService.getSignUpHashByTelegramID(message.from().id().longValue(), username)); + } else { + if (user_from.getUid() == 0) { + telegramSignupNotify(message.from().id().longValue(), userService.getSignUpHashByTelegramID(message.from().id().longValue(), username)); + } else { + String attachment = StringUtils.EMPTY; + if (message.photo() != null) { + String fileId = Arrays.stream(message.photo()).max(Comparator.comparingInt(PhotoSize::fileSize)) + .orElse(new PhotoSize()).fileId(); + if (StringUtils.isNotEmpty(fileId)) { + GetFile request = new GetFile(fileId); + GetFileResponse response = bot.execute(request); + logger.info("got file {}", response.file()); + URL fileURL = new URL(bot.getFullFilePath(response.file())); + attachment = HttpUtils.downloadImage(fileURL, tmpDir); + logger.info("received {}", attachment); + } + } + String text = message.text(); + if (StringUtils.isBlank(text)) { + text = message.caption(); + } + if (StringUtils.isBlank(text)) { + text = StringUtils.EMPTY; + } + if (StringUtils.isNotEmpty(text) || StringUtils.isNotEmpty(attachment)) { + if (text.equalsIgnoreCase("LOGIN") + || text.equalsIgnoreCase("PING") + || text.equalsIgnoreCase("HELP") + || text.equalsIgnoreCase("/login") + || text.equalsIgnoreCase("/start") + || text.equalsIgnoreCase("/help")) { + String msgUrl = "http://juick.com/login?hash=" + userService.getHashByUID(user_from.getUid()); + String msg = String.format("Hi, %s!\nYou can post messages and images to Juick there.\n" + + "Tap to [log into website](%s) to get more info", user_from.getName(), msgUrl); + telegramNotify(message.from().id().longValue(), msg); + } else { + Message replyMessage = message.replyToMessage(); + if (replyMessage != null) { + if (replyMessage.entities() != null) { + Optional juickLink = Arrays.stream(replyMessage.entities()) + .filter(this::isJuickLink) + .findFirst(); + if (juickLink.isPresent()) { + if (StringUtils.isNotEmpty(juickLink.get().url())) { + UriComponents uriComponents = UriComponentsBuilder.fromUriString( + juickLink.get().url()).build(); + String path = uriComponents.getPath(); + if (StringUtils.isNotEmpty(path) && path.length() > 1) { + int mid = Integer.valueOf(path.substring(1)); + String prefix = String.format("#%d ", mid); + if (StringUtils.isNotEmpty(uriComponents.getFragment())) { + int rid = Integer.valueOf(uriComponents.getFragment()); + prefix = String.format("#%d/%d ", mid, rid); + } + apiServer.processMessage(user_from, prefix + text, attachment); + telegramNotify(message.from().id().longValue(), "Reply sent"); + } else { + logger.warn("invalid path: {}", path); + } + } else { + logger.warn("invalid entity: {}", juickLink); + } + } else { + telegramNotify(message.from().id().longValue(), + "Can not reply to this message", replyMessage.messageId()); + } + } else { + telegramNotify(message.from().id().longValue(), + "Can not reply to this message", replyMessage.messageId()); + } + } else { + apiServer.processMessage(user_from, text, attachment); + telegramNotify(message.from().id().longValue(), "Message sent"); + } + } + } + } + } + } + + private boolean isJuickLink(MessageEntity e) { + return e.offset() == 0 && e.type().equals(MessageEntity.Type.text_link) && e.length() == 2; + } + + @Override + public void onApplicationEvent(@Nonnull MessageEvent event) { + com.juick.Message jmsg = event.getMessage(); + String msgUrl = formatUrl(jmsg); + if (jmsg.getMid() > 0 && jmsg.getRid() == 0) { + String msg = String.format("[%s](%s) %s", MSG_LINK, msgUrl, formatPost(jmsg, true)); + + List users = telegramService.getTelegramIdentifiers(subscriptionService.getSubscribedUsers(jmsg.getUser().getUid(), jmsg.getMid())); + List chats = telegramService.getChats(); + // registered subscribed users + + users.forEach(c -> telegramNotify(c, msg)); + // anonymous + chats.stream().filter(u -> telegramService.getUser(u) == 0).forEach(c -> telegramNotify(c, msg)); + } else if (jmsg.getRid() > 0) { + // get quote + com.juick.Message msg = messagesService.getReply(jmsg.getMid(), jmsg.getRid()); + String fmsg = String.format("[%s](%s) %s", MSG_LINK, msgUrl, formatPost(msg, true)); + telegramService.getTelegramIdentifiers( + subscriptionService.getUsersSubscribedToComments(jmsg.getMid(), jmsg.getUser().getUid()) + ).forEach(c -> telegramNotify(c, fmsg)); + } + } + + public void telegramNotify(Long chatId, String msg) { + telegramNotify(chatId, msg, 0); + } + + public void telegramNotify(Long chatId, String msg, Integer replyTo) { + SendMessage telegramMessage = new SendMessage(chatId, msg); + if (replyTo > 0) { + telegramMessage.replyToMessageId(replyTo); + } + telegramMessage.parseMode(ParseMode.Markdown).disableWebPagePreview(true); + bot.execute(telegramMessage, new Callback() { + @Override + public void onResponse(SendMessage request, SendResponse response) { + if (!response.isOk()) { + if (response.errorCode() == 403) { + // remove from anonymous chat + telegramService.getChats().stream().filter(c -> c.equals(chatId)).findFirst().ifPresent( + d -> { + telegramService.deleteChat(d); + logger.info("deleted {} chat", d); + } + ); + int userId = telegramService.getUser(chatId); + if (userId > 0) { + User userToDelete = userService.getUserByUID(userId) + .orElse(new User()); + boolean status = telegramService.deleteTelegramUser(userToDelete.getUid()); + logger.info("deleting telegram id of @{} : {}", userToDelete.getName(), status); + boolean chatStatus = telegramService.deleteChat(chatId); + logger.info("deleting telegram chat {} : {}", chatId, chatStatus); + } + } else { + logger.warn("error response, isOk: {}, errorCode: {}, description: {}", + response.isOk(), response.errorCode(), response.description()); + } + } + } + + @Override + public void onFailure(SendMessage request, IOException e) { + logger.warn("telegram failure", e); + } + }); + } + + public void telegramSignupNotify(Long telegramId, String hash) { + bot.execute(new SendMessage(telegramId, + String.format("You are subscribed to all Juick messages. " + + "[Create or link](http://juick.com/signup?type=durov&hash=%s) " + + "an existing Juick account to get your subscriptions and ability to post messages", hash)) + .parseMode(ParseMode.Markdown), new Callback() { + @Override + public void onResponse(SendMessage request, SendResponse response) { + logger.info("got response: {}", response.message()); + } + + @Override + public void onFailure(SendMessage request, IOException e) { + logger.warn("telegram failure", e); + } + }); + } +} -- cgit v1.2.3