Some work on channel-gating

This commit is contained in:
Charlie Laabs
2021-12-20 23:29:01 -06:00
parent 466c122dd2
commit a2ae99d75d
5 changed files with 85 additions and 28 deletions

2
.gitignore vendored
View File

@@ -65,3 +65,5 @@ config.json
# error output file
error.json
markovDB.json
config

View File

@@ -1,14 +1,53 @@
import { SlashCommandBuilder } from '@discordjs/builders';
import { SlashCommandBuilder, SlashCommandChannelOption } from '@discordjs/builders';
import { REST } from '@discordjs/rest';
import { Routes } from 'discord-api-types/v9';
import { ChannelType, Routes } from 'discord-api-types/v9';
import { config } from './config';
import { packageJson } from './util';
const CHANNEL_OPTIONS_MAX = 25;
const helpSlashCommand = new SlashCommandBuilder()
.setName('help')
.setDescription(`How to use ${packageJson().name}`);
const commands = [helpSlashCommand.toJSON()];
/**
* Helps generate a list of parameters for channel options
*/
const channelOptionsGenerator = (builder: SlashCommandChannelOption, index: number) =>
builder
.setName(`channel-${index + 1}`)
.setDescription('A text channel')
.setRequired(index === 0)
.addChannelType(ChannelType.GuildText as any);
const listenChannelCommand = new SlashCommandBuilder()
.setName('listen')
.addSubcommand((sub) => {
sub
.setName('add')
.setDescription(
`Add channels to learn from. Doesn't add the channel's past messages; re-train to do that.`
);
Array.from(Array(CHANNEL_OPTIONS_MAX).keys()).forEach((index) =>
sub.addChannelOption((opt) => channelOptionsGenerator(opt, index))
);
return sub;
})
.addSubcommand((sub) => {
sub
.setName('remove')
.setDescription(
`Remove channels from being learned from. Doesn't remove the channel's data; re-train to do that.`
);
Array.from(Array(CHANNEL_OPTIONS_MAX).keys()).forEach((index) =>
sub.addChannelOption((opt) => channelOptionsGenerator(opt, index))
);
return sub;
})
.setDescription(`How to use ${packageJson().name}`);
const commands = [helpSlashCommand.toJSON(), listenChannelCommand.toJSON()];
export async function deployCommands(clientId: string) {
const rest = new REST({ version: '9' }).setToken(config.token);

View File

@@ -5,10 +5,10 @@ import { Guild } from './Guild';
@Entity()
export class Channel extends BaseEntity {
@PrimaryColumn()
id: string;
id: number;
@Column({
default: true,
default: false,
})
listen: boolean;

View File

@@ -5,7 +5,7 @@ import { Channel } from './Channel';
@Entity()
export class Guild extends BaseEntity {
@PrimaryColumn()
id: string;
id: number;
@OneToMany(() => Channel, (channel) => channel.guild, { onDelete: 'CASCADE', cascade: true })
channels: Channel[];

View File

@@ -40,6 +40,13 @@ const markovOpts: MarkovConstructorOptions = {
stateSize: config.stateSize,
};
const markovGenerateOptions: MarkovGenerateOptions<MarkovDataCustom> = {
filter: (result): boolean => {
return result.score >= config.minScore;
},
maxTries: config.maxTries,
};
/**
* #v3-complete
*/
@@ -50,6 +57,20 @@ async function getMarkovByGuildId(guildId: string): Promise<Markov> {
return markov;
}
/**
* #v3-complete
*/
async function isValidChannel(channelId: string): Promise<boolean> {
const id = parseInt(channelId, 10);
const channel = await Channel.findOne(id);
if (!channel) {
L.warn({ channelId }, 'Channel does not exist, setting to valid');
await Channel.create({ id }).save();
return false;
}
return channel.listen;
}
/**
* Checks if the author of a message as moderator-like permissions.
* @param {GuildMember} member Sender of the message
@@ -174,20 +195,25 @@ async function generateResponse(
): Promise<void> {
L.debug('Responding...');
if (!interaction.guildId) {
L.info('Received a message without a guildId');
L.debug('Received an interaction without a guildId');
return;
}
if (!interaction.channelId) {
L.debug('Received an interaction without a channelId');
return;
}
const isValid = await isValidChannel(interaction.channelId);
if (!isValid) {
L.debug(
{ channelId: interaction.channelId },
'Channel is not enabled for listening. Ignoring...'
);
return;
}
const options: MarkovGenerateOptions<MarkovDataCustom> = {
filter: (result): boolean => {
return result.score >= config.minScore;
},
maxTries: config.maxTries,
};
const markov = await getMarkovByGuildId(interaction.guildId);
try {
const response = await markov.generate<MarkovDataCustom>(options);
const response = await markov.generate<MarkovDataCustom>(markovGenerateOptions);
L.info({ response }, 'Generated response');
const messageOpts: Discord.MessageOptions = { tts };
const attachmentUrls = response.refs
@@ -197,7 +223,6 @@ async function generateResponse(
const randomRefAttachment = getRandomElement(attachmentUrls);
messageOpts.files = [randomRefAttachment];
} else {
// TODO: This might not even work
const randomMessage = await MarkovInputData.createQueryBuilder<
MarkovInputData<MarkovDataCustom>
>('input')
@@ -297,19 +322,10 @@ client.on('ready', async (readyClient) => {
await deployCommands(readyClient.user.id);
const guildsToSave: Guild[] = [];
const channelsToSave: Channel[] = [];
readyClient.guilds.valueOf().forEach((guild) => {
const dbGuild = Guild.create({ id: guild.id });
const textChannels = guild.channels.valueOf().filter((channel) => channel.isText());
const dbChannels = textChannels.map((channel) =>
Channel.create({ id: channel.id, guild: dbGuild })
);
guildsToSave.push(dbGuild);
channelsToSave.push(...dbChannels);
});
const guildsToSave = readyClient.guilds
.valueOf()
.map((guild) => Guild.create({ id: parseInt(guild.id, 10) }));
await Guild.upsert(guildsToSave, ['id']);
await Channel.upsert(channelsToSave, ['id']); // TODO: ensure this doesn't overwrite the existing `listen`
});
client.on('error', (err) => {