import TelegramBot, { SendMessageOptions } from "node-telegram-bot-api"; import * as dotenv from "dotenv"; import axios from "axios"; import moment from "moment"; import { Client } from "discord.js"; // import { Client, Intents, Options } from "discord.js"; interface ScoringPeriodData { currentScoringPeriod: { scoringPeriodId: number; started: string; ends: string; referralCode: number; }; lastPeriod: { scoringPeriodId: number; started: string; ends: string; referralCode: number; totalDirectScore: number; totalReferralScore: number; highlights: string[]; }; } dotenv.config(); const tgApiKey = process.env.TG_API_KEY_EN || ""; const bot = new TelegramBot(tgApiKey, { polling: true }); const discordToken = process.env.DISCORD_TOKEN || ""; const client = new Client(); let nextSyncDate = moment(); let scoringData = {} as ScoringPeriodData; let lastGradedPeriod = null as number | null; const messageDeletionTimeout = 60000; client.login(discordToken); client.on("ready", async () => { if (!client.user) return; console.log(`Logged in to discord as ${client.user.tag}!`); }); moment.locale("en"); const loadScoringPeriodData = async () => { const url = "https://raw.githubusercontent.com/Joystream/founding-members/main/data/fm-info.json"; const { data } = await axios.get(url); return data.scoringPeriodsFull; }; client.on("message", async (message) => { await reloadScoringData(); if (message.content === "!help") { const helpMessage = `Hello ${message.author.username}, I can help you with the status of Joystream Scoring Periods.\nIn channels I delete my messages after 30 seconds, but in DM they will be kept.\nSupported commands:\n!scoring - Get the scoring period information\n!help - Get this help`; message.channel.send({content: helpMessage}).then( (msg: { delete: () => void; }) => { if (message.channel.type !== "dm") { message.delete() setTimeout(() => { msg.delete() }, messageDeletionTimeout) } }); } if (message.content === "!scoring") { const leaderboardLink = `\nCheck scores here: https://t.me/JoystreamLeaderboardBot\nSubmit your report here: https://www.joystream.org/founding-members/form`; const submitReportLink = "submit your report"; const messageContent = generateScoringPeriodMessage( message.author.username, submitReportLink, leaderboardLink ); message.channel.send({content: messageContent}).then( (msg: { suppressEmbeds: (arg0: boolean) => void; delete: () => void; }) => { msg.suppressEmbeds(true) if (message.channel.type !== "dm") { message.delete() setTimeout(() => { msg.delete() }, messageDeletionTimeout) } }); } }); bot.on("message", async (msg: TelegramBot.Message) => { await reloadScoringData(); if (msg && msg.from) { console.log(msg.chat); const chatId = msg.chat.id; const username = `${msg.from.first_name} ${ msg.from.last_name || "" }`.trim(); const userParsed = `[${username}](tg://user?id=${msg.from.id})`; const leaderboardLink = ` - [Check scores](https://t.me/JoystreamLeaderboardBot)`; const options: SendMessageOptions = { parse_mode: "Markdown", disable_web_page_preview: true, }; if (msg.text?.startsWith("/scoring")) { const submitReportLink = "[submit your report](https://www.joystream.org/founding-members/form)"; const messageContent = generateScoringPeriodMessage( userParsed, submitReportLink, leaderboardLink ); bot.sendMessage(chatId, messageContent, options).then((message) => { try { bot.deleteMessage(chatId, msg.message_id.toString()); } catch (e) {} setTimeout(() => { try { bot.deleteMessage(chatId, message.message_id.toString()); } catch (e) {} }, messageDeletionTimeout); }); } } }); const generateScoringPeriodMessage = ( userParsed: string, submitReportLink: string, leaderboardLink: string ) => { const startDate = moment.parseZone(scoringData.currentScoringPeriod.started); const endDate = moment.parseZone(scoringData.currentScoringPeriod.ends); const duration = moment.duration(endDate.diff(moment())); const daysDuration = duration.asDays().toFixed(); let dayText = "days"; if (daysDuration === "1") { dayText = "day"; } const daysLeft = `***${duration .asDays() .toFixed()} ${dayText} on ${endDate.format( "dddd DD MMM" )} at ${endDate.format("HH:mm")}***`; const deadline = endDate.add(5, "d").format("dddd DD MMM HH:mm"); const prevDeadline = startDate.add(5, "d").format("dddd DD MMM HH:mm"); const hello = `Hello ${userParsed}!\n`; const currentPeriodId = scoringData.currentScoringPeriod.scoringPeriodId; const prevPeriodId = scoringData.currentScoringPeriod.scoringPeriodId - 1; const currentScoring = `The current scoring period ***#${currentPeriodId}*** ends in ${daysLeft}\n`; const currentDeadline = `Please make sure to ${submitReportLink} for period ***#${currentPeriodId}*** before the deadline ***${deadline}***\n`; const isLastScoringClosed = endDate .add(5, "d") .subtract(2, "w") .isAfter(moment()); const previousPeriodDeadline = isLastScoringClosed ? `Reports for the previous period ***#${prevPeriodId}*** are closed\n` : `Reports for the previous period ***#${prevPeriodId}*** can be submitted before ***${prevDeadline}***\n`; const latestGradedPeriod = `Latest graded period - ***#${lastGradedPeriod}***${leaderboardLink}`; const messageContent = `${hello}${currentScoring}${currentDeadline}${previousPeriodDeadline}${latestGradedPeriod}`; return messageContent; }; async function reloadScoringData() { if (nextSyncDate.isBefore(moment())) { scoringData = await loadScoringPeriodData(); lastGradedPeriod = scoringData.lastPeriod.scoringPeriodId; nextSyncDate = moment.parseZone(scoringData.currentScoringPeriod.ends); } }