bot.ts 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. import { channelId, hydraLocation, waitFor, waitTimeUnit, createdAgo, createdAgoUnit } from "../config";
  2. import { readFileSync } from 'fs';
  3. import axios from 'axios';
  4. import {IVideoResponse, LooseObject} from './types';
  5. const moment = require('moment');
  6. const momentFormat = require("moment-duration-format");
  7. const Discord = require("discord.js");
  8. momentFormat(moment);
  9. const delay = (ms: number | undefined) => new Promise(res => setTimeout(res, ms));
  10. const queryParams = readFileSync('./query_params.json', 'utf-8');
  11. const graphql = readFileSync('./videos_query.graphql', 'utf-8').replaceAll("\n", "\\n");
  12. const httpRequestBody = readFileSync('./request.json', 'utf-8').replace('__PARAMS__', queryParams).replace('__QUERY__', graphql);
  13. const licenses: LooseObject = JSON.parse(readFileSync('./licenses.json', 'utf-8'));
  14. const main = async () => {
  15. const client = new Discord.Client();
  16. client.once("ready", async () => {
  17. console.log('Discord.js client ready');
  18. await client.channels.fetch(channelId);
  19. });
  20. await client.login(process.env.TOKEN); // environment variable TOKEN must be set
  21. console.log('Bot logged in successfully');
  22. let ids: any[] = [];
  23. do {
  24. const createdAt = moment().utc().subtract(createdAgo, createdAgoUnit); // current time minus some configurable number of time units
  25. const formattedDate = createdAt.format('YYYY-DD-MMMTHH:mm:ssZ');
  26. console.log(`Checking for new videos uploaded since ${formattedDate}`);
  27. await axios
  28. .post(hydraLocation, httpRequestBody.replace('__DATE_AFTER__', formattedDate), {headers: {'Content-Type': 'application/json'}})
  29. .then((res: any) => {
  30. let response: IVideoResponse = <IVideoResponse>res.data;
  31. if(response.data.videosConnection) {
  32. console.log(`${response.data.videosConnection.edges.length} new videos uploaded`)
  33. for (let edge of response.data.videosConnection.edges) {
  34. if (!edge.node.thumbnailPhotoDataObject) {
  35. continue; // metadata for this video is not yet ready. Video will be announced in next iterations.
  36. }
  37. if (lookup(ids, edge.node.id)) {
  38. console.log(`Video ${edge.node.id} already announced. `);
  39. } else {
  40. const channel = client.channels.cache.get(channelId);
  41. const licenseKey = edge.node.license.code;
  42. const exampleEmbed = new Discord.MessageEmbed()
  43. .setColor('#4038FF') // official joystream blue, see https://www.joystream.org/brand/guides/
  44. .setTitle(edge.node.title)
  45. .setURL(`https://play.joystream.org/video/${edge.node.id}`)
  46. .setDescription(edge.node.description.substring(1, 200)) // cut off lengthy descriptions
  47. .addFields(
  48. { name: 'ID', value: edge.node.id, inline: true },
  49. { name: 'Category', value: edge.node.category.name, inline: true},
  50. { name: 'Duration', value: durationFormat(edge.node.duration), inline: true },
  51. { name: 'Language', value: edge.node.language.iso, inline: true },
  52. { name: 'License', value: licenses[licenseKey], inline: true },
  53. )
  54. .setImage(`${edge.node.thumbnailPhotoDataObject.liaison.metadata}asset/v0/${edge.node.thumbnailPhotoDataObject.joystreamContentId}`)
  55. .setTimestamp();
  56. if(edge.node.channel.avatarPhotoDataObject && edge.node.channel.avatarPhotoDataObject.liaison) {
  57. const avatar =
  58. `${edge.node.channel.avatarPhotoDataObject.liaison.metadata}asset/v0/${edge.node.channel.avatarPhotoDataObject.joystreamContentId}`;
  59. exampleEmbed.setAuthor(edge.node.channel.title, avatar, `https://play.joystream.org/channel/${edge.node.channel.id}`);
  60. } else {
  61. exampleEmbed.setAuthor(edge.node.channel.title);
  62. }
  63. channel.send(exampleEmbed);
  64. ids.push({id: edge.node.id, createdAt: Date.parse(edge.node.createdAt)});
  65. }
  66. }
  67. cleanup(ids, createdAt);
  68. }
  69. })
  70. .catch((error: any) => {
  71. console.error(error);
  72. });
  73. // waiting...
  74. await delay(moment.duration(waitFor, waitTimeUnit).asMilliseconds());
  75. } while (true);
  76. }
  77. const cleanup = (ids: any[], cutoffDate: Date) => {
  78. console.log("Local storage cleaning in progress");
  79. let cleaned = 0;
  80. ids.reduceRight(function(acc, item, index, object) {
  81. if (item.createdAt < cutoffDate) {
  82. object.splice(index, 1);
  83. cleaned += 1;
  84. }
  85. }, []);
  86. if (cleaned > 0) {
  87. console.log(`Cleaned records: ${cleaned}`);
  88. }
  89. }
  90. const lookup = (ids: any[], id: string) => {
  91. for (let video of ids) {
  92. if (video.id == id) {
  93. return true;
  94. }
  95. }
  96. return false;
  97. }
  98. const durationFormat = (duration: number) => {
  99. if (duration < 60) {
  100. return `${duration}s.`
  101. } else {
  102. return moment.duration(duration, 'seconds').format("hh:mm:ss")
  103. }
  104. }
  105. main().catch(console.error).finally(() => process.exit());;