Skip to content

Commit

Permalink
1.1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
GalvinPython committed Sep 7, 2024
1 parent 76dc7b0 commit b8033df
Show file tree
Hide file tree
Showing 8 changed files with 115 additions and 24 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ Discord has a rate limit for publishing channels of 10 announcements per server
Please do not use this in your server if you exceed this limit

# New in 1.1.0
The bot has been restructured - mainly because of the admin permissions and how volatile it could be. Whilst it worked, I wanted to ditch it and make it more secure. However, it is now slightly more complicated to setup, hence why there's a guide
The bot has been restructured - mainly because of the admin permissions and how volatile it could be. Whilst it worked, I wanted to ditch it and make it more secure. You now have more control over what gets published and when it joins a server, will automatically add all needed permissions to it.
*Not all features that were meant to be in 1.1.0 were added in it. That's for 1.1.1 as I had to ship it early*

# Website
The website for the bot is [here](https://autopublish.galvindev.me.uk)!
Expand Down
11 changes: 7 additions & 4 deletions bot/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import client from '.';
import { ApplicationCommandOptionType, ChannelType, CommandInteractionOptionResolver, PermissionFlagsBits, type CommandInteraction } from 'discord.js';
import quickEmbed from './quickEmbed';
import type { Command } from './types/commands';
import { addChannelToGuild, checkChannelIsPaused, getChannelIdsOfGuild, hasGuildPaused, removeChannelFromGuild } from './database';
import { addChannelToGuild, checkChannelIsPaused, getChannelIdsOfGuild, hasGuildPaused, pauseGuild, removeChannelFromGuild, unpauseGuild } from './database';

const commands: Record<string, Command> = {
ping: {
Expand Down Expand Up @@ -150,7 +150,7 @@ const commands: Record<string, Command> = {
const guildHasPaused = await hasGuildPaused(guildId);

const channels = await interaction.guild.channels.fetch();
const accessibleChannels = channels?.filter(channel => channel && channel.permissionsFor(interaction.client.user)?.has(PermissionFlagsBits.ViewChannel) && channel.type == ChannelType.GuildAnnouncement);
const accessibleChannels = channels?.filter(channel => channel && client.user && channel.permissionsFor(client.user)?.has(PermissionFlagsBits.ViewChannel) && channel.permissionsFor(client.user)?.has(PermissionFlagsBits.ManageMessages) && channel.type == ChannelType.GuildAnnouncement);
const accessibleChannelsIds = accessibleChannels?.map(channel => channel?.id);

const channelsCanPublishFrom: string[] = [];
Expand All @@ -171,7 +171,7 @@ const commands: Record<string, Command> = {

const canPublishEmbed = quickEmbed({
color: 'Green',
title: 'Channels the bot can publish from',
title: 'Channels the bot will publish from',
description: channelsCanPublishFrom.length > 0 ? channelsCanPublishFrom.map(id => `<#${id}>`).join('\n') : 'No channels available'
}, interaction);

Expand All @@ -183,7 +183,8 @@ const commands: Record<string, Command> = {

await interaction.reply({
ephemeral: true,
embeds: guildHasPaused ? [canPublishEmbed, barredEmbed, pausedEmbed] : [canPublishEmbed, barredEmbed]
embeds: guildHasPaused ? [canPublishEmbed, barredEmbed, pausedEmbed] : [canPublishEmbed, barredEmbed],
content: 'If there are any missing channels, please check that the bot has both the `View Channel` and `Manage Messages` permissions.'
}).catch(console.error);
},
},
Expand Down Expand Up @@ -321,12 +322,14 @@ const commands: Record<string, Command> = {
await interaction.reply('Already paused in the entire server');
return;
}
pauseGuild(interaction.guildId);
await interaction.reply('Paused publishing in the entire server');
} else if (action === 'resume') {
if (!await hasGuildPaused(interaction.guildId)) {
await interaction.reply('Guild is not paused');
return;
}
unpauseGuild(interaction.guildId);
await interaction.reply('Resumed publishing in the entire server');
}
}
Expand Down
73 changes: 56 additions & 17 deletions bot/database.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,30 @@
import { Database } from 'bun:sqlite';
import path from 'path';
import type { DatabaseStructureChannels, DatabaseStructureGuilds } from './types/database';

const db: Database = new Database(path.join(__dirname, 'db.sqlite'));

interface DatabaseStructure {
channel_id: string;
guild_id: string;
guild_paused: boolean;
}
export let dbPausedChannels: Record<string, string[]> = {};
export let dbPausedGuilds: string[] = [];

export async function initDatabase(): Promise<boolean> {
try {
db.query(`
CREATE TABLE IF NOT EXISTS guilds (
CREATE TABLE IF NOT EXISTS channels (
channel_id TEXT NOT NULL PRIMARY KEY,
guild_id TEXT NOT NULL,
guild_paused BOOLEAN DEFAULT FALSE
);
guild_id TEXT NOT NULL
)
`).run();

db.query(`
CREATE TABLE IF NOT EXISTS guilds (
guild_id TEXT NOT NULL PRIMARY KEY,
guild_paused BOOLEAN NOT NULL DEFAULT FALSE
)
`).run();
console.log('Database initialized');
await updatePausedList();
setInterval(updatePausedList, 1000 * 60)
return true;
} catch (error) {
console.error('Error initializing database:', error);
Expand All @@ -27,9 +33,8 @@ export async function initDatabase(): Promise<boolean> {
}

export async function getChannelIdsOfGuild(guildId: string): Promise<string[]> {
const result = db.query('SELECT channel_id FROM guilds WHERE guild_id = $guildId').all({ $guildId: guildId });
console.dir(result, { depth: null });
return (result as DatabaseStructure[]).map((row: DatabaseStructure) => row.channel_id);
const result = db.query('SELECT channel_id FROM channels WHERE guild_id = $guildId').all({ $guildId: guildId });
return (result as DatabaseStructureChannels[]).map((row: DatabaseStructureChannels) => row.channel_id);
}

export async function hasGuildPaused(guildId: string): Promise<boolean> {
Expand All @@ -38,22 +43,56 @@ export async function hasGuildPaused(guildId: string): Promise<boolean> {
}

export async function pauseGuild(guildId: string): Promise<void> {
db.query('INSERT INTO guilds (channel_id, guild_id, guild_paused) VALUES (0, $guildId, $guildPaused)').run({ $guildId: guildId, $guildPaused: true });
db.query('UPDATE guilds SET guild_paused = TRUE WHERE guild_id = $guildId').run({ $guildId: guildId });
}

export async function unpauseGuild(guildId: string): Promise<void> {
db.query('DELETE FROM guilds WHERE guild_id = $guildId AND guildPaused = TRUE AND channel_id = 0').run({ $guildId: guildId });
db.query('UPDATE guilds SET guild_paused = FALSE WHERE guild_id = $guildId').run({ $guildId: guildId });
}

export async function addChannelToGuild(guildId: string, channelId: string): Promise<void> {
db.query('INSERT INTO guilds (channel_id, guild_id, guild_paused) VALUES ($channelId, $guildId, FALSE)').run({ $channelId: channelId, $guildId: guildId });
db.query('INSERT INTO channels (channel_id, guild_id) VALUES ($channelId, $guildId)').run({ $channelId: channelId, $guildId: guildId });
}

export async function removeChannelFromGuild(guildId: string, channelId: string): Promise<void> {
db.query('DELETE FROM guilds WHERE guild_id = $guildId AND channel_id = $channelId').run({ $guildId: guildId, $channelId: channelId });
db.query('DELETE FROM channels WHERE guild_id = $guildId AND channel_id = $channelId').run({ $guildId: guildId, $channelId: channelId });
}

export async function checkChannelIsPaused(guildId: string, channelId: string): Promise<boolean> {
const result = db.query('SELECT guild_paused FROM guilds WHERE guild_id = $guildId AND channel_id = $channelId').get({ $guildId: guildId, $channelId: channelId });
const result = db.query('SELECT guild_paused FROM guilds WHERE guild_id = $guildId').get({ $guildId: guildId, $channelId: channelId });
return !(!result)
}

//#region Guild Management
export async function addNewGuild(guildId: string): Promise<void> {
db.query('INSERT INTO guilds (guild_id) VALUES ($guildId').run({ $guildId: guildId });
}
export async function removeGuild(guildId: string): Promise<void> {
db.query('DELETE FROM guilds WHERE guild_id = $guildId').run({ $guildId: guildId });
db.query('DELETE FROM channels WHERE guild_id = $guildId').run({ $guildId: guildId });
}
//#endregion

//#region Update
// Update dbPausedChannels and dbPausedGuilds every minute
export async function updatePausedList() {
dbPausedChannels = {};
dbPausedGuilds = [];

const resultGuilds = db.query('SELECT * FROM guilds').all();
(resultGuilds as DatabaseStructureGuilds[]).forEach((row: DatabaseStructureGuilds) => {
if (row.guild_paused) {
dbPausedGuilds.push(row.guild_id);
}
});

const resultChannels = db.query('SELECT * FROM channels').all();
(resultChannels as DatabaseStructureChannels[]).forEach((row: DatabaseStructureChannels) => {
if (!dbPausedChannels[row.guild_id]) dbPausedChannels[row.guild_id] = [];
dbPausedChannels[row.guild_id].push(row.channel_id);
});

console.dir(dbPausedChannels, { depth: null });
console.dir(dbPausedGuilds, { depth: null });
}
//#endregion
21 changes: 20 additions & 1 deletion bot/events/guildAdd.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Events, EmbedBuilder, TextChannel } from 'discord.js';
import { Events, EmbedBuilder, TextChannel, PermissionFlagsBits, ChannelType } from 'discord.js';
import client from '../index';
import { addNewGuild } from '../database';

client.on(Events.GuildCreate, async guild => {
try {
Expand Down Expand Up @@ -44,4 +45,22 @@ client.on(Events.GuildCreate, async guild => {
} catch (error) {
console.error(`Could not fetch the owner or send a message: ${error}`);
}

// Try adding the bot permissions to announcement channels
try {
const announcementChannels = guild.channels.cache.filter(channel => channel.type === ChannelType.GuildAnnouncement);
announcementChannels.forEach(async channel => {
if (client.user) {
await channel.permissionOverwrites.create(client.user.id, {
ManageChannels: true,
ManageMessages: true,
SendMessages: true,
ViewChannel: true,
})
}
});
addNewGuild(guild.id);
} catch (error) {
console.error(`Could not add permissions to announcement channels: ${error}`);
}
});
14 changes: 14 additions & 0 deletions bot/events/messageCreate.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@
import { Message, ChannelType } from 'discord.js';
import client from '../index';
import log from '../log';
import { dbPausedChannels, dbPausedGuilds } from '../database';

// Run this event whenever a message has been sent
client.on('messageCreate', async (message: Message) => {
if (message.channel.type == ChannelType.GuildAnnouncement) {
const guildId = message.guildId
const channelId = message.channelId

if (!guildId || !channelId) return;

if (dbPausedGuilds.includes(guildId) || dbPausedChannels[guildId]?.includes(channelId)) {
log(false, `Message in paused channel:
${channelId} [${message.channel.name}]
(${guildId}, ${message.guild?.name})`
)
return
}

try {
await message.crosspost()
log(true, `Published message in:
Expand Down
6 changes: 6 additions & 0 deletions bot/log.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ import client from '.';

const targetChannel = process.env.DISCORD_LOGGING_CHANNEL;

/**
* Logs to the logging channel
* @param success Display error if false, success if true
* @param msg The message to be logged
* @returns void
*/
export default async function (success: boolean, msg: string) {
if (!targetChannel || targetChannel === undefined || targetChannel === '') return;

Expand Down
9 changes: 9 additions & 0 deletions bot/types/database.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export interface DatabaseStructureChannels {
channel_id: string;
guild_id: string;
}

export interface DatabaseStructureGuilds {
guild_id: string;
guild_paused: boolean;
}
2 changes: 1 addition & 1 deletion site/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ <h3>Links</h3>
<a href="https://twitter.com/reallygalvin">Twitter</a>
</section>
<br>
<span><b>Current version (1.0.3)</b> released <b>30th June 2024</b></span>
<span><b>Current version (1.1.0)</b> released <b>7th September 2024</b></span>
</main>
</body>

Expand Down

0 comments on commit b8033df

Please sign in to comment.