react.ts 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. import { client } from "../client";
  2. import { ICommand } from "./command";
  3. import { getRepository } from "typeorm";
  4. import { MessageReaction } from "@db/entity/MessageReaction";
  5. import { KnownUser } from "@db/entity/KnownUser";
  6. import { ReactionType, ReactionEmote } from "@db/entity/ReactionEmote";
  7. import { isAuthorisedAsync } from "../util";
  8. const pattern = /^react to\s+"([^"]+)"\s+with\s+\<:[^:]+:([^\>]+)\>$/i;
  9. async function getRandomEmotes(allowedTypes: ReactionType[], limit: number) {
  10. let reactionEmotesRepo = getRepository(ReactionEmote);
  11. let a = await reactionEmotesRepo.query(`
  12. select distinct on (type) type, "reactionId"
  13. from
  14. ( select *
  15. from reaction_emote
  16. where type in (${allowedTypes.map((s, i) => `$${i + 1}`).join(",")})
  17. order by random(), random()
  18. limit ${limit}
  19. ) as sub`, allowedTypes) as ReactionEmote[];
  20. return a;
  21. }
  22. export default {
  23. commands: [
  24. {
  25. pattern: "react to",
  26. action: async (msg, s) => {
  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.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. },
  47. {
  48. pattern: "remove reaction to",
  49. action: async (msg, s) => {
  50. if (!await isAuthorisedAsync(msg.member))
  51. return;
  52. let content = s.substring("remove reaction to ".length).trim().toLowerCase();
  53. let repo = getRepository(MessageReaction);
  54. let result = await repo.delete({ message: content });
  55. if (result.affected == 0) {
  56. msg.channel.send(`${msg.author.toString()} No such reaction available!`);
  57. return;
  58. }
  59. msg.channel.send(`${msg.author.toString()} Removed reaction!`);
  60. }
  61. },
  62. {
  63. pattern: "reactions",
  64. action: async msg => {
  65. let reactionsRepo = getRepository(MessageReaction);
  66. let messages = await reactionsRepo.find({
  67. select: [ "message" ]
  68. });
  69. let reactions = messages.reduce((p, c) => `${p}\n${c.message}`, "");
  70. msg.channel.send(`I'll react to the following messages:\n\`\`\`${reactions}\n\`\`\``);
  71. }
  72. }
  73. ],
  74. documentation: {
  75. "react to \"<message>\" with <emote>": {
  76. auth: true,
  77. description: "React to <message> with <emote>."
  78. },
  79. "remove reaction to <message>": {
  80. auth: true,
  81. description: "Stops reacting to <message>."
  82. },
  83. "reactions": {
  84. auth: false,
  85. description: "Lists all known messages this bot can react to."
  86. }
  87. },
  88. onMessage: async (actionsDone, msg, content) => {
  89. if (actionsDone)
  90. return false;
  91. let lowerContent = content.toLowerCase();
  92. let reactionRepo = getRepository(MessageReaction);
  93. let usersRepo = getRepository(KnownUser);
  94. let message = await reactionRepo.findOne({ message: lowerContent });
  95. if(message) {
  96. msg.react(client.emojis.get(message.reactionEmoteId));
  97. return true;
  98. }
  99. if (msg.mentions.users.size == 0)
  100. return false;
  101. let knownUsers = await usersRepo.find({
  102. select: [ "mentionReactionType" ],
  103. where: [...msg.mentions.users.map(u => ({ userID: u.id }))]
  104. });
  105. if(knownUsers.length == 0)
  106. return false;
  107. let reactionEmoteTypes = new Set<ReactionType>();
  108. for(let user of knownUsers) {
  109. if(user.mentionReactionType == ReactionType.NONE)
  110. continue;
  111. reactionEmoteTypes.add(user.mentionReactionType);
  112. }
  113. let randomEmotes = await getRandomEmotes([...reactionEmoteTypes], 5);
  114. if(randomEmotes.length == 0)
  115. return false;
  116. for(let emote of randomEmotes)
  117. await msg.react(client.emojis.find(e => e.id == emote.reactionId));
  118. return true;
  119. },
  120. onIndirectMention: async (actionsDone, msg) => {
  121. if (actionsDone)
  122. return false;
  123. let emoteType = ReactionType.ANGERY;
  124. let repo = getRepository(KnownUser);
  125. let knownUser = await repo.findOne({
  126. select: [ "replyReactionType" ],
  127. where: [{
  128. userID: msg.author.id
  129. }]
  130. });
  131. if(knownUser){
  132. if(knownUser.replyReactionType == ReactionType.NONE)
  133. return false;
  134. emoteType = knownUser.replyReactionType;
  135. }
  136. let emotes = await getRandomEmotes([ emoteType ], 1);
  137. if(emotes.length != 1)
  138. return false;
  139. let emote = client.emojis.find(e => e.id == emotes[0].reactionId);
  140. if (!emote) {
  141. console.log(`WARNING: Emote ${emotes[0]} no longer is valid. Deleting invalid emojis from the list...`);
  142. let emotesRepo = getRepository(ReactionEmote);
  143. await emotesRepo.delete({ reactionId: emotes[0].reactionId });
  144. return false;
  145. }
  146. msg.channel.send(emote.toString());
  147. return true;
  148. }
  149. } as ICommand;