portal.ts 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. import { type Handler } from "aws-lambda";
  2. import { createRestAPIClient } from "masto";
  3. import ScraperArticles from "../utils/scraper-articles";
  4. import RedisClient from "../libs/redis-client";
  5. import LogLevels from "../enums/log-levels";
  6. import Emojis from "../enums/emojis";
  7. import config from "../config";
  8. import { type IScraperArticlesOptions } from "../interfaces/scaper-articles-options";
  9. export default class Portal {
  10. private readonly _name: string;
  11. private readonly _scraperArticlesOptions: IScraperArticlesOptions
  12. private readonly _redisClient: RedisClient;
  13. private readonly _mastodonClient: any
  14. private readonly _scraperArticles: ScraperArticles;
  15. constructor (name: string, accessToken: string, scraperArticlesOptions: IScraperArticlesOptions) {
  16. this._name = name;
  17. this._scraperArticlesOptions = scraperArticlesOptions;
  18. this._scraperArticles = new ScraperArticles(this._name, this._scraperArticlesOptions);
  19. this._redisClient = new RedisClient();
  20. this._mastodonClient = createRestAPIClient({ url: config.MASTODON_URL, accessToken });
  21. }
  22. public async run (event?: any, context?: any): Promise<void> {
  23. try {
  24. const articles = await this._scraperArticles.getArticles();
  25. if (config.LOG_LEVEL === LogLevels.DEBUG) {
  26. console.log(`${this._name} | Articles`, articles);
  27. }
  28. let totalPublished = 0;
  29. const length = articles.length;
  30. // Order has to be reversed to appear in the correct order when posting
  31. for (let i = length - 1; i >= 0; i--) {
  32. const article = articles[i];
  33. const exists = await this._redisClient.retrieve(article.link);
  34. if (exists !== null) {
  35. continue;
  36. }
  37. const date = new Date(Date.now()).toLocaleDateString();
  38. let message = `${Emojis.NEWS} ${article.title}`;
  39. if (article.content !== "") {
  40. message += `\n\n${article.content}`;
  41. }
  42. message += `\n${Emojis.LINK} ${article.link}`;
  43. if (message.trim().length === 0) {
  44. continue;
  45. }
  46. // Adding hashtags if there is
  47. if (this._scraperArticlesOptions.hashtags) {
  48. message += `\n${Emojis.TAGS} ` ;
  49. this._scraperArticlesOptions.hashtags.forEach(hashtag => {
  50. message += "#" + hashtag + " "
  51. });
  52. }
  53. message.trimEnd();
  54. // To avoid publiposts (I'm lookint at you La Tercera >:-|)
  55. if (article.link.includes("publirreportajes")) {
  56. continue;
  57. }
  58. if (article.title.includes("Exclusivo suscriptor")) {
  59. article.title = article.title.replace("Exclusivo suscriptor", "");
  60. }
  61. // If the message is more than 400 characters long, its very likely due to the article.content
  62. if (message.length > 400) {
  63. message = `${Emojis.NEWS} ${article.title}.\n\n${article.content}`.substring(0, 397) + "...";
  64. message += `\n${Emojis.LINK} ${article.link}`;
  65. }
  66. if (this._name.toUpperCase() == "Chile Cultura".toUpperCase()) {
  67. // Icons Chilecultura
  68. let regex = new RegExp(/\n(\d{1,2} \w{3})\s*\n?\s*-\s*(\d{1,2} \w{3})/);
  69. message = message.replace("date_range", `${Emojis.CALENDAR} `);
  70. message = message.replace(regex, (match, p1, p2) => {
  71. return `${p1} - ${p2}`;
  72. });
  73. message = message.replace("location_on", Emojis.PIN).replace(`${Emojis.PIN}\n`, `${Emojis.PIN} `);
  74. // Expresión regular para capturar el texto antes del 📆 seguido de una fecha
  75. regex = new RegExp(/(.*?)📆 (\d{1,2} \w{3} - \d{1,2} \w{3})/);
  76. message = message.replace(regex, (match, p1, p2) => {
  77. return `🎭 ${p1}\n📆 ${p2}`;
  78. });
  79. }
  80. const mediaIds: any[] = [];
  81. if (article.image !== null && article.image !== undefined) {
  82. const media = await this._mastodonClient.v2.media.create({ file: article.image, description: article.title });
  83. mediaIds.push(media.id);
  84. }
  85. console.log(`\n${this._name} | Sending`, message);
  86. await this._mastodonClient.v1.statuses.create({ status: message, mediaIds });
  87. await this._redisClient.store(
  88. article.link,
  89. date,
  90. { EX: this._scraperArticlesOptions.cacheExpiration ? this._scraperArticlesOptions.cacheExpiration : 60 * 60 * 24 } // EX: 24 hrs expiration
  91. );
  92. totalPublished++
  93. }
  94. console.log(`${this._name} | Published ${totalPublished} new articles`);
  95. } catch (err: any) {
  96. console.log(`${this._name} | An error has occurred\n`)
  97. console.error(err.message);
  98. if (config.LOG_LEVEL === LogLevels.DEBUG) {
  99. if (event !== undefined) {
  100. console.debug("\nEvent\n");
  101. console.debug(event);
  102. }
  103. if (context !== undefined) {
  104. console.debug("\nContext\n");
  105. console.debug(context);
  106. }
  107. }
  108. }
  109. console.log(`${this._name} | Finished`);
  110. }
  111. public getHandler (): Handler {
  112. return async (event, context) => {
  113. await this.run(event, context);
  114. }
  115. }
  116. }