|
@@ -1,18 +1,24 @@
|
|
|
import TurndownService, { Options } from "turndown";
|
|
|
import interval from "interval-promise";
|
|
|
-import { client, forumClient } from "../client";
|
|
|
+import { client, forumClient, FORUMS_DOMAIN } from "../client";
|
|
|
import sha1 from "sha1";
|
|
|
import * as path from "path";
|
|
|
import * as fs from "fs";
|
|
|
-import translate from "translate-google";
|
|
|
+import { HTML2BBCode } from "html2bbcode";
|
|
|
+import { Dict } from "../util";
|
|
|
|
|
|
import { IAggregator, NewsPostItem, INewsPostData } from "./aggregators/aggregator";
|
|
|
import { ICommand } from "./command";
|
|
|
-import { RichEmbed, TextChannel, Message, Channel } from "discord.js";
|
|
|
-import { getRepository } from "typeorm";
|
|
|
+import { RichEmbed, TextChannel, Message, Channel, ReactionCollector, MessageReaction, User, Collector } from "discord.js";
|
|
|
+import { getRepository, IsNull, Not } from "typeorm";
|
|
|
import { KnownChannel } from "@db/entity/KnownChannel";
|
|
|
import { AggroNewsItem } from "@db/entity/AggroNewsItem";
|
|
|
|
|
|
+import { v3beta1 } from "@google-cloud/translate";
|
|
|
+
|
|
|
+const { TranslationServiceClient } = v3beta1;
|
|
|
+const tlClient = new TranslationServiceClient();
|
|
|
+
|
|
|
const UPDATE_INTERVAL = 5;
|
|
|
const MAX_PREVIEW_LENGTH = 300;
|
|
|
|
|
@@ -22,6 +28,10 @@ const AGGREGATOR_MANAGER_CHANNEL = "aggregatorManager";
|
|
|
|
|
|
const FORUMS_STAGING_ID = 54;
|
|
|
const FORUMS_NEWS_ID = 49;
|
|
|
+const bbCodeParser = new HTML2BBCode();
|
|
|
+
|
|
|
+const reactionCollectors: Dict<ReactionCollector> = {};
|
|
|
+const verifyMessageIdToPost: Dict<AggroNewsItem> = {};
|
|
|
|
|
|
|
|
|
const turndown = new TurndownService();
|
|
@@ -55,7 +65,7 @@ async function checkFeeds() {
|
|
|
cacheMessageId: null,
|
|
|
postedMessageId: null
|
|
|
} as NewsPostItem;
|
|
|
- itemObj.contents = markdownify(item.contents);
|
|
|
+
|
|
|
itemObj.hash = sha1(itemObj.contents);
|
|
|
|
|
|
await addNewsItem(itemObj);
|
|
@@ -74,6 +84,12 @@ function clipText(text: string) {
|
|
|
async function addNewsItem(item: NewsPostItem) {
|
|
|
let repo = getRepository(AggroNewsItem);
|
|
|
|
|
|
+ let ch = client.channels.get(aggregateChannelID);
|
|
|
+
|
|
|
+ if(!(ch instanceof TextChannel))
|
|
|
+ return;
|
|
|
+
|
|
|
+ let isNew = true;
|
|
|
let newsItem = await repo.findOne({
|
|
|
where: { feedName: item.feedId, newsId: item.newsId }
|
|
|
});
|
|
@@ -84,6 +100,7 @@ async function addNewsItem(item: NewsPostItem) {
|
|
|
return;
|
|
|
else
|
|
|
await deleteCacheMessage(newsItem.editMessageId);
|
|
|
+ isNew = false;
|
|
|
} else {
|
|
|
newsItem = repo.create({
|
|
|
newsId: item.newsId,
|
|
@@ -92,17 +109,40 @@ async function addNewsItem(item: NewsPostItem) {
|
|
|
});
|
|
|
}
|
|
|
|
|
|
- let ch = client.channels.get(aggregateChannelID);
|
|
|
+ if(item.needsTranslation)
|
|
|
+ try {
|
|
|
+ let request = {
|
|
|
+ parent: tlClient.locationPath(process.env.GOOGLE_APP_ID, "global"),
|
|
|
+ contents: [ item.title, item.contents ],
|
|
|
+ mimeType: "text/html",
|
|
|
+ sourceLanguageCode: "ja",
|
|
|
+ targetLanguageCode: "en"
|
|
|
+ };
|
|
|
+
|
|
|
+ let [ res ] = await tlClient.translateText(request);
|
|
|
+
|
|
|
+ item.title = res.translations[0].translatedText
|
|
|
+ item.contents = res.translations[1].translatedText;
|
|
|
+ } catch(err) {
|
|
|
+ console.log(`Failed to translate because ${err}`);
|
|
|
+ }
|
|
|
+
|
|
|
+ item.contents = bbCodeParser.feed(item.contents).toString();
|
|
|
+
|
|
|
+ if(!newsItem.forumsEditPostId) {
|
|
|
+ let createResponse = await forumClient.createThread(FORUMS_STAGING_ID, item.title, item.contents);
|
|
|
+ newsItem.forumsEditPostId = createResponse.thread.thread_id;
|
|
|
+ } else {
|
|
|
+ await forumClient.postReply(newsItem.forumsNewsPostId, item.contents);
|
|
|
+ }
|
|
|
|
|
|
- if(!(ch instanceof TextChannel))
|
|
|
- return;
|
|
|
|
|
|
let msg = await ch.send(new RichEmbed({
|
|
|
title: item.title,
|
|
|
url: item.link,
|
|
|
color: item.embedColor,
|
|
|
timestamp: new Date(),
|
|
|
- description: clipText(item.contents),
|
|
|
+ description: `${(isNew ? "**[NEW]**" : "**[EDIT]**")}\n[**Edit on forums**](${FORUMS_DOMAIN}/index.php?threads/.${newsItem.forumsEditPostId}/)`,
|
|
|
author: {
|
|
|
name: item.author
|
|
|
},
|
|
@@ -113,9 +153,53 @@ async function addNewsItem(item: NewsPostItem) {
|
|
|
|
|
|
newsItem.editMessageId = msg.id;
|
|
|
|
|
|
+ await msg.react("✅");
|
|
|
+ await msg.react("❌");
|
|
|
+
|
|
|
+ let collector = msg.createReactionCollector(isVerifyReaction, { maxEmojis: 1 });
|
|
|
+ collector.on("collect", collectReaction)
|
|
|
+ reactionCollectors[msg.id] = collector;
|
|
|
+ verifyMessageIdToPost[msg.id] = newsItem;
|
|
|
+
|
|
|
await repo.save(newsItem);
|
|
|
}
|
|
|
|
|
|
+function isVerifyReaction(reaction: MessageReaction, user: User) {
|
|
|
+ return (reaction.emoji.name == "✅" || reaction.emoji.name == "❌") && !user.bot && user.id != client.user.id;
|
|
|
+}
|
|
|
+
|
|
|
+async function collectReaction(reaction: MessageReaction, collector: Collector<string, MessageReaction>) {
|
|
|
+ let repo = getRepository(AggroNewsItem);
|
|
|
+
|
|
|
+ let m = reaction.message;
|
|
|
+ collector.stop();
|
|
|
+ delete reactionCollectors[m.id];
|
|
|
+ let post = verifyMessageIdToPost[m.id];
|
|
|
+
|
|
|
+ if (reaction.emoji.name == "✅") {
|
|
|
+ let res = await forumClient.getThread(post.forumsEditPostId);
|
|
|
+ let forumPost = await forumClient.getPost(res.thread.first_post_id);
|
|
|
+
|
|
|
+ if(!post.forumsNewsPostId) {
|
|
|
+ let newThread = await forumClient.createThread(FORUMS_NEWS_ID, res.thread.title, forumPost.message);
|
|
|
+ post.forumsNewsPostId = newThread.thread.thread_id;
|
|
|
+ } else {
|
|
|
+ let curThread = await forumClient.editThread(post.forumsNewsPostId, {
|
|
|
+ title: res.thread.title
|
|
|
+ });
|
|
|
+
|
|
|
+ await forumClient.editPost(curThread.thread.first_post_id, {
|
|
|
+ message: forumPost.message
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ await forumClient.deleteThread(post.forumsEditPostId);
|
|
|
+ await repo.update({ newsId: post.newsId, feedName: post.feedName }, { editMessageId: null, forumsEditPostId: null, forumsNewsPostId: post.forumsNewsPostId });
|
|
|
+ await reaction.message.delete();
|
|
|
+ delete verifyMessageIdToPost[m.id];
|
|
|
+}
|
|
|
+
|
|
|
async function deleteCacheMessage(messageId: string) {
|
|
|
let ch = client.channels.get(aggregateChannelID);
|
|
|
if(!(ch instanceof TextChannel))
|
|
@@ -127,10 +211,12 @@ async function deleteCacheMessage(messageId: string) {
|
|
|
await msg.delete();
|
|
|
}
|
|
|
|
|
|
-async function tryFetchMessage(channel : TextChannel, messageId: string) {
|
|
|
+async function tryFetchMessage(channel: Channel, messageId: string) {
|
|
|
try {
|
|
|
+ if (!(channel instanceof TextChannel))
|
|
|
+ return null;
|
|
|
return await channel.fetchMessage(messageId);
|
|
|
- }catch(error){
|
|
|
+ } catch (error) {
|
|
|
return null;
|
|
|
}
|
|
|
}
|
|
@@ -158,6 +244,30 @@ function initAggregators() {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+async function initPendingReactors() {
|
|
|
+ let verifyChannel = client.channels.get(aggregateChannelID);
|
|
|
+
|
|
|
+ let repo = getRepository(AggroNewsItem);
|
|
|
+
|
|
|
+ let pendingVerifyMessages = await repo.find({
|
|
|
+ where: { editMessageId: Not(IsNull()) }
|
|
|
+ });
|
|
|
+
|
|
|
+ for (let msg of pendingVerifyMessages) {
|
|
|
+ let m = await tryFetchMessage(verifyChannel, msg.editMessageId);
|
|
|
+
|
|
|
+ if (!m) {
|
|
|
+ await repo.update({ feedName: msg.feedName, newsId: msg.newsId }, { editMessageId: null });
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ let collector = m.createReactionCollector(isVerifyReaction, { maxEmojis: 1 });
|
|
|
+ collector.on("collect", collectReaction);
|
|
|
+ reactionCollectors[m.id] = collector;
|
|
|
+ verifyMessageIdToPost[m.id] = msg;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
export default {
|
|
|
onStart : async () => {
|
|
|
let repo = getRepository(KnownChannel);
|
|
@@ -166,9 +276,12 @@ export default {
|
|
|
where: { channelType: AGGREGATOR_MANAGER_CHANNEL }
|
|
|
});
|
|
|
|
|
|
- if(ch)
|
|
|
- aggregateChannelID = ch.channelId;
|
|
|
+ if(!ch)
|
|
|
+ return;
|
|
|
+
|
|
|
+ aggregateChannelID = ch.channelId;
|
|
|
|
|
|
+ await initPendingReactors();
|
|
|
initAggregators();
|
|
|
interval(checkFeeds, UPDATE_INTERVAL * 60 * 1000);
|
|
|
}
|