react.ts 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  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.bot.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. let emoji = client.bot.emojis.resolve(message.reactionEmoteId);
  78. if (emoji)
  79. msg.react(emoji);
  80. return true;
  81. }
  82. if (msg.mentions.users.size == 0)
  83. return false;
  84. let knownUsers = await usersRepo.find({
  85. select: ["mentionReactionType"],
  86. where: [...msg.mentions.users.map(u => ({ userID: u.id }))]
  87. });
  88. if (knownUsers.length == 0)
  89. return false;
  90. let reactionEmoteTypes = new Set<ReactionType>();
  91. for (let user of knownUsers) {
  92. if (user.mentionReactionType == ReactionType.NONE)
  93. continue;
  94. reactionEmoteTypes.add(user.mentionReactionType);
  95. }
  96. if(reactionEmoteTypes.size == 0)
  97. return false;
  98. let randomEmotes = await getRandomEmotes([...reactionEmoteTypes], 5);
  99. if (randomEmotes.length == 0)
  100. return false;
  101. for (let emote of randomEmotes) {
  102. let emoji = client.bot.emojis.resolve(emote.reactionId);
  103. if(emoji)
  104. await msg.react(emoji);
  105. }
  106. return true;
  107. }
  108. @Action(ActionType.INDIRECT_MENTION)
  109. async reactToPing(actionsDone: boolean, msg: Message) {
  110. if (actionsDone)
  111. return false;
  112. let emoteType = ReactionType.ANGERY;
  113. let repo = getRepository(KnownUser);
  114. let knownUser = await repo.findOne({
  115. select: ["replyReactionType"],
  116. where: [{
  117. userID: msg.author.id
  118. }]
  119. });
  120. if (knownUser) {
  121. if (knownUser.replyReactionType == ReactionType.NONE)
  122. return false;
  123. emoteType = knownUser.replyReactionType;
  124. }
  125. let emotes = await getRandomEmotes([emoteType], 1);
  126. if (emotes.length != 1)
  127. return false;
  128. let emote = client.bot.emojis.resolve(emotes[0].reactionId);
  129. if (!emote) {
  130. console.log(`WARNING: Emote ${emotes[0]} no longer is valid. Deleting invalid emojis from the list...`);
  131. let emotesRepo = getRepository(ReactionEmote);
  132. await emotesRepo.delete({ reactionId: emotes[0].reactionId });
  133. return false;
  134. }
  135. msg.channel.send(emote.toString());
  136. return true;
  137. }
  138. };