bot-en.ts 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. import TelegramBot, { SendMessageOptions } from "node-telegram-bot-api";
  2. import * as dotenv from "dotenv";
  3. import axios from "axios";
  4. import moment from "moment";
  5. import { Client } from "discord.js";
  6. // import { Client, Intents, Options } from "discord.js";
  7. interface ScoringPeriodData {
  8. currentScoringPeriod: {
  9. scoringPeriodId: number;
  10. started: string;
  11. ends: string;
  12. referralCode: number;
  13. };
  14. lastPeriod: {
  15. scoringPeriodId: number;
  16. started: string;
  17. ends: string;
  18. referralCode: number;
  19. totalDirectScore: number;
  20. totalReferralScore: number;
  21. highlights: string[];
  22. };
  23. }
  24. dotenv.config();
  25. const tgApiKey = process.env.TG_API_KEY_EN || "";
  26. const bot = new TelegramBot(tgApiKey, { polling: true });
  27. const discordToken = process.env.DISCORD_TOKEN || "";
  28. const client = new Client();
  29. let nextSyncDate = moment();
  30. let scoringData = {} as ScoringPeriodData;
  31. let lastGradedPeriod = null as number | null;
  32. const messageDeletionTimeout = 60000;
  33. client.login(discordToken);
  34. client.on("ready", async () => {
  35. if (!client.user) return;
  36. console.log(`Logged in to discord as ${client.user.tag}!`);
  37. });
  38. moment.locale("en");
  39. const loadScoringPeriodData = async () => {
  40. const url =
  41. "https://raw.githubusercontent.com/Joystream/founding-members/main/data/fm-info.json";
  42. const { data } = await axios.get(url);
  43. return data.scoringPeriodsFull;
  44. };
  45. client.on("message", async (message) => {
  46. await reloadScoringData();
  47. if (message.content === "!help") {
  48. 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`;
  49. message.channel.send({content: helpMessage}).then( (msg: { delete: () => void; }) => {
  50. if (message.channel.type !== "dm") {
  51. message.delete()
  52. setTimeout(() => {
  53. msg.delete()
  54. }, messageDeletionTimeout)
  55. }
  56. });
  57. }
  58. if (message.content === "!scoring") {
  59. const leaderboardLink = `\nCheck scores here: https://t.me/JoystreamLeaderboardBot\nSubmit your report here: https://www.joystream.org/founding-members/form`;
  60. const submitReportLink = "submit your report";
  61. const messageContent = generateScoringPeriodMessage(
  62. message.author.username,
  63. submitReportLink,
  64. leaderboardLink
  65. );
  66. message.channel.send({content: messageContent}).then( (msg: { suppressEmbeds: (arg0: boolean) => void; delete: () => void; }) => {
  67. msg.suppressEmbeds(true)
  68. if (message.channel.type !== "dm") {
  69. message.delete()
  70. setTimeout(() => {
  71. msg.delete()
  72. }, messageDeletionTimeout)
  73. }
  74. });
  75. }
  76. });
  77. bot.on("message", async (msg: TelegramBot.Message) => {
  78. await reloadScoringData();
  79. if (msg && msg.from) {
  80. console.log(msg.chat);
  81. const chatId = msg.chat.id;
  82. const username = `${msg.from.first_name} ${
  83. msg.from.last_name || ""
  84. }`.trim();
  85. const userParsed = `[${username}](tg://user?id=${msg.from.id})`;
  86. const leaderboardLink = ` - [Check scores](https://t.me/JoystreamLeaderboardBot)`;
  87. const options: SendMessageOptions = {
  88. parse_mode: "Markdown",
  89. disable_web_page_preview: true,
  90. };
  91. if (msg.text?.startsWith("/scoring")) {
  92. const submitReportLink = "[submit your report](https://www.joystream.org/founding-members/form)";
  93. const messageContent = generateScoringPeriodMessage(
  94. userParsed,
  95. submitReportLink,
  96. leaderboardLink
  97. );
  98. bot.sendMessage(chatId, messageContent, options).then((message) => {
  99. try {
  100. bot.deleteMessage(chatId, msg.message_id.toString());
  101. } catch (e) {}
  102. setTimeout(() => {
  103. try {
  104. bot.deleteMessage(chatId, message.message_id.toString());
  105. } catch (e) {}
  106. }, messageDeletionTimeout);
  107. });
  108. }
  109. }
  110. });
  111. const generateScoringPeriodMessage = (
  112. userParsed: string,
  113. submitReportLink: string,
  114. leaderboardLink: string
  115. ) => {
  116. const startDate = moment.parseZone(scoringData.currentScoringPeriod.started);
  117. const endDate = moment.parseZone(scoringData.currentScoringPeriod.ends);
  118. const duration = moment.duration(endDate.diff(moment()));
  119. const daysDuration = duration.asDays().toFixed();
  120. let dayText = "days";
  121. if (daysDuration === "1") {
  122. dayText = "day";
  123. }
  124. const daysLeft = `***${duration
  125. .asDays()
  126. .toFixed()} ${dayText} on ${endDate.format(
  127. "dddd DD MMM"
  128. )} at ${endDate.format("HH:mm")}***`;
  129. const deadline = endDate.add(5, "d").format("dddd DD MMM HH:mm");
  130. const prevDeadline = startDate.add(5, "d").format("dddd DD MMM HH:mm");
  131. const hello = `Hello ${userParsed}!\n`;
  132. const currentPeriodId = scoringData.currentScoringPeriod.scoringPeriodId;
  133. const prevPeriodId = scoringData.currentScoringPeriod.scoringPeriodId - 1;
  134. const currentScoring = `The current scoring period ***#${currentPeriodId}*** ends in ${daysLeft}\n`;
  135. const currentDeadline = `Please make sure to ${submitReportLink} for period ***#${currentPeriodId}*** before the deadline ***${deadline}***\n`;
  136. const isLastScoringClosed = endDate
  137. .add(5, "d")
  138. .subtract(2, "w")
  139. .isAfter(moment());
  140. const previousPeriodDeadline = isLastScoringClosed
  141. ? `Reports for the previous period ***#${prevPeriodId}*** are closed\n`
  142. : `Reports for the previous period ***#${prevPeriodId}*** can be submitted before ***${prevDeadline}***\n`;
  143. const latestGradedPeriod = `Latest graded period - ***#${lastGradedPeriod}***${leaderboardLink}`;
  144. const messageContent = `${hello}${currentScoring}${currentDeadline}${previousPeriodDeadline}${latestGradedPeriod}`;
  145. return messageContent;
  146. };
  147. async function reloadScoringData() {
  148. if (nextSyncDate.isBefore(moment())) {
  149. scoringData = await loadScoringPeriodData();
  150. lastGradedPeriod = scoringData.lastPeriod.scoringPeriodId;
  151. nextSyncDate = moment.parseZone(scoringData.currentScoringPeriod.ends);
  152. }
  153. }