react.ts 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. import { client } from "../client";
  2. import { getRepository } from "typeorm";
  3. import { MessageReaction } from "@shared/db/entity/MessageReaction";
  4. import { KnownUser } from "@shared/db/entity/KnownUser";
  5. import { ReactionType, ReactionEmote } from "@shared/db/entity/ReactionEmote";
  6. import { isAuthorisedAsync } from "../util";
  7. import { CommandSet, Command, Action, ActionType } from "src/model/command";
  8. import { Message } from "discord.js";
  9. const pattern = /^react to\s+"([^"]+)"\s+with\s+\<:[^:]+:([^\>]+)\>$/i;
  10. async function getRandomEmotes(allowedTypes: ReactionType[], limit: number) {
  11. let reactionEmotesRepo = getRepository(ReactionEmote);
  12. let a = await reactionEmotesRepo.query(`
  13. select distinct on (type) type, "reactionId"
  14. from
  15. ( select *
  16. from reaction_emote
  17. where type in (${allowedTypes.map((s, i) => `$${i + 1}`).join(",")})
  18. order by random(), random()
  19. limit ${limit}
  20. ) as sub`, allowedTypes) as ReactionEmote[];
  21. return a;
  22. }
  23. @CommandSet
  24. export class ReactCommands {
  25. @Command({ pattern: "react to", auth: true, documentation: {description: "React to <message> with <emote>.", example: "react to \"<message>\" with <emote>"} })
  26. async addReaction(msg: Message, s: string) {
  27. if (!await isAuthorisedAsync(msg.member))
  28. return;
  29. let contents = pattern.exec(s);
  30. if (contents != null) {
  31. let reactable = contents[1].trim().toLowerCase();
  32. let reactionEmoji = contents[2];
  33. if (!client.emojis.cache.has(reactionEmoji)) {
  34. msg.channel.send(`${msg.author.toString()} I cannot react with this emoji :(`);
  35. return;
  36. }
  37. let repo = getRepository(MessageReaction);
  38. let message = repo.create({
  39. message: reactable,
  40. reactionEmoteId: reactionEmoji
  41. });
  42. await repo.save(message);
  43. msg.channel.send(`${msg.author.toString()} Added reaction!`);
  44. }
  45. }
  46. @Command({ pattern: "remove reaction to", auth: true, documentation: {description: "Stops reacting to <message>.", example: "remove reaction to <message>"} })
  47. async removeReaction(msg: Message, s: string) {
  48. if (!await isAuthorisedAsync(msg.member))
  49. return;
  50. let content = s.substring("remove reaction to ".length).trim().toLowerCase();
  51. let repo = getRepository(MessageReaction);
  52. let result = await repo.delete({ message: content });
  53. if (result.affected == 0) {
  54. msg.channel.send(`${msg.author.toString()} No such reaction available!`);
  55. return;
  56. }
  57. msg.channel.send(`${msg.author.toString()} Removed reaction!`);
  58. }
  59. @Command({ pattern: "reactions", documentation: {description: "Lists all known messages this bot can react to.", example: "reactions"} })
  60. async listReactions(msg: Message) {
  61. let reactionsRepo = getRepository(MessageReaction);
  62. let messages = await reactionsRepo.find({
  63. select: ["message"]
  64. });
  65. let reactions = messages.reduce((p, c) => `${p}\n${c.message}`, "");
  66. msg.channel.send(`I'll react to the following messages:\n\`\`\`${reactions}\n\`\`\``);
  67. }
  68. @Action(ActionType.MESSAGE)
  69. async reactToMentions(actionsDone: boolean, msg: Message, content: string) {
  70. if (actionsDone)
  71. return false;
  72. let lowerContent = content.toLowerCase();
  73. let reactionRepo = getRepository(MessageReaction);
  74. let usersRepo = getRepository(KnownUser);
  75. let message = await reactionRepo.findOne({ message: lowerContent });
  76. if (message) {
  77. msg.react(client.emojis.resolve(message.reactionEmoteId));
  78. return true;
  79. }
  80. if (msg.mentions.users.size == 0)
  81. return false;
  82. let knownUsers = await usersRepo.find({
  83. select: ["mentionReactionType"],
  84. where: [...msg.mentions.users.map(u => ({ userID: u.id }))]
  85. });
  86. if (knownUsers.length == 0)
  87. return false;
  88. let reactionEmoteTypes = new Set<ReactionType>();
  89. for (let user of knownUsers) {
  90. if (user.mentionReactionType == ReactionType.NONE)
  91. continue;
  92. reactionEmoteTypes.add(user.mentionReactionType);
  93. }
  94. if(reactionEmoteTypes.size == 0)
  95. return false;
  96. let randomEmotes = await getRandomEmotes([...reactionEmoteTypes], 5);
  97. if (randomEmotes.length == 0)
  98. return false;
  99. for (let emote of randomEmotes)
  100. await msg.react(client.emojis.cache.find(e => e.id == emote.reactionId));
  101. return true;
  102. }
  103. @Action(ActionType.INDIRECT_MENTION)
  104. async reactToPing(actionsDone: boolean, msg: Message) {
  105. if (actionsDone)
  106. return false;
  107. let emoteType = ReactionType.ANGERY;
  108. let repo = getRepository(KnownUser);
  109. let knownUser = await repo.findOne({
  110. select: ["replyReactionType"],
  111. where: [{
  112. userID: msg.author.id
  113. }]
  114. });
  115. if (knownUser) {
  116. if (knownUser.replyReactionType == ReactionType.NONE)
  117. return false;
  118. emoteType = knownUser.replyReactionType;
  119. }
  120. let emotes = await getRandomEmotes([emoteType], 1);
  121. if (emotes.length != 1)
  122. return false;
  123. let emote = client.emojis.cache.find(e => e.id == emotes[0].reactionId);
  124. if (!emote) {
  125. console.log(`WARNING: Emote ${emotes[0]} no longer is valid. Deleting invalid emojis from the list...`);
  126. let emotesRepo = getRepository(ReactionEmote);
  127. await emotesRepo.delete({ reactionId: emotes[0].reactionId });
  128. return false;
  129. }
  130. msg.channel.send(emote.toString());
  131. return true;
  132. }
  133. };