rcg.ts 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. import { Message } from "discord.js";
  2. import got from "got";
  3. import { logger } from "src/logging";
  4. import { Command, ICommandData, Plugin } from "src/model/plugin";
  5. import { tryDo } from "@shared/common/async_utils";
  6. import Jimp from "jimp";
  7. const rcgRe = /<div class="rcg-panels">\s*<img src="([^"]*)" .*\/>\s*<img src="([^"]*)" .*\/>\s*<img src="([^"]*)" .*\/>\s*<\/div>/i;
  8. interface XkcdResponse {
  9. img: string;
  10. }
  11. @Plugin
  12. export class Rcg {
  13. async sendErrorMessage(msg: Message): Promise<void> {
  14. const xkcdResult = await tryDo(got.get<XkcdResponse>("https://xkcd.com/info.0.json", { responseType: "json" }));
  15. if(!xkcdResult.ok || !xkcdResult.result) {
  16. await msg.reply({ content: "sorry, I couldn't get any comics :(.", failIfNotExists: false });
  17. return;
  18. }
  19. await msg.reply({
  20. content: "sorry, I couldn't get a random comic! Here is today's XKCD instead:",
  21. files: [ xkcdResult.result.body.img ]
  22. });
  23. }
  24. @Command({
  25. type: "mention",
  26. pattern: "random comic",
  27. auth: false,
  28. documentation: {description: "Generates a comic just for you!", example: "random comic"}
  29. })
  30. async randomComic({ message }: ICommandData): Promise<void> {
  31. const result = await tryDo(got.get("http://explosm.net/rcg/view/?promo=false"));
  32. if (!result.ok) {
  33. logger.error("Failed to get RCG. Got error: %s", result.error);
  34. await this.sendErrorMessage(message);
  35. return;
  36. }
  37. const regexResult = rcgRe.exec(result.result.body);
  38. if(!regexResult || regexResult.length == 0) {
  39. logger.error("Could not find RCG from body. Got response body: %s", result.result.body);
  40. await this.sendErrorMessage(message);
  41. return;
  42. }
  43. const panelUrls = [regexResult[1], regexResult[2], regexResult[3]];
  44. const panels = [];
  45. let totalWidth = 0;
  46. let maxHeight = 0;
  47. for (const panelUrl of panelUrls) {
  48. const panelImgResult = await tryDo(Jimp.read(panelUrl));
  49. if (!panelImgResult.ok) {
  50. logger.error("Failed to download panel %s", panelUrl);
  51. await this.sendErrorMessage(message);
  52. return;
  53. }
  54. const img = panelImgResult.result;
  55. const [w, h] = [ img.getWidth(), img.getHeight() ];
  56. panels.push(img);
  57. totalWidth += w;
  58. if (h > maxHeight) {
  59. maxHeight = h;
  60. }
  61. }
  62. const PADDING = 5;
  63. let newImg = await Jimp.create(PADDING * 4 + totalWidth, PADDING * 2 + maxHeight, "white");
  64. let curX = PADDING;
  65. for (const panel of panels) {
  66. newImg = await newImg.blit(panel, curX, PADDING);
  67. curX += panel.getWidth() + PADDING;
  68. }
  69. newImg.quality(80);
  70. const buffer = await newImg.getBufferAsync(Jimp.MIME_JPEG);
  71. const messagePostResult = await tryDo(message.reply({
  72. content: "I find this very funny:",
  73. files: [ buffer ]
  74. }));
  75. if (!messagePostResult.ok) {
  76. logger.error("Failed to get RCG. Got error: %s", messagePostResult.error);
  77. await this.sendErrorMessage(message);
  78. }
  79. }
  80. }