Skip to content

Update help channel embed when channel is occupied #117

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Mar 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 14 additions & 6 deletions src/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,26 @@ let db: Connection | undefined;
export async function getDB() {
if (db) return db;

// Require ssl in production
const extraOpts =
process.env.NODE_ENV === 'production'
? {
ssl: true,
extra: {
ssl: {
rejectUnauthorized: false,
},
},
}
: {};

db = await createConnection({
type: 'postgres',
url: dbUrl,
synchronize: true,
logging: false,
entities: [RepUser, RepGive, HelpUser],
ssl: true,
extra: {
ssl: {
rejectUnauthorized: false,
},
},
...extraOpts,
});
console.log('Connected to DB');
return db;
Expand Down
2 changes: 2 additions & 0 deletions src/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,5 @@ export const ongoingEmptyTimeout = parseInt(process.env.ONGOING_EMPTY_TIMEOUT!);

export const TS_BLUE = '#007ACC';
export const GREEN = '#77b155';
// Picked from Discord's "hourglass" emoji (in ⌛ | Occupied Help Channels)
export const HOURGLASS_ORANGE = '#ffa647';
100 changes: 79 additions & 21 deletions src/modules/helpchan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,21 @@ import {
Guild,
TextChannel,
GuildMember,
User,
} from 'discord.js';
import { HelpUser } from '../entities/HelpUser';
import {
categories,
TS_BLUE,
GREEN,
HOURGLASS_ORANGE,
askCooldownRoleId,
channelNames,
dormantChannelTimeout,
dormantChannelLoop,
askHelpChannelId,
ongoingEmptyTimeout,
trustedRoleId,
} from '../env';
import { isTrustedMember } from '../util/inhibitors';

Expand All @@ -39,6 +42,26 @@ This channel will be dedicated to answering your question only. Others will try
For more tips, check out StackOverflow's guide on **[asking good questions](https://stackoverflow.com/help/how-to-ask)**.
`;

// The "empty" line has a braille pattern blank unicode character, in order to
// achieve a leading newline, since normally whitespace is stripped. This is a
// hack, but it works even on a system without the fonts to display Discord
// emoji, so it should work everywhere. https://www.compart.com/en/unicode/U+2800
const occupiedMessage = (asker: User) => `
**This channel is claimed by ${asker}.**
It is dedicated to answering their questions only. More info: <#${askHelpChannelId}>

**${asker} You'll get better and faster answers if you:**
• Describe the context. What are you trying to accomplish?
• Include any error messages, and the code that produce them (5-15 lines).
• Use code blocks, not screenshots. Start with ${'```ts'} for syntax highlighting.
• Also reproduce the issue in the **[TypeScript Playground](https://www.typescriptlang.org/play)**, if possible.

Usually someone will try to answer and help solve the issue within a few hours. If not, and you have followed the bullets above, you may ping the <@&${trustedRoleId}> role.

For more tips, check out StackOverflow's guide on **[asking good questions](https://stackoverflow.com/help/how-to-ask)**.
`;

const DORMANT_MESSAGE = `
This help channel has been marked as **dormant**, and has been moved into the **Help: Dormant** category at the bottom of the channel list. It is no longer possible to send messages in this channel until it becomes available again.

Expand All @@ -62,7 +85,22 @@ export class HelpChanModule extends Module {
} hours of inactivity or when you send !close.`,
);

OCCUPIED_EMBED_BASE = new MessageEmbed()
.setTitle('⌛ Occupied Help Channel')
.setColor(HOURGLASS_ORANGE);

occupiedEmbed(asker: User) {
return new MessageEmbed(this.OCCUPIED_EMBED_BASE)
.setDescription(occupiedMessage(asker))
.setFooter(
`Closes after ${
dormantChannelTimeout / 60 / 60 / 1000
} hours of inactivity or when ${asker.username} sends !close.`,
);
}

DORMANT_EMBED = new MessageEmbed()
.setTitle('💤 Dormant Help Channel')
.setColor(TS_BLUE)
.setDescription(DORMANT_MESSAGE);

Expand Down Expand Up @@ -117,9 +155,8 @@ export class HelpChanModule extends Module {
const embed = messages.first()?.embeds[0];

return (
embed &&
embed.description?.trim() ===
this.AVAILABLE_EMBED.description?.trim()
embed?.title &&
embed.title.trim() === this.OCCUPIED_EMBED_BASE.title?.trim()
);
}

Expand Down Expand Up @@ -176,6 +213,9 @@ export class HelpChanModule extends Module {

this.busyChannels.add(msg.channel.id);

let embed = this.occupiedEmbed(msg.author);

await this.updateStatusEmbed(msg.channel, embed);
await msg.pin();
await this.addCooldown(msg.member, msg.channel);
await this.moveChannel(msg.channel, categories.ongoing);
Expand Down Expand Up @@ -244,24 +284,7 @@ export class HelpChanModule extends Module {
);
if (dormant && dormant instanceof TextChannel) {
await this.moveChannel(dormant, categories.ask);

let lastMessage = dormant.messages.cache
.array()
.reverse()
.find(m => m.author.id === this.client.user?.id);

if (!lastMessage)
lastMessage = (await dormant.messages.fetch({ limit: 5 }))
.array()
.find(m => m.author.id === this.client.user?.id);

if (lastMessage) {
// If there is a last message (the dormant message) by the bot, just edit it
await lastMessage.edit(this.AVAILABLE_EMBED);
} else {
// Otherwise, just send a new message
await dormant.send(this.AVAILABLE_EMBED);
}
await this.updateStatusEmbed(dormant, this.AVAILABLE_EMBED);
} else {
const chan = await guild.channels.create(
this.getChannelName(guild),
Expand Down Expand Up @@ -321,6 +344,38 @@ export class HelpChanModule extends Module {
}
}

private async updateStatusEmbed(channel: TextChannel, embed: MessageEmbed) {
const isStatusEmbed = (embed: MessageEmbed) =>
[
this.AVAILABLE_EMBED.title,
this.OCCUPIED_EMBED_BASE.title,
this.DORMANT_EMBED.title,
].includes(embed.title);

// The message cache does not have a stable order (at least with respect
// to creation date), so sorting is needed to find the latest embed.
let lastMessage = channel.messages.cache
.array()
.filter(m => m.author.id === this.client.user?.id)
.sort((m1, m2) => m2.createdTimestamp - m1.createdTimestamp)
.find(m => m.embeds.some(isStatusEmbed));

if (!lastMessage)
// Fetch has a stable order, with recent messages first
lastMessage = (await channel.messages.fetch({ limit: 5 }))
.array()
.filter(m => m.author.id === this.client.user?.id)
.find(m => m.embeds.some(isStatusEmbed));

if (lastMessage) {
// If there is a last message (the status message) by the bot, edit it
await lastMessage.edit(embed);
} else {
// Otherwise, just send a new message
await channel.send(embed);
}
}

private async addCooldown(member: GuildMember, channel: TextChannel) {
await member.roles.add(askCooldownRoleId);
const helpUser = new HelpUser();
Expand Down Expand Up @@ -415,7 +470,10 @@ export class HelpChanModule extends Module {
.setAuthor(member.displayName, member.user.displayAvatarURL())
.setDescription(msgContent),
);

await toPin.pin();
const occupied = this.occupiedEmbed(member.user);
await this.updateStatusEmbed(claimedChannel, occupied);
await this.addCooldown(member, claimedChannel);
await this.moveChannel(claimedChannel, categories.ongoing);
await claimedChannel.send(
Expand Down