Skip to content

Commit

Permalink
chore: bunch of stuff (#9)
Browse files Browse the repository at this point in the history
  • Loading branch information
castdrian authored Jun 25, 2024
2 parents 0d389a6 + e08938c commit b18c03f
Show file tree
Hide file tree
Showing 17 changed files with 52 additions and 47 deletions.
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
DISCORD_TOKEN=
DEV_GUILD_ID= # optional, overrides guild and channel specific behavior and sets minimum confidence to 0, otherwise data.toml and database settings will be used
WIT_AI_SERVER_TOKEN__012345678901234567= # replace guild id with guild that corresponds to the wit project
WIT_AI_SERVER_TOKEN__012345678901234567= # replace guild id with guild that corresponds to the wit project or dev guild id
WIT_AI_SERVER_TOKEN__987654321098765432= # no limit on amount of tokens
8 changes: 0 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,6 @@ sudo docker compose up --build

### Development

#### Local

```bash
bun start
```

#### Docker

```bash
sudo docker compose up --build
```
Binary file modified bun.lockb
Binary file not shown.
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
services:
db-init:
image: alpine:latest
container_name: db-init
volumes:
- autosupport-db:/var/lib/sqlite
entrypoint: >
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
},
"devDependencies": {
"@biomejs/biome": "^1.8.1",
"bun-types": "^1.1.14",
"bun-types": "^1.1.15",
"drizzle-kit": "^0.22.7",
"husky": "^9.0.11",
"lint-staged": "^15.2.7",
Expand Down
4 changes: 2 additions & 2 deletions src/commands/chat/info.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { Command, version as sapphver } from '@sapphire/framework';
import { version as bunver } from 'bun';
import { ActionRowBuilder, ButtonBuilder, ButtonStyle, type CommandInteraction, version as djsver, time } from 'discord.js';
import { ActionRowBuilder, ButtonBuilder, ButtonStyle, type ChatInputCommandInteraction, version as djsver, time } from 'discord.js';
import { cpu, mem, osInfo } from 'systeminformation';
import { version as tsver } from 'typescript';

import pkg from '@root/package.json';

export class InfoCommand extends Command {
public override async chatInputRun(interaction: CommandInteraction) {
public override async chatInputRun(interaction: ChatInputCommandInteraction) {
try {
const { readyAt } = this.container.client;
const uptimeString = time(readyAt ?? new Date(), 'R');
Expand Down
22 changes: 11 additions & 11 deletions src/commands/chat/settings.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { addIgnoredRoleId, addSupportChannelId, clearConfinementRoleId, getOrCreateGuildSettings, removeIgnoredRoleId, removeSupportChannelId, setConfinementRoleId, setIgnoreReplies, setMinimumConfidence } from '@root/src/database/db';
import { addIgnoredRoleId, addSupportChannelId, clearConfinementRoleId, getOrCreateGuildSettings, removeIgnoredRoleId, removeSupportChannelId, setConfinementRoleId, setIgnoreReplies, setMinimumConfidence } from '@src/database/db';
import { Subcommand } from '@sapphire/plugin-subcommands';
import { inlineCodeBlock } from '@sapphire/utilities';
import { PermissionFlagsBits } from 'discord.js';
import { type ChatInputCommandInteraction, PermissionFlagsBits } from 'discord.js';
import { ChannelType, channelMention, roleMention } from 'discord.js';

export class SettingsCommand extends Subcommand {
Expand Down Expand Up @@ -41,63 +41,63 @@ export class SettingsCommand extends Subcommand {
});
}

public async chatInputInfo(interaction: Subcommand.ChatInputCommandInteraction) {
public async chatInputInfo(interaction: ChatInputCommandInteraction) {
if (!interaction.guildId) return;
const settings = await getOrCreateGuildSettings(interaction.guildId);
const formattedSettings = `Minimum Confidence: ${inlineCodeBlock(`${(settings.minimumConfidence * 100).toString()}%`)}\nIgnore Replies: ${settings.ignoreReplies ? inlineCodeBlock('Yes') : inlineCodeBlock('No')}\nSupport Channels: ${settings.channelIds.length > 0 ? settings.channelIds.map(channelMention).join(' ') : inlineCodeBlock('None')}\nIgnored Roles: ${settings.ignoredRoles.length > 0 ? settings.ignoredRoles.map(roleMention).join(' ') : inlineCodeBlock('None')}\nConfinement Role: ${settings.confinementRoleId ? roleMention(settings.confinementRoleId) : inlineCodeBlock('None')}`;
await interaction.reply({ content: formattedSettings, ephemeral: true });
}

public async chatInputConfidence(interaction: Subcommand.ChatInputCommandInteraction) {
public async chatInputConfidence(interaction: ChatInputCommandInteraction) {
if (!interaction.guildId) return;
const confidence = interaction.options.getInteger('value', true);
await setMinimumConfidence(interaction.guildId, confidence / 100);
await interaction.reply({ content: `minimum confidence set to ${confidence}%`, ephemeral: true });
}

public async chatInputIgnoreReplies(interaction: Subcommand.ChatInputCommandInteraction) {
public async chatInputIgnoreReplies(interaction: ChatInputCommandInteraction) {
if (!interaction.guildId) return;
const ignoreReplies = interaction.options.getBoolean('value', true);
await setIgnoreReplies(interaction.guildId, ignoreReplies);
await interaction.reply({ content: `ignore replies set to ${ignoreReplies}`, ephemeral: true });
}

public async chatInputAddSupportChannel(interaction: Subcommand.ChatInputCommandInteraction) {
public async chatInputAddSupportChannel(interaction: ChatInputCommandInteraction) {
if (!interaction.guildId) return;
const channel = interaction.options.getChannel('channel', true);
await addSupportChannelId(interaction.guildId, channel.id);
await interaction.reply({ content: `channel ${channelMention(channel.id)} added as support channel`, ephemeral: true });
}

public async chatInputRemoveSupportChannel(interaction: Subcommand.ChatInputCommandInteraction) {
public async chatInputRemoveSupportChannel(interaction: ChatInputCommandInteraction) {
if (!interaction.guildId) return;
const channel = interaction.options.getChannel('channel', true);
await removeSupportChannelId(interaction.guildId, channel.id);
await interaction.reply({ content: `channel ${channelMention(channel.id)} removed as support channel`, ephemeral: true });
}

public async chatInputAddIgnoredRole(interaction: Subcommand.ChatInputCommandInteraction) {
public async chatInputAddIgnoredRole(interaction: ChatInputCommandInteraction) {
if (!interaction.guildId) return;
const role = interaction.options.getRole('role', true);
await addIgnoredRoleId(interaction.guildId, role.id);
await interaction.reply({ content: `${roleMention(role.id)} added as ignored role`, ephemeral: true });
}

public async chatInputRemoveIgnoredRole(interaction: Subcommand.ChatInputCommandInteraction) {
public async chatInputRemoveIgnoredRole(interaction: ChatInputCommandInteraction) {
if (!interaction.guildId) return;
const role = interaction.options.getRole('role', true);
await removeIgnoredRoleId(interaction.guildId, role.id);
await interaction.reply({ content: `${roleMention(role.id)} removed as ignored role`, ephemeral: true });
}

public async chatInputSetConfinementRole(interaction: Subcommand.ChatInputCommandInteraction) {
public async chatInputSetConfinementRole(interaction: ChatInputCommandInteraction) {
if (!interaction.guildId) return;
const role = interaction.options.getRole('role', true);
await setConfinementRoleId(interaction.guildId, role.id);
await interaction.reply({ content: `confinement role set to ${roleMention(role.id)}`, ephemeral: true });
}

public async chatInputClearConfinementRole(interaction: Subcommand.ChatInputCommandInteraction) {
public async chatInputClearConfinementRole(interaction: ChatInputCommandInteraction) {
if (!interaction.guildId) return;
await clearConfinementRoleId(interaction.guildId);
await interaction.reply({ content: 'confinement role cleared', ephemeral: true });
Expand Down
4 changes: 2 additions & 2 deletions src/commands/context/confine.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type Inmate, addInmate, getConfinementRoleId } from "@root/src/database/db";
import { type Inmate, addInmate, getConfinementRoleId } from "@src/database/db";
import { Command } from "@sapphire/framework";
import { config, responseCache } from "@src/config";
import {
Expand Down Expand Up @@ -65,7 +65,7 @@ export class ConfineCommand extends Command {
}
} catch (ex) {
await res.edit({ content: `Failed to confine member.\n${(ex as Error).message}`, components: [] });
console.error(ex);
this.container.logger.error(ex);
}
} catch (ex) {
this.container.logger.error(ex);
Expand Down
4 changes: 1 addition & 3 deletions src/commands/context/respond.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,7 @@ export class ResponseCommand extends Command {
if (!interaction.inGuild() || !interaction.targetMessage.inGuild()) return;
if (!config.devGuildId && !responseCache.has(interaction.guildId)) return;

const intents = await witIntents(config.witAiServerToken[
config.devGuildId ? Object.keys(config.witAiServerToken)[0] : interaction.targetMessage.guildId
]);
const intents = await witIntents(config.witAiServerToken[interaction.targetMessage.guildId]);

const row = new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(
new StringSelectMenuBuilder().setCustomId("select_response").addOptions(
Expand Down
11 changes: 3 additions & 8 deletions src/commands/context/utterance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,8 @@ export class UtteranceCommand extends Command {
return;
if (!interaction.inGuild() || !interaction.targetMessage.inGuild()) return;
if (!config.devGuildId && !responseCache.has(interaction.guildId)) return;

const intents = await witIntents(config.witAiServerToken[
config.devGuildId ? Object.keys(config.witAiServerToken)[0] : interaction.targetMessage.guildId
]);

const intents = await witIntents(config.witAiServerToken[interaction.targetMessage.guildId]);

const row = new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(
new StringSelectMenuBuilder().setCustomId("select_intent").addOptions(
Expand Down Expand Up @@ -57,10 +55,7 @@ export class UtteranceCommand extends Command {
);
if (!intent) return await res.delete();

await trainWitUtterance(interaction.targetMessage.content, intent.name, config.witAiServerToken[
config.devGuildId ? Object.keys(config.witAiServerToken)[0] : interaction.targetMessage.guildId
]);

await trainWitUtterance(interaction.targetMessage.content, intent.name, config.witAiServerToken[interaction.targetMessage.guildId]);
await confirmation.update({ content: 'Training intent classifier with selected message.', components: [] });
}
} catch (ex) {
Expand Down
14 changes: 14 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,20 @@ export const config = createConfigLoader()
.addZodSchema(configSchema)
.load();

function validateConfig(data: unknown) {
const parsed = configSchema.parse(data);
if (parsed.devGuildId && !Object.keys(parsed.witAiServerToken).includes(parsed.devGuildId)) {
throw new z.ZodError([
{
path: ['witAiServerToken'],
message: 'if devGuildId is set, it must be a key in witAiServerToken',
code: z.ZodIssueCode.custom,
},
]);
}
}

validateConfig(config);
const tomlSchema = z.record(z.string().regex(/^(?<id>\d{17,20})$/), z.record(z.string(), z.string()))
tomlSchema.parse(data);

Expand Down
4 changes: 4 additions & 0 deletions src/data.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,7 @@ custom_fonts = "Fonts are experimental and some packs may break the application.
ios_notifications = "For notifications to work on a Jailed device you need to sign the ipa with a certificate that has the notification entitlement enabled.\n\nPlease keep in mind that this channel is not sideloading support.\nPlease keep questions pertaining to this topic elsewhere."
thirty_two_bit_android_os = "32-bit versions of Android OS are not supported. Please use a 64-bit operating system."
broken_addons = "You can find a curated list of broken add-ons in https://discord.com/channels/1196075698301968455/1210652540757090315/1229376326528663596"
altstore_pal = "AltStore PAL is an alternate app marketplace for notarized applications. You cannot use it to sideload.\n\nPlease keep in mind that this channel is not sideloading support.\nPlease keep questions pertaining to this topic elsewhere."
no_passkeys_when_pre_patched = "Passkeys are not supported when using the pre-patched application. Please use a different method to log in to your account or use the LSPosed module / iOS tweak."
ios_app_icons = "When sideloading with a paid certificate the application will be signed using a wildcard App ID.\nThis effectively breaks being able to change app icons.\nYou need to sign the application with a local dev cert instead if you want this functionality.\n\nPlease keep in mind that this channel is not sideloading support.\nPlease keep questions pertaining to this topic elsewhere."
addon_404 = "You are not supposed to visit the url directly since it does not serve anything that can be rendered. Use it to install the add-on."
6 changes: 3 additions & 3 deletions src/listeners/client/messageCreate.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { getOrCreateGuildSettings } from "@root/src/database/db";
import { getOrCreateGuildSettings } from "@src/database/db";
import { Listener } from "@sapphire/framework";
import { getResponse } from "@src/autosupport";
import { config, responseCache } from "@src/config";
import { getResponse } from "@utils/autosupport";
import { config } from "@src/config";
import type { Message } from "discord.js";

export class MessageListener extends Listener {
Expand Down
2 changes: 1 addition & 1 deletion src/listeners/client/ready.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { version } from "@root/package.json";
import { getInmates, removeInmate } from "@root/src/database/db";
import { getInmates, removeInmate } from "@src/database/db";
import { ApplyOptions } from "@sapphire/decorators";
import { Listener, type ListenerOptions } from "@sapphire/framework";
import { ActivityType, User } from "discord.js";
Expand Down
5 changes: 4 additions & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
} from "@sapphire/framework";
import { config } from "@src/config";
import * as schema from '@src/database/schema';
import { GatewayIntentBits } from "discord.js";
import { DefaultWebSocketManagerOptions, GatewayIntentBits } from "discord.js";
import { drizzle } from 'drizzle-orm/bun-sqlite';
import { migrate } from 'drizzle-orm/bun-sqlite/migrator';

Expand All @@ -18,6 +18,9 @@ const client = new SapphireClient({
],
});

// @ts-expect-error just for fun
DefaultWebSocketManagerOptions.identifyProperties.browser = 'Discord iOS';

const sqlite = new Database('autosupport.db');
const db = drizzle(sqlite, { schema });
migrate(db, { migrationsFolder: './src/database/drizzle' });
Expand Down
10 changes: 4 additions & 6 deletions src/autosupport.ts → src/utils/autosupport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { config, responseCache } from "@src/config";
import { type Intent, witMessage } from "@utils/wit";
import { Collection, type Message } from "discord.js";
import { createWorker } from 'tesseract.js';
import { getMinimumConfidence } from "./database/db";
import { getMinimumConfidence } from "@src/database/db";

function getHighestConfidenceIntent(
intents: Intent[],
Expand Down Expand Up @@ -36,9 +36,7 @@ export async function getResponse(message: Message) {

const res = await witMessage(
`${message.content}\n${imageText}`,
config.witAiServerToken[
config.devGuildId ? Object.keys(config.witAiServerToken)[0] : message.guildId
],
config.witAiServerToken[message.guildId],
);

if (!res.intents.length) return;
Expand All @@ -59,11 +57,11 @@ export async function getResponse(message: Message) {
}
}

responseContent = aggregatedResponses.get(selectedIntent.name) ?? '';
responseContent = aggregatedResponses.get(selectedIntent.name)!;
} else {
const guildResponses = responseCache.get(message.guildId);
if (guildResponses) {
responseContent = guildResponses.get(selectedIntent.name) ?? '';
responseContent = guildResponses.get(selectedIntent.name)!;
}
}

Expand Down
File renamed without changes.

0 comments on commit b18c03f

Please sign in to comment.