mirror of
https://github.com/tauri-apps/tauri-discord-bot.git
synced 2026-01-31 00:35:21 +01:00
Update ping command and some config and formatting cleanup (#147)
This commit is contained in:
18
.prettierrc
18
.prettierrc
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"singleQuote": true,
|
"singleQuote": true,
|
||||||
"quoteProps": "as-needed",
|
"quoteProps": "as-needed",
|
||||||
"trailingComma": "all",
|
"trailingComma": "all",
|
||||||
"bracketSpacing": true,
|
"bracketSpacing": true,
|
||||||
"arrowParens": "always",
|
"arrowParens": "always",
|
||||||
"semi": true,
|
"semi": true,
|
||||||
"useTabs": false,
|
"useTabs": false,
|
||||||
"tabWidth": 4
|
"tabWidth": 4
|
||||||
}
|
}
|
||||||
|
|||||||
62
package.json
62
package.json
@@ -1,33 +1,33 @@
|
|||||||
{
|
{
|
||||||
"name": "tauri-discord-bot",
|
"name": "tauri-discord-bot",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "nodemon --ext ts,js,mjs,json --legacy-watch --loader tsm .",
|
"dev": "nodemon --ext ts,js,mjs,json --legacy-watch --loader tsm .",
|
||||||
"start": "tsm ."
|
"start": "tsm ."
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "20.x"
|
"node": ">=20.x"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"discord.js": "14.15.2",
|
"discord.js": "14.15.2",
|
||||||
"dotenv": "16.4.5",
|
"dotenv": "16.4.5",
|
||||||
"express": "4.19.2",
|
"express": "4.19.2",
|
||||||
"jellycommands": "1.0.0-next.43",
|
"jellycommands": "1.0.0-next.43",
|
||||||
"ts-node": "10.9.2",
|
"ts-node": "10.9.2",
|
||||||
"tsm": "2.3.0",
|
"tsm": "2.3.0",
|
||||||
"unfurl.js": "6.3.1",
|
"unfurl.js": "6.3.1",
|
||||||
"url-regex-safe": "3.0.0"
|
"url-regex-safe": "3.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/express": "4.17.21",
|
"@types/express": "4.17.21",
|
||||||
"@types/node": "20.12.12",
|
"@types/node": "20.12.12",
|
||||||
"@types/url-regex-safe": "1.0.2",
|
"@types/url-regex-safe": "1.0.2",
|
||||||
"nodemon": "3.1.1",
|
"nodemon": "3.1.1",
|
||||||
"prettier": "3.2.5",
|
"prettier": "3.2.5",
|
||||||
"typescript": "5.4.5"
|
"typescript": "5.4.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
{
|
{
|
||||||
"extends": [
|
"extends": ["config:base"]
|
||||||
"config:base"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|||||||
60
src/commands/ping.ts
Normal file
60
src/commands/ping.ts
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import { APIRole, Role, roleMention } from 'discord.js';
|
||||||
|
import { command } from 'jellycommands';
|
||||||
|
|
||||||
|
export default command({
|
||||||
|
name: 'ping',
|
||||||
|
description: 'Ping a role',
|
||||||
|
global: true,
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'role',
|
||||||
|
description: 'The role you want to ping',
|
||||||
|
type: 'Role',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
run: async ({ interaction }) => {
|
||||||
|
// Fetch the role and make sure it's pingable.
|
||||||
|
const role = interaction.options.getRole('role', true);
|
||||||
|
|
||||||
|
// Fetch the member, since the interaction member might not be fully resolved.
|
||||||
|
const member = await interaction.guild.members.fetch(
|
||||||
|
interaction.user.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Check if the role is pingable or the member has permission to ping everyone anyways.
|
||||||
|
let pingable = member.permissions.has('MentionEveryone', true)
|
||||||
|
? 'yes'
|
||||||
|
: pingableStatus(role);
|
||||||
|
|
||||||
|
if (pingable === 'no') {
|
||||||
|
return await interaction.reply({
|
||||||
|
content: 'This role is not pingable.',
|
||||||
|
ephemeral: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// The user has to have the role to ping it in some circumstances.
|
||||||
|
if (pingable === 'self') {
|
||||||
|
if (!member.roles.cache.has(role.id)) {
|
||||||
|
return await interaction.reply({
|
||||||
|
content: 'You do not have permission to ping this role.',
|
||||||
|
ephemeral: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ping the role in a reply so that you can see the original sender of the command.
|
||||||
|
await interaction.reply(`${roleMention(role.id)}`);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
function pingableStatus(role: Role | APIRole): 'yes' | 'no' | 'self' {
|
||||||
|
if (role.name === 'working-group' || role.name.startsWith('wg-')) {
|
||||||
|
return 'self';
|
||||||
|
} else if (['mod', 'moderator'].includes(role.name)) {
|
||||||
|
return 'yes';
|
||||||
|
} else {
|
||||||
|
return 'no';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
import { command } from 'jellycommands';
|
|
||||||
import { wrap_in_embed } from '../utils/embed_helpers';
|
|
||||||
import { GuildMemberRoleManager } from 'discord.js';
|
|
||||||
|
|
||||||
export default command({
|
|
||||||
name: 'reping',
|
|
||||||
description: 'Ping a role',
|
|
||||||
|
|
||||||
global: true,
|
|
||||||
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
name: 'role',
|
|
||||||
description: 'The role you want to ping',
|
|
||||||
type: 'Role',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
run: async ({ interaction }) => {
|
|
||||||
// Find the desired role
|
|
||||||
const role = interaction.options.getRole('role', true);
|
|
||||||
// Check if the user has the role
|
|
||||||
let hasRole = (
|
|
||||||
interaction.member.roles as GuildMemberRoleManager
|
|
||||||
).cache.find((val) => val.id === role.id);
|
|
||||||
// Exit if the user doesn't have the role
|
|
||||||
if (!hasRole) return;
|
|
||||||
// Send the ping message
|
|
||||||
interaction.channel.send(`<@&${role.id}>`);
|
|
||||||
// Follow up the interaction
|
|
||||||
await interaction.reply(wrap_in_embed('Role pinged'));
|
|
||||||
// Delete the reply after 3 seconds
|
|
||||||
setTimeout(async () => {
|
|
||||||
await interaction.deleteReply();
|
|
||||||
}, 3000);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -141,7 +141,8 @@ export default command({
|
|||||||
);
|
);
|
||||||
// Successfully solved the thread
|
// Successfully solved the thread
|
||||||
// Get the first message in the thread
|
// Get the first message in the thread
|
||||||
const start_message = await thread.fetchStarterMessage();
|
const start_message =
|
||||||
|
await thread.fetchStarterMessage();
|
||||||
// Get the first 2 messages after the start message
|
// Get the first 2 messages after the start message
|
||||||
const messages = await thread.messages.fetch({
|
const messages = await thread.messages.fetch({
|
||||||
limit: 2,
|
limit: 2,
|
||||||
@@ -167,7 +168,9 @@ export default command({
|
|||||||
msg.components = [row];
|
msg.components = [row];
|
||||||
await bot_message.edit(msg);
|
await bot_message.edit(msg);
|
||||||
// Commands require a reply
|
// Commands require a reply
|
||||||
await interaction.followUp(wrap_in_embed('Thread solved.'));
|
await interaction.followUp(
|
||||||
|
wrap_in_embed('Thread solved.'),
|
||||||
|
);
|
||||||
// Delete the reply after 10 seconds
|
// Delete the reply after 10 seconds
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
await interaction.deleteReply();
|
await interaction.deleteReply();
|
||||||
@@ -177,21 +180,33 @@ export default command({
|
|||||||
if (!(thread.parent instanceof ForumChannel))
|
if (!(thread.parent instanceof ForumChannel))
|
||||||
throw new Error("Can't solve a non-help channel");
|
throw new Error("Can't solve a non-help channel");
|
||||||
// Parent forum channel
|
// Parent forum channel
|
||||||
const solveChannel = thread.guild.channels.cache.get(thread.parentId) as ForumChannel
|
const solveChannel = thread.guild.channels.cache.get(
|
||||||
|
thread.parentId,
|
||||||
|
) as ForumChannel;
|
||||||
// Solve tag
|
// Solve tag
|
||||||
const solveTag = solveChannel.availableTags.find(tag => tag.name === SOLVED_TAG).id
|
const solveTag = solveChannel.availableTags.find(
|
||||||
|
(tag) => tag.name === SOLVED_TAG,
|
||||||
|
).id;
|
||||||
// Unsolve tag
|
// Unsolve tag
|
||||||
const unsolveTag = solveChannel.availableTags.find(tag => tag.name === UNSOLVED_TAG).id
|
const unsolveTag = solveChannel.availableTags.find(
|
||||||
|
(tag) => tag.name === UNSOLVED_TAG,
|
||||||
|
).id;
|
||||||
// If this is a ThreadChannel
|
// If this is a ThreadChannel
|
||||||
let tags = thread.appliedTags.filter(tag => tag !== solveTag && tag !== unsolveTag).splice(0, 4)
|
let tags = thread.appliedTags
|
||||||
|
.filter((tag) => tag !== solveTag && tag !== unsolveTag)
|
||||||
|
.splice(0, 4);
|
||||||
// Add the solved tag
|
// Add the solved tag
|
||||||
tags.unshift(solveTag)
|
tags.unshift(solveTag);
|
||||||
// If neither tag is going to exist in the channel, add unsolved
|
// If neither tag is going to exist in the channel, add unsolved
|
||||||
if (!tags.includes(solveTag) && !tags.includes(unsolveTag)) tags.unshift(unsolveTag)
|
if (!tags.includes(solveTag) && !tags.includes(unsolveTag))
|
||||||
|
tags.unshift(unsolveTag);
|
||||||
// Ensure no duplicates are in the array
|
// Ensure no duplicates are in the array
|
||||||
tags = [...new Set(tags)].sort()
|
tags = [...new Set(tags)].sort();
|
||||||
// Apply tags
|
// Apply tags
|
||||||
if (tags.toString() !== thread.appliedTags.sort().toString()) thread.setAppliedTags(tags)
|
if (
|
||||||
|
tags.toString() !== thread.appliedTags.sort().toString()
|
||||||
|
)
|
||||||
|
thread.setAppliedTags(tags);
|
||||||
// Commands require a reply
|
// Commands require a reply
|
||||||
await interaction.followUp(wrap_in_embed('Thread solved.'));
|
await interaction.followUp(wrap_in_embed('Thread solved.'));
|
||||||
// Delete the reply after 10 seconds
|
// Delete the reply after 10 seconds
|
||||||
|
|||||||
@@ -28,11 +28,10 @@ export default command({
|
|||||||
await interaction.guild.channels.fetchActiveThreads()
|
await interaction.guild.channels.fetchActiveThreads()
|
||||||
).threads
|
).threads
|
||||||
.map((x) => x)
|
.map((x) => x)
|
||||||
.filter(
|
.filter((thread) =>
|
||||||
(thread) =>
|
thread
|
||||||
thread
|
.permissionsFor(interaction.user)
|
||||||
.permissionsFor(interaction.user)
|
.has(['ReadMessageHistory', 'ViewChannel']),
|
||||||
.has(['ReadMessageHistory', 'ViewChannel']),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
switch (subcommand) {
|
switch (subcommand) {
|
||||||
|
|||||||
114
src/config.ts
114
src/config.ts
@@ -1,8 +1,8 @@
|
|||||||
export const DEV_MODE = process.env.NODE_ENV !== 'production';
|
export const DEV_MODE = process.env.NODE_ENV !== 'production';
|
||||||
|
|
||||||
export const GUILD_ID = DEV_MODE
|
export const GUILD_ID = DEV_MODE
|
||||||
? process.env.DEV_GUILD_ID
|
? process.env.DEV_GUILD_ID
|
||||||
: '616186924390023171';
|
: '616186924390023171';
|
||||||
|
|
||||||
export const DISCORD_TOKEN = process.env.DISCORD_TOKEN;
|
export const DISCORD_TOKEN = process.env.DISCORD_TOKEN;
|
||||||
|
|
||||||
@@ -10,29 +10,29 @@ export const TAURI_BLUE = 0x67d6ed;
|
|||||||
|
|
||||||
// people
|
// people
|
||||||
const ADMIN_ROLES = DEV_MODE
|
const ADMIN_ROLES = DEV_MODE
|
||||||
? [process.env.DEV_ADMIN_ROLE]
|
? [process.env.DEV_ADMIN_ROLE]
|
||||||
: [
|
: [
|
||||||
// admin
|
// admin
|
||||||
'985400380663935088',
|
'985400380663935088',
|
||||||
// core
|
// core
|
||||||
'616187491715907585',
|
'616187491715907585',
|
||||||
// working-group
|
// working-group
|
||||||
'761977421305610241',
|
'761977421305610241',
|
||||||
];
|
];
|
||||||
|
|
||||||
// list of support roles without admin rights
|
// list of support roles without admin rights
|
||||||
export const HELPER_ROLES = DEV_MODE
|
export const HELPER_ROLES = DEV_MODE
|
||||||
? [process.env.DEV_HELPER_ROLE]
|
? [process.env.DEV_HELPER_ROLE]
|
||||||
: [
|
: [
|
||||||
// Helping Hand
|
// Helping Hand
|
||||||
'995034988699455609',
|
'995034988699455609',
|
||||||
];
|
];
|
||||||
|
|
||||||
export const BOT_DEVS = [
|
export const BOT_DEVS = [
|
||||||
// LorenzoLewis
|
// LorenzoLewis
|
||||||
'402698003569180674',
|
'402698003569180674',
|
||||||
// Simon
|
// Simon
|
||||||
// '329752097530839041',
|
// '329752097530839041',
|
||||||
];
|
];
|
||||||
|
|
||||||
// list of roles/user IDs other than the creator allowed to modify threads
|
// list of roles/user IDs other than the creator allowed to modify threads
|
||||||
@@ -40,55 +40,55 @@ export const THREAD_ADMIN_IDS = [...ADMIN_ROLES, ...BOT_DEVS];
|
|||||||
|
|
||||||
// auto thread channels with the issue handling feature
|
// auto thread channels with the issue handling feature
|
||||||
export const HELP_THREAD_CHANNELS = DEV_MODE
|
export const HELP_THREAD_CHANNELS = DEV_MODE
|
||||||
? [process.env.DEV_HELP_CHANNEL]
|
? [process.env.DEV_HELP_CHANNEL]
|
||||||
: [
|
: [
|
||||||
// #help-triage
|
// #help-triage
|
||||||
'625037620996734986',
|
'625037620996734986',
|
||||||
];
|
];
|
||||||
|
|
||||||
// channels that will be automatically threaded when a message is created
|
// channels that will be automatically threaded when a message is created
|
||||||
export const AUTO_THREAD_CHANNELS = DEV_MODE
|
export const AUTO_THREAD_CHANNELS = DEV_MODE
|
||||||
? [process.env.DEV_DID_A_THING_CHANNEL, ...HELP_THREAD_CHANNELS]
|
? [process.env.DEV_DID_A_THING_CHANNEL, ...HELP_THREAD_CHANNELS]
|
||||||
: [
|
: [
|
||||||
// #did-a-thing
|
// #did-a-thing
|
||||||
'616234029842300930',
|
'616234029842300930',
|
||||||
...HELP_THREAD_CHANNELS,
|
...HELP_THREAD_CHANNELS,
|
||||||
];
|
];
|
||||||
|
|
||||||
export const MESSAGE_READ = '✅';
|
export const MESSAGE_READ = '✅';
|
||||||
|
|
||||||
export const REACTION_ROLE: {
|
export const REACTION_ROLE: {
|
||||||
emojiName: string;
|
emojiName: string;
|
||||||
emojiId: string;
|
emojiId: string;
|
||||||
roleId: string;
|
roleId: string;
|
||||||
description: string;
|
description: string;
|
||||||
}[] = DEV_MODE
|
}[] = DEV_MODE
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
emojiName: 'sausageroll',
|
emojiName: 'sausageroll',
|
||||||
emojiId: '995712110925451324',
|
emojiId: '995712110925451324',
|
||||||
roleId: process.env.DEV_REACTION_ROLE,
|
roleId: process.env.DEV_REACTION_ROLE,
|
||||||
description:
|
description:
|
||||||
'Join the conversation in the contributors channels (you can still view without this role)',
|
'Join the conversation in the contributors channels (you can still view without this role)',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
: [
|
: [
|
||||||
{
|
{
|
||||||
emojiName: 'tauri',
|
emojiName: 'tauri',
|
||||||
emojiId: '876938722266972210',
|
emojiId: '876938722266972210',
|
||||||
roleId: '986176820187631616',
|
roleId: '986176820187631616',
|
||||||
description:
|
description:
|
||||||
'Join the conversation in the contributors channels (you can still view without this role)',
|
'Join the conversation in the contributors channels (you can still view without this role)',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const REACTION_ROLE_CHANNEL = DEV_MODE
|
export const REACTION_ROLE_CHANNEL = DEV_MODE
|
||||||
? process.env.DEV_REACTION_ROLE_CHANNEL
|
? process.env.DEV_REACTION_ROLE_CHANNEL
|
||||||
: '616210923354456064';
|
: '616210923354456064';
|
||||||
|
|
||||||
export const SUPPORT_FORUM = DEV_MODE
|
export const SUPPORT_FORUM = DEV_MODE
|
||||||
? process.env.DEV_SUPPORT_FORUM_CHANNEL
|
? process.env.DEV_SUPPORT_FORUM_CHANNEL
|
||||||
: '1047150269156294677';
|
: '1047150269156294677';
|
||||||
export const SOLVABLE_FORUMS = [SUPPORT_FORUM];
|
export const SOLVABLE_FORUMS = [SUPPORT_FORUM];
|
||||||
export const UNSOLVED_TAG = 'unsolved';
|
export const UNSOLVED_TAG = 'unsolved';
|
||||||
export const SOLVED_TAG = 'solved';
|
export const SOLVED_TAG = 'solved';
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import { add_thread_prefix } from '../utils/threads';
|
|||||||
|
|
||||||
export default event({
|
export default event({
|
||||||
name: 'messageCreate',
|
name: 'messageCreate',
|
||||||
run: async ({ }, message) => {
|
run: async ({}, message) => {
|
||||||
// Rules for whether or not the message should be dealt with by the bot
|
// Rules for whether or not the message should be dealt with by the bot
|
||||||
const should_ignore =
|
const should_ignore =
|
||||||
message.author.bot ||
|
message.author.bot ||
|
||||||
|
|||||||
@@ -1,47 +1,76 @@
|
|||||||
import {
|
import { ThreadChannel, ChannelType, ForumChannel } from 'discord.js';
|
||||||
ThreadChannel,
|
|
||||||
ChannelType,
|
|
||||||
ForumChannel,
|
|
||||||
} from 'discord.js';
|
|
||||||
import { event } from 'jellycommands';
|
import { event } from 'jellycommands';
|
||||||
import { wrap_in_embed } from '../utils/embed_helpers';
|
import { wrap_in_embed } from '../utils/embed_helpers';
|
||||||
import { SOLVABLE_FORUMS, UNSOLVED_TAG, SOLVED_TAG, MESSAGE_READ, SUPPORT_FORUM } from '../config';
|
import {
|
||||||
|
SOLVABLE_FORUMS,
|
||||||
|
UNSOLVED_TAG,
|
||||||
|
SOLVED_TAG,
|
||||||
|
MESSAGE_READ,
|
||||||
|
SUPPORT_FORUM,
|
||||||
|
} from '../config';
|
||||||
|
|
||||||
export default event({
|
export default event({
|
||||||
name: 'messageCreate',
|
name: 'messageCreate',
|
||||||
run: async ({ }, message) => {
|
run: async ({}, message) => {
|
||||||
// Rules for whether or not the message should be dealt with by the bot
|
// Rules for whether or not the message should be dealt with by the bot
|
||||||
const should_ignore = message.author.bot || !(message.channel.type === ChannelType.PublicThread)
|
const should_ignore =
|
||||||
|
message.author.bot ||
|
||||||
|
!(message.channel.type === ChannelType.PublicThread);
|
||||||
// If the message should be ignored, return without further processing
|
// If the message should be ignored, return without further processing
|
||||||
if (should_ignore) return;
|
if (should_ignore) return;
|
||||||
// If this is posted in a solvable forum channel
|
// If this is posted in a solvable forum channel
|
||||||
if (message.channel instanceof ThreadChannel && SOLVABLE_FORUMS.includes(message.channel.parentId)) {
|
if (
|
||||||
|
message.channel instanceof ThreadChannel &&
|
||||||
|
SOLVABLE_FORUMS.includes(message.channel.parentId)
|
||||||
|
) {
|
||||||
// Parent forum channel
|
// Parent forum channel
|
||||||
const solveChannel = message.guild.channels.cache.get(message.channel.parentId) as ForumChannel
|
const solveChannel = message.guild.channels.cache.get(
|
||||||
|
message.channel.parentId,
|
||||||
|
) as ForumChannel;
|
||||||
// Solve tag
|
// Solve tag
|
||||||
const solveTag = solveChannel.availableTags.find(tag => tag.name === SOLVED_TAG).id
|
const solveTag = solveChannel.availableTags.find(
|
||||||
|
(tag) => tag.name === SOLVED_TAG,
|
||||||
|
).id;
|
||||||
// Unsolve tag
|
// Unsolve tag
|
||||||
const unsolveTag = solveChannel.availableTags.find(tag => tag.name === UNSOLVED_TAG).id
|
const unsolveTag = solveChannel.availableTags.find(
|
||||||
|
(tag) => tag.name === UNSOLVED_TAG,
|
||||||
|
).id;
|
||||||
// The channel will have one of the tags, no further action required
|
// The channel will have one of the tags, no further action required
|
||||||
if (message.channel.appliedTags.filter(tag => tag === unsolveTag || tag === solveTag).length === 1) return
|
if (
|
||||||
|
message.channel.appliedTags.filter(
|
||||||
|
(tag) => tag === unsolveTag || tag === solveTag,
|
||||||
|
).length === 1
|
||||||
|
)
|
||||||
|
return;
|
||||||
// Tags to apply, without solve or unsolved, maximum 4 entries
|
// Tags to apply, without solve or unsolved, maximum 4 entries
|
||||||
let tags = message.channel.appliedTags.filter(tag => tag !== solveTag && tag !== unsolveTag).splice(0, 4)
|
let tags = message.channel.appliedTags
|
||||||
|
.filter((tag) => tag !== solveTag && tag !== unsolveTag)
|
||||||
|
.splice(0, 4);
|
||||||
// Marked as both solved and unsolved
|
// Marked as both solved and unsolved
|
||||||
if (message.channel.appliedTags.includes(solveTag) && message.channel.appliedTags.includes(unsolveTag)) {
|
if (
|
||||||
|
message.channel.appliedTags.includes(solveTag) &&
|
||||||
|
message.channel.appliedTags.includes(unsolveTag)
|
||||||
|
) {
|
||||||
// Add the solved tag
|
// Add the solved tag
|
||||||
tags.unshift(solveTag)
|
tags.unshift(solveTag);
|
||||||
}
|
}
|
||||||
// If neither tag is going to exist in the channel, add unsolved
|
// If neither tag is going to exist in the channel, add unsolved
|
||||||
if (!tags.includes(solveTag) && !tags.includes(unsolveTag)) tags.unshift(unsolveTag)
|
if (!tags.includes(solveTag) && !tags.includes(unsolveTag))
|
||||||
|
tags.unshift(unsolveTag);
|
||||||
// Ensure no duplicates are in the array
|
// Ensure no duplicates are in the array
|
||||||
tags = [...new Set(tags)].sort()
|
tags = [...new Set(tags)].sort();
|
||||||
// Apply tags
|
// Apply tags
|
||||||
if (tags.toString() !== message.channel.appliedTags.sort().toString()) message.channel.setAppliedTags(tags)
|
if (
|
||||||
|
tags.toString() !==
|
||||||
|
message.channel.appliedTags.sort().toString()
|
||||||
|
)
|
||||||
|
message.channel.setAppliedTags(tags);
|
||||||
// If this is a new post and not just a regular message
|
// If this is a new post and not just a regular message
|
||||||
// Disabled for now due to the fact that nobody reads the message
|
// Disabled for now due to the fact that nobody reads the message
|
||||||
if (!message.nonce && message.position === 0 && false) {
|
if (!message.nonce && message.position === 0 && false) {
|
||||||
const msg = await message.channel.send(wrap_in_embed(
|
const msg = await message.channel.send(
|
||||||
`Thank you for your message!
|
wrap_in_embed(
|
||||||
|
`Thank you for your message!
|
||||||
|
|
||||||
1. Search the <#${SUPPORT_FORUM}> forum for existing posts
|
1. Search the <#${SUPPORT_FORUM}> forum for existing posts
|
||||||
2. Search Github issues to see if this is a known issue
|
2. Search Github issues to see if this is a known issue
|
||||||
@@ -49,9 +78,10 @@ export default event({
|
|||||||
4. Provide reproduction steps for your issue
|
4. Provide reproduction steps for your issue
|
||||||
5. Be polite and remember to follow the [Tauri Code of Conduct](https://github.com/tauri-apps/governance-and-guidance/blob/main/CODE_OF_CONDUCT.md)
|
5. Be polite and remember to follow the [Tauri Code of Conduct](https://github.com/tauri-apps/governance-and-guidance/blob/main/CODE_OF_CONDUCT.md)
|
||||||
|
|
||||||
Once you've read this and taken the appropriate steps, react to this message`
|
Once you've read this and taken the appropriate steps, react to this message`,
|
||||||
))
|
),
|
||||||
await msg.react(MESSAGE_READ)
|
);
|
||||||
|
await msg.react(MESSAGE_READ);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -10,11 +10,11 @@ export default event({
|
|||||||
// Send the message for role reactions
|
// Send the message for role reactions
|
||||||
sendReactionRoleMessage(client);
|
sendReactionRoleMessage(client);
|
||||||
// Update the cache so that the bot can properly react to messages sent before it went live
|
// Update the cache so that the bot can properly react to messages sent before it went live
|
||||||
updateCache(client)
|
updateCache(client);
|
||||||
// Run a task in the background every 60 minutes
|
// Run a task in the background every 60 minutes
|
||||||
setInterval(async () => {
|
setInterval(async () => {
|
||||||
// Update the cache so that old messages can be reacted to
|
// Update the cache so that old messages can be reacted to
|
||||||
updateCache(client)
|
updateCache(client);
|
||||||
}, 60_000 * 60) // Every 60 minutes
|
}, 60_000 * 60); // Every 60 minutes
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,54 +1,99 @@
|
|||||||
import {
|
import { ThreadChannel, ChannelType, ForumChannel } from 'discord.js';
|
||||||
ThreadChannel,
|
|
||||||
ChannelType,
|
|
||||||
ForumChannel,
|
|
||||||
} from 'discord.js';
|
|
||||||
import { event } from 'jellycommands';
|
import { event } from 'jellycommands';
|
||||||
import { SOLVABLE_FORUMS, UNSOLVED_TAG, SOLVED_TAG } from '../config';
|
import { SOLVABLE_FORUMS, UNSOLVED_TAG, SOLVED_TAG } from '../config';
|
||||||
|
|
||||||
export default event({
|
export default event({
|
||||||
name: 'threadUpdate',
|
name: 'threadUpdate',
|
||||||
run: async ({ }, oldChannel, newChannel) => {
|
run: async ({}, oldChannel, newChannel) => {
|
||||||
if (newChannel instanceof ThreadChannel) {
|
if (newChannel instanceof ThreadChannel) {
|
||||||
// If newChannel is a solvable channel
|
// If newChannel is a solvable channel
|
||||||
if (SOLVABLE_FORUMS.includes(newChannel.parentId)) {
|
if (SOLVABLE_FORUMS.includes(newChannel.parentId)) {
|
||||||
// Parent forum channel
|
// Parent forum channel
|
||||||
const solveChannel = newChannel.guild.channels.cache.get(newChannel.parentId) as ForumChannel
|
const solveChannel = newChannel.guild.channels.cache.get(
|
||||||
|
newChannel.parentId,
|
||||||
|
) as ForumChannel;
|
||||||
// Solve tag
|
// Solve tag
|
||||||
const solveTag = solveChannel.availableTags.find(tag => tag.name === SOLVED_TAG).id
|
const solveTag = solveChannel.availableTags.find(
|
||||||
|
(tag) => tag.name === SOLVED_TAG,
|
||||||
|
).id;
|
||||||
// Unsolve tag
|
// Unsolve tag
|
||||||
const unsolveTag = solveChannel.availableTags.find(tag => tag.name === UNSOLVED_TAG).id
|
const unsolveTag = solveChannel.availableTags.find(
|
||||||
|
(tag) => tag.name === UNSOLVED_TAG,
|
||||||
|
).id;
|
||||||
// The new channel will only have one of the tags, no further action required
|
// The new channel will only have one of the tags, no further action required
|
||||||
if (newChannel.appliedTags.filter(tag => tag === unsolveTag || tag === solveTag).length === 1) return
|
if (
|
||||||
|
newChannel.appliedTags.filter(
|
||||||
|
(tag) => tag === unsolveTag || tag === solveTag,
|
||||||
|
).length === 1
|
||||||
|
)
|
||||||
|
return;
|
||||||
// Detect if this was a Bot interaction by checking the combination of solved/unsolved in the old and new channels
|
// Detect if this was a Bot interaction by checking the combination of solved/unsolved in the old and new channels
|
||||||
if (oldChannel.appliedTags.includes(unsolveTag) && oldChannel.appliedTags.includes(solveTag) && (!newChannel.appliedTags.includes(solveTag) || !newChannel.appliedTags.includes(unsolveTag))) return;
|
if (
|
||||||
|
oldChannel.appliedTags.includes(unsolveTag) &&
|
||||||
|
oldChannel.appliedTags.includes(solveTag) &&
|
||||||
|
(!newChannel.appliedTags.includes(solveTag) ||
|
||||||
|
!newChannel.appliedTags.includes(unsolveTag))
|
||||||
|
)
|
||||||
|
return;
|
||||||
// Tags to apply, without solve or unsolved, maximum 4 entries
|
// Tags to apply, without solve or unsolved, maximum 4 entries
|
||||||
let tags = newChannel.appliedTags.filter(tag => tag !== solveTag && tag !== unsolveTag).splice(0, 4)
|
let tags = newChannel.appliedTags
|
||||||
|
.filter((tag) => tag !== solveTag && tag !== unsolveTag)
|
||||||
|
.splice(0, 4);
|
||||||
// solved !unsolved > !solved !unsolved : user removed solved, set unsolved
|
// solved !unsolved > !solved !unsolved : user removed solved, set unsolved
|
||||||
if ((oldChannel.appliedTags.includes(solveTag) && !oldChannel.appliedTags.includes(unsolveTag)) && (!newChannel.appliedTags.includes(solveTag) && !newChannel.appliedTags.includes(unsolveTag))) {
|
if (
|
||||||
tags.unshift(unsolveTag)
|
oldChannel.appliedTags.includes(solveTag) &&
|
||||||
|
!oldChannel.appliedTags.includes(unsolveTag) &&
|
||||||
|
!newChannel.appliedTags.includes(solveTag) &&
|
||||||
|
!newChannel.appliedTags.includes(unsolveTag)
|
||||||
|
) {
|
||||||
|
tags.unshift(unsolveTag);
|
||||||
// !solved unsolved > !solved !unsolved : user removed unsolved, set solved
|
// !solved unsolved > !solved !unsolved : user removed unsolved, set solved
|
||||||
} else if ((oldChannel.appliedTags.includes(unsolveTag) && !oldChannel.appliedTags.includes(solveTag)) && (!newChannel.appliedTags.includes(solveTag) && !newChannel.appliedTags.includes(unsolveTag))) {
|
} else if (
|
||||||
tags.unshift(solveTag)
|
oldChannel.appliedTags.includes(unsolveTag) &&
|
||||||
|
!oldChannel.appliedTags.includes(solveTag) &&
|
||||||
|
!newChannel.appliedTags.includes(solveTag) &&
|
||||||
|
!newChannel.appliedTags.includes(unsolveTag)
|
||||||
|
) {
|
||||||
|
tags.unshift(solveTag);
|
||||||
// solved !unsolved > solved unsolved : user marked already solved as unsolved, set unsolved
|
// solved !unsolved > solved unsolved : user marked already solved as unsolved, set unsolved
|
||||||
} else if ((oldChannel.appliedTags.includes(solveTag) && !oldChannel.appliedTags.includes(unsolveTag)) && (newChannel.appliedTags.includes(solveTag) && newChannel.appliedTags.includes(unsolveTag))) {
|
} else if (
|
||||||
tags.unshift(unsolveTag)
|
oldChannel.appliedTags.includes(solveTag) &&
|
||||||
|
!oldChannel.appliedTags.includes(unsolveTag) &&
|
||||||
|
newChannel.appliedTags.includes(solveTag) &&
|
||||||
|
newChannel.appliedTags.includes(unsolveTag)
|
||||||
|
) {
|
||||||
|
tags.unshift(unsolveTag);
|
||||||
// !solved unsolved > solved unsolved : user marked already unsolved as solved, set solved
|
// !solved unsolved > solved unsolved : user marked already unsolved as solved, set solved
|
||||||
} else if ((oldChannel.appliedTags.includes(unsolveTag) && !oldChannel.appliedTags.includes(solveTag)) && (newChannel.appliedTags.includes(solveTag) && newChannel.appliedTags.includes(unsolveTag))) {
|
} else if (
|
||||||
tags.unshift(solveTag)
|
oldChannel.appliedTags.includes(unsolveTag) &&
|
||||||
|
!oldChannel.appliedTags.includes(solveTag) &&
|
||||||
|
newChannel.appliedTags.includes(solveTag) &&
|
||||||
|
newChannel.appliedTags.includes(unsolveTag)
|
||||||
|
) {
|
||||||
|
tags.unshift(solveTag);
|
||||||
// > unsolved solved : post was going to be marked as both, assume solved is intended
|
// > unsolved solved : post was going to be marked as both, assume solved is intended
|
||||||
} else if (newChannel.appliedTags.includes(unsolveTag) && newChannel.appliedTags.includes(solveTag)) {
|
} else if (
|
||||||
tags.unshift(solveTag)
|
newChannel.appliedTags.includes(unsolveTag) &&
|
||||||
|
newChannel.appliedTags.includes(solveTag)
|
||||||
|
) {
|
||||||
|
tags.unshift(solveTag);
|
||||||
// > !unsolved !unsolved : post was going to be marked as neither, assume unsolved is intended
|
// > !unsolved !unsolved : post was going to be marked as neither, assume unsolved is intended
|
||||||
} else if (!newChannel.appliedTags.includes(unsolveTag) && !newChannel.appliedTags.includes(solveTag)) {
|
} else if (
|
||||||
tags.unshift(unsolveTag)
|
!newChannel.appliedTags.includes(unsolveTag) &&
|
||||||
|
!newChannel.appliedTags.includes(solveTag)
|
||||||
|
) {
|
||||||
|
tags.unshift(unsolveTag);
|
||||||
}
|
}
|
||||||
// If neither tag is going to exist for any reason, add unsolved
|
// If neither tag is going to exist for any reason, add unsolved
|
||||||
if (!tags.includes(solveTag) && !tags.includes(unsolveTag)) tags.unshift(unsolveTag)
|
if (!tags.includes(solveTag) && !tags.includes(unsolveTag))
|
||||||
|
tags.unshift(unsolveTag);
|
||||||
// Ensure no duplicates are in the array
|
// Ensure no duplicates are in the array
|
||||||
tags = [...new Set(tags)].sort()
|
tags = [...new Set(tags)].sort();
|
||||||
// Apply tags if they are different from what's already being applied
|
// Apply tags if they are different from what's already being applied
|
||||||
if (tags.toString() !== newChannel.appliedTags.sort().toString()) newChannel.setAppliedTags(tags)
|
if (
|
||||||
|
tags.toString() !== newChannel.appliedTags.sort().toString()
|
||||||
|
)
|
||||||
|
newChannel.setAppliedTags(tags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -4,24 +4,30 @@ import http from 'http';
|
|||||||
const app = express();
|
const app = express();
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
router.use((req: express.Request, res: express.Response, next: express.NextFunction) => {
|
router.use(
|
||||||
res.header('Access-Control-Allow-Methods', 'GET');
|
(
|
||||||
const status = app.locals.getStatus()
|
req: express.Request,
|
||||||
if (status === 200) {
|
res: express.Response,
|
||||||
next();
|
next: express.NextFunction,
|
||||||
} else {
|
) => {
|
||||||
res.status(status).send({ error: 'Something failed!' })
|
res.header('Access-Control-Allow-Methods', 'GET');
|
||||||
}
|
const status = app.locals.getStatus();
|
||||||
});
|
if (status === 200) {
|
||||||
|
next();
|
||||||
|
} else {
|
||||||
|
res.status(status).send({ error: 'Something failed!' });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
router.get('/health', (req: express.Request, res: express.Response) => {
|
router.get('/health', (req: express.Request, res: express.Response) => {
|
||||||
res.status(200).send('Ok');
|
res.status(200).send('Ok');
|
||||||
});
|
});
|
||||||
|
|
||||||
app.use('/api/v1', router);
|
app.use('/api/v1', router);
|
||||||
|
|
||||||
export default function (callback: any) {
|
export default function (callback: any) {
|
||||||
app.locals.getStatus = callback
|
app.locals.getStatus = callback;
|
||||||
const server = http.createServer(app);
|
const server = http.createServer(app);
|
||||||
return server.listen(3000);
|
return server.listen(3000);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ const client = new JellyCommands({
|
|||||||
GatewayIntentBits.GuildMessages,
|
GatewayIntentBits.GuildMessages,
|
||||||
GatewayIntentBits.GuildMessageReactions,
|
GatewayIntentBits.GuildMessageReactions,
|
||||||
GatewayIntentBits.MessageContent,
|
GatewayIntentBits.MessageContent,
|
||||||
GatewayIntentBits.GuildMessages
|
GatewayIntentBits.GuildMessages,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -33,12 +33,11 @@ const client = new JellyCommands({
|
|||||||
});
|
});
|
||||||
|
|
||||||
function health() {
|
function health() {
|
||||||
if (!client.isReady())
|
if (!client.isReady()) return 502;
|
||||||
return 502
|
return 200;
|
||||||
return 200
|
|
||||||
}
|
}
|
||||||
|
|
||||||
healthcheck(health);
|
healthcheck(health);
|
||||||
|
|
||||||
// Auto reads the DISCORD_TOKEN environment variable
|
// Auto reads the DISCORD_TOKEN environment variable
|
||||||
await client.login()
|
await client.login();
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ export async function sendReactionRoleMessage(client: Client) {
|
|||||||
);
|
);
|
||||||
messageArray.push('\n**<#879007560429088800>**');
|
messageArray.push('\n**<#879007560429088800>**');
|
||||||
messageArray.push(
|
messageArray.push(
|
||||||
"Get involved with Tauri development and browse the different projects.",
|
'Get involved with Tauri development and browse the different projects.',
|
||||||
);
|
);
|
||||||
|
|
||||||
let messageBody = messageArray.join('\n');
|
let messageBody = messageArray.join('\n');
|
||||||
@@ -98,7 +98,9 @@ export async function sendReactionRoleMessage(client: Client) {
|
|||||||
console.debug('Message edited');
|
console.debug('Message edited');
|
||||||
} else {
|
} else {
|
||||||
// Delete old messages from the bot
|
// Delete old messages from the bot
|
||||||
messages.filter((item) => item.author.id == item.client.user.id).forEach((item) => item.delete())
|
messages
|
||||||
|
.filter((item) => item.author.id == item.client.user.id)
|
||||||
|
.forEach((item) => item.delete());
|
||||||
console.debug('Attempting to send message...');
|
console.debug('Attempting to send message...');
|
||||||
// Send the message
|
// Send the message
|
||||||
message = await channel.send(messageBody);
|
message = await channel.send(messageBody);
|
||||||
|
|||||||
@@ -1,16 +1,13 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"module": "Node16",
|
"module": "ESNext",
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"target": "ES2022",
|
"target": "ESNext",
|
||||||
"noImplicitAny": true,
|
"noImplicitAny": true,
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "Node",
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"outDir": "dist",
|
"outDir": "dist",
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true
|
||||||
"lib": [
|
},
|
||||||
"ES2021.String"
|
"include": ["src"]
|
||||||
]
|
|
||||||
},
|
|
||||||
"include": ["src"]
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user