-
Notifications
You must be signed in to change notification settings - Fork 40
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add emoji watcher gag * Random message deletion * Add typing status gag * Enforce american spellings * chaos community vote * init commands * remove message deletion * reply then delete * extract consts * add timeout * prettier
- Loading branch information
1 parent
4b3f04b
commit 650bb7d
Showing
5 changed files
with
295 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import { ChannelHandlers } from "../../types"; | ||
|
||
const AMERICAN_SPELLING_MAP: Record<string, string> = { | ||
colour: "color", | ||
flavour: "flavor", | ||
honour: "honor", | ||
humour: "humor", | ||
labour: "labor", | ||
neighbour: "neighbor", | ||
rumour: "rumor", | ||
savour: "savor", | ||
valour: "valor", | ||
behaviour: "behavior", | ||
favour: "favor", | ||
harbour: "harbor", | ||
odour: "odor", | ||
armour: "armor", | ||
}; | ||
|
||
const SUPER_SARCASTIC_REPLIES_TO_BRITISH_SPELLING_WORDS = [ | ||
"Oh, splendid, another dose of British charm! But I'm afraid we're strictly American today, darling.", | ||
"Well, well, well, what do we have here? Looks like someone's trying to add a little extra 'u' to our lives. Nice try!", | ||
"Oh, lovely attempt at being fancy! But sorry, old chap, we're sticking to our American roots today.", | ||
"Ah, a bit of British flair, how quaint! But alas, it's not quite the style we're going for today.", | ||
"Oh, bravo! Another British gem shining through. But sorry, it's strictly red, white, and blue today!", | ||
"Well, aren't you just a rebel with your extra letters? Sorry, but we're cutting ties with the 'u' today.", | ||
"Ah, the British invasion continues! But I'm afraid your linguistic imperialism won't fly here today.", | ||
"Oh, delightful! Another nod to our friends across the pond. But sorry, we're staying true to our American roots today.", | ||
"Splendid attempt at British sophistication! But sorry, we're keeping it simple and straightforward today.", | ||
"Oh, look at you being all fancy with your British spellings! But I'm afraid it's strictly Yankee Doodle Dandy around here today.", | ||
]; | ||
|
||
export default { | ||
handleMessage: async ({ msg: maybeMessage }) => { | ||
const msg = maybeMessage.partial | ||
? await maybeMessage.fetch() | ||
: maybeMessage; | ||
|
||
const content = msg.content.toLowerCase(); | ||
const hasBadWord = Object.keys(AMERICAN_SPELLING_MAP).find((word) => | ||
content.match(new RegExp(`\\b${word}\\b`, "i")), | ||
); | ||
|
||
if (!hasBadWord) { | ||
return; | ||
} | ||
|
||
await msg.reply( | ||
SUPER_SARCASTIC_REPLIES_TO_BRITISH_SPELLING_WORDS[ | ||
Math.floor( | ||
Math.random() * | ||
SUPER_SARCASTIC_REPLIES_TO_BRITISH_SPELLING_WORDS.length, | ||
) | ||
], | ||
); | ||
|
||
await msg.delete(); | ||
}, | ||
} as ChannelHandlers; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
import { GuildMember, MessageReaction, TextChannel, User } from "discord.js"; | ||
import { ChannelHandlers } from "../../types"; | ||
import { sleep } from "../../helpers/misc"; | ||
|
||
const RECENT_CHATTERS = new Set<string>(); | ||
|
||
let timedOutUserId: string | null = null; | ||
let isProcessingPlea = false; | ||
let isWorthy = true; | ||
let dissenter: GuildMember | null = null; | ||
let conformer: GuildMember | null = null; | ||
|
||
const TIMEOUT_DURATION_MINS = 10; | ||
const VOTING_DURATION_MINS = 2; | ||
const NUM_RECENT_CHATTERS_TO_TRIGGER_CHAOS = 10; | ||
|
||
const getRandomReactorId = ( | ||
reactions: MessageReaction, | ||
botId: string, | ||
): User | null => { | ||
const users = reactions.users.cache; | ||
const usersArray = Array.from(users.values()); | ||
const randomUser = usersArray[Math.floor(Math.random() * usersArray.length)]; | ||
|
||
if (usersArray.length === 1 && randomUser.id === botId) { | ||
return null; | ||
} | ||
|
||
while (randomUser.id === botId) { | ||
console.log("Trying to get a random user again"); | ||
return getRandomReactorId(reactions, botId); | ||
} | ||
|
||
return randomUser; | ||
}; | ||
|
||
/** | ||
* This feature allows the community to vote on whether a user should be timed out. | ||
* If the user gets timed out, they have to write a pleading message in a certain channel and the AI gods have to deem them worthy enough to be untimedout. | ||
* If they are deemed worthy, it randomly times one of the community voters out for an hour and they can't plead. | ||
*/ | ||
export default { | ||
handleMessage: async ({ msg: maybeMessage, bot }) => { | ||
const msg = maybeMessage.partial | ||
? await maybeMessage.fetch() | ||
: maybeMessage; | ||
|
||
if (msg.author.bot) { | ||
return; | ||
} | ||
|
||
if (timedOutUserId === msg.author.id) { | ||
if (isProcessingPlea || !isWorthy) { | ||
await msg.delete(); | ||
return; | ||
} | ||
|
||
isProcessingPlea = true; | ||
await msg.reply("Let's see if the AI gods deem you worthy...."); | ||
await sleep(3 + Math.floor(Math.random() * 7)); | ||
isProcessingPlea = false; | ||
isWorthy = Math.random() > 0.5; | ||
|
||
if (isWorthy) { | ||
await msg.reply( | ||
"The AI gods have deemed you worthy. You are free to go.", | ||
); | ||
timedOutUserId = null; | ||
|
||
if (conformer) { | ||
try { | ||
await conformer.timeout(TIMEOUT_DURATION_MINS * 60 * 1000); | ||
} catch (error) { | ||
console.error(error); | ||
} | ||
const channel = msg.channel as TextChannel; | ||
await channel.send(`Chaos reigns upon <@${conformer.id}> instead.`); | ||
} | ||
return; | ||
} | ||
|
||
await msg.reply( | ||
"The AI gods have deemed you unworthy. You will remain timed out.", | ||
); | ||
|
||
await msg.guild?.members.cache | ||
.get(timedOutUserId) | ||
?.timeout(TIMEOUT_DURATION_MINS * 60 * 1000); | ||
|
||
sleep(TIMEOUT_DURATION_MINS * 60).then(() => { | ||
// If the person is still timed out, remove the timeout | ||
// Otherwise, somebody else has been timed out so we just let it go | ||
if (timedOutUserId === msg.author.id) { | ||
timedOutUserId = null; | ||
} | ||
}); | ||
|
||
return; | ||
} | ||
|
||
RECENT_CHATTERS.add(msg.author.id); | ||
|
||
if (RECENT_CHATTERS.size >= NUM_RECENT_CHATTERS_TO_TRIGGER_CHAOS) { | ||
// Get a random user from recent chatters | ||
const userIds = Array.from(RECENT_CHATTERS); | ||
const randomUser = userIds[Math.floor(Math.random() * userIds.length)]; | ||
const user = await msg.client.users.fetch(randomUser); | ||
if (!user) return; | ||
|
||
const channel = msg.channel as TextChannel; | ||
const message = await channel.send( | ||
`Chaos is here. React with 👍 to time out <@${user.id}> or 👎 to let them live.`, | ||
); | ||
|
||
const filter = (reaction: MessageReaction, user: User) => | ||
user.id !== message.author.id && user.id !== bot.user?.id; | ||
|
||
const collector = message.createReactionCollector({ | ||
filter, | ||
time: 1000 * 60 * VOTING_DURATION_MINS, | ||
}); | ||
|
||
await message.react("👍"); | ||
await message.react("👎"); | ||
|
||
collector.on("end", async () => { | ||
const yesReactions = message.reactions.cache.find( | ||
(reaction) => reaction.emoji.name === "👍", | ||
); | ||
const noReactions = message.reactions.cache.find( | ||
(reaction) => reaction.emoji.name === "👎", | ||
); | ||
|
||
if (!yesReactions || !noReactions) { | ||
return; | ||
} | ||
|
||
const randomConformerId = bot?.user?.id | ||
? getRandomReactorId(yesReactions, bot.user.id) | ||
: null; | ||
conformer = randomConformerId | ||
? await message.guild?.members.fetch(randomConformerId) | ||
: null; | ||
|
||
const randomDissenterId = bot?.user?.id | ||
? getRandomReactorId(noReactions, bot.user.id) | ||
: null; | ||
dissenter = randomDissenterId | ||
? await message.guild?.members.fetch(randomDissenterId) | ||
: null; | ||
|
||
if ( | ||
yesReactions?.count && | ||
yesReactions.count > (noReactions?.count ?? 0) | ||
) { | ||
await channel.send( | ||
`The community has spoken. <@${user.id}> has been timed out. <@${user.id}>, your next message is your one attempt to plead your case. If the AI gods deem you worthy, you will be spared and chaos will reign on somebody else.`, | ||
); | ||
|
||
timedOutUserId = user.id; | ||
} else { | ||
await channel.send( | ||
`The community has spared <@${user.id}>. Do not let this happen again.`, | ||
); | ||
|
||
if (dissenter) { | ||
await dissenter.timeout(TIMEOUT_DURATION_MINS * 60 * 1000); | ||
await channel.send(`Chaos reigns upon <@${dissenter.id}> instead.`); | ||
} | ||
} | ||
}); | ||
|
||
RECENT_CHATTERS.clear(); | ||
} | ||
}, | ||
} as ChannelHandlers; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { sleep } from "../../helpers/misc"; | ||
import { ChannelHandlers } from "../../types"; | ||
|
||
const EMOJIS = [ | ||
[0.02, "👀"], | ||
[0.02, "🤔"], | ||
[0.02, "❓"], | ||
] as const; | ||
|
||
export default { | ||
handleMessage: async ({ msg: maybeMessage }) => { | ||
const msg = maybeMessage.partial | ||
? await maybeMessage.fetch() | ||
: maybeMessage; | ||
|
||
for (const [chance, emoji] of EMOJIS) { | ||
const random = Math.random(); | ||
if (random < chance) { | ||
const resp = await msg.react(emoji); | ||
|
||
// For added chaos, remove the reaction in ~30-60s | ||
const duration = Math.floor(Math.random() * 30) + 30; | ||
sleep(duration).then(() => { | ||
resp.remove(); | ||
}); | ||
|
||
return; | ||
} | ||
} | ||
}, | ||
} as ChannelHandlers; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { ChannelHandlers } from "../../types"; | ||
|
||
const CHANCE_TO_TYPE = 0.02; | ||
|
||
export default { | ||
handleMessage: async ({ msg: maybeMessage }) => { | ||
const msg = maybeMessage.partial | ||
? await maybeMessage.fetch() | ||
: maybeMessage; | ||
|
||
const random = Math.random(); | ||
if (random > CHANCE_TO_TYPE) { | ||
return; | ||
} | ||
|
||
const channel = msg.channel; | ||
await channel.sendTyping(); // This will start typing for 10 seconds (a Discord value, not customizable) | ||
}, | ||
} as ChannelHandlers; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters