mirror of
https://github.com/pacnpal/markov-discord.git
synced 2025-12-20 03:01:04 -05:00
Some work on channel-gating
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -65,3 +65,5 @@ config.json
|
|||||||
# error output file
|
# error output file
|
||||||
error.json
|
error.json
|
||||||
markovDB.json
|
markovDB.json
|
||||||
|
|
||||||
|
config
|
||||||
|
|||||||
@@ -1,14 +1,53 @@
|
|||||||
import { SlashCommandBuilder } from '@discordjs/builders';
|
import { SlashCommandBuilder, SlashCommandChannelOption } from '@discordjs/builders';
|
||||||
import { REST } from '@discordjs/rest';
|
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 { config } from './config';
|
||||||
import { packageJson } from './util';
|
import { packageJson } from './util';
|
||||||
|
|
||||||
|
const CHANNEL_OPTIONS_MAX = 25;
|
||||||
|
|
||||||
const helpSlashCommand = new SlashCommandBuilder()
|
const helpSlashCommand = new SlashCommandBuilder()
|
||||||
.setName('help')
|
.setName('help')
|
||||||
.setDescription(`How to use ${packageJson().name}`);
|
.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) {
|
export async function deployCommands(clientId: string) {
|
||||||
const rest = new REST({ version: '9' }).setToken(config.token);
|
const rest = new REST({ version: '9' }).setToken(config.token);
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ import { Guild } from './Guild';
|
|||||||
@Entity()
|
@Entity()
|
||||||
export class Channel extends BaseEntity {
|
export class Channel extends BaseEntity {
|
||||||
@PrimaryColumn()
|
@PrimaryColumn()
|
||||||
id: string;
|
id: number;
|
||||||
|
|
||||||
@Column({
|
@Column({
|
||||||
default: true,
|
default: false,
|
||||||
})
|
})
|
||||||
listen: boolean;
|
listen: boolean;
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { Channel } from './Channel';
|
|||||||
@Entity()
|
@Entity()
|
||||||
export class Guild extends BaseEntity {
|
export class Guild extends BaseEntity {
|
||||||
@PrimaryColumn()
|
@PrimaryColumn()
|
||||||
id: string;
|
id: number;
|
||||||
|
|
||||||
@OneToMany(() => Channel, (channel) => channel.guild, { onDelete: 'CASCADE', cascade: true })
|
@OneToMany(() => Channel, (channel) => channel.guild, { onDelete: 'CASCADE', cascade: true })
|
||||||
channels: Channel[];
|
channels: Channel[];
|
||||||
|
|||||||
60
src/index.ts
60
src/index.ts
@@ -40,6 +40,13 @@ const markovOpts: MarkovConstructorOptions = {
|
|||||||
stateSize: config.stateSize,
|
stateSize: config.stateSize,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const markovGenerateOptions: MarkovGenerateOptions<MarkovDataCustom> = {
|
||||||
|
filter: (result): boolean => {
|
||||||
|
return result.score >= config.minScore;
|
||||||
|
},
|
||||||
|
maxTries: config.maxTries,
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* #v3-complete
|
* #v3-complete
|
||||||
*/
|
*/
|
||||||
@@ -50,6 +57,20 @@ async function getMarkovByGuildId(guildId: string): Promise<Markov> {
|
|||||||
return 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.
|
* Checks if the author of a message as moderator-like permissions.
|
||||||
* @param {GuildMember} member Sender of the message
|
* @param {GuildMember} member Sender of the message
|
||||||
@@ -174,20 +195,25 @@ async function generateResponse(
|
|||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
L.debug('Responding...');
|
L.debug('Responding...');
|
||||||
if (!interaction.guildId) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
const options: MarkovGenerateOptions<MarkovDataCustom> = {
|
|
||||||
filter: (result): boolean => {
|
|
||||||
return result.score >= config.minScore;
|
|
||||||
},
|
|
||||||
maxTries: config.maxTries,
|
|
||||||
};
|
|
||||||
|
|
||||||
const markov = await getMarkovByGuildId(interaction.guildId);
|
const markov = await getMarkovByGuildId(interaction.guildId);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await markov.generate<MarkovDataCustom>(options);
|
const response = await markov.generate<MarkovDataCustom>(markovGenerateOptions);
|
||||||
L.info({ response }, 'Generated response');
|
L.info({ response }, 'Generated response');
|
||||||
const messageOpts: Discord.MessageOptions = { tts };
|
const messageOpts: Discord.MessageOptions = { tts };
|
||||||
const attachmentUrls = response.refs
|
const attachmentUrls = response.refs
|
||||||
@@ -197,7 +223,6 @@ async function generateResponse(
|
|||||||
const randomRefAttachment = getRandomElement(attachmentUrls);
|
const randomRefAttachment = getRandomElement(attachmentUrls);
|
||||||
messageOpts.files = [randomRefAttachment];
|
messageOpts.files = [randomRefAttachment];
|
||||||
} else {
|
} else {
|
||||||
// TODO: This might not even work
|
|
||||||
const randomMessage = await MarkovInputData.createQueryBuilder<
|
const randomMessage = await MarkovInputData.createQueryBuilder<
|
||||||
MarkovInputData<MarkovDataCustom>
|
MarkovInputData<MarkovDataCustom>
|
||||||
>('input')
|
>('input')
|
||||||
@@ -297,19 +322,10 @@ client.on('ready', async (readyClient) => {
|
|||||||
|
|
||||||
await deployCommands(readyClient.user.id);
|
await deployCommands(readyClient.user.id);
|
||||||
|
|
||||||
const guildsToSave: Guild[] = [];
|
const guildsToSave = readyClient.guilds
|
||||||
const channelsToSave: Channel[] = [];
|
.valueOf()
|
||||||
readyClient.guilds.valueOf().forEach((guild) => {
|
.map((guild) => Guild.create({ id: parseInt(guild.id, 10) }));
|
||||||
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);
|
|
||||||
});
|
|
||||||
await Guild.upsert(guildsToSave, ['id']);
|
await Guild.upsert(guildsToSave, ['id']);
|
||||||
await Channel.upsert(channelsToSave, ['id']); // TODO: ensure this doesn't overwrite the existing `listen`
|
|
||||||
});
|
});
|
||||||
|
|
||||||
client.on('error', (err) => {
|
client.on('error', (err) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user