Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],
// Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": "yarn install; npx playwright install-deps; npx playwright install",
"postCreateCommand": "yarn i; npx playwright install-deps; npx playwright install; sudo npx playwright install-deps",
// Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
"remoteUser": "node",
"mounts": [
Expand Down
14 changes: 11 additions & 3 deletions .github/workflows/github-actions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,19 @@ env:
# None of these are real secrets although this should probably be updated
DATABASE_URL: mysql://root:mysql@localhost:33306/api_dev
VITE_DATABASE_URL: mysql://root:mysql@localhost:33306/api_dev

ELASTICSEARCH_URL: http://localhost:9200
VITE_ELASTICSEARCH_URL: http://localhost:9200
VITE_ELASTICSEARCH_MESSAGE_INDEX: messages
VITE_ELASTICSEARCH_INDEX: api_dev

ELASTICSEARCH_USERNAME: elastic
VITE_ELASTICSEARCH_USERNAME: elastic

ELASTICSEARCH_PASSWORD: changeme
VITE_ELASTICSEARCH_PASSWORD: changeme

ELASTICSEARCH_MESSAGE_INDEX: messages
VITE_ELASTICSEARCH_MESSAGE_INDEX: messages

OWNERS: "[]"
DISCORD_CLIENT_ID: "123"
VITE_DISCORD_CLIENT_ID: "123"
Expand Down Expand Up @@ -48,7 +56,7 @@ jobs:
run: yarn && npx playwright install-deps

- name: Setup Database
run: yarn migrate
run: yarn db:push

- name: Lint
run: yarn lint
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,5 @@ dist/

# turbo
.turbo

.pnpm-store/
1 change: 0 additions & 1 deletion .npmrc

This file was deleted.

32 changes: 16 additions & 16 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node-terminal",
"request": "launch",
"name": "Run Discord Bot",
"skipFiles": [
"<node_internals>/**"
],
"command": "yarn run dev:discord-bot",
}
]
}
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node-terminal",
"request": "launch",
"name": "Run Discord Bot",
"skipFiles": [
"<node_internals>/**"
],
"command": "yarn run dev:discord-bot",
}
]
}
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@


<p align='center'>
<a href='https://answeroverflow.com/'>
<img src="https://www.answeroverflow.com/content/branding/AnswerOverflowLOGO.png">
Expand Down
13 changes: 8 additions & 5 deletions apps/discord-bot/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"license": "UNLICENSE",
"dependencies": {
"@answeroverflow/api": "*",
"@answeroverflow/db": "*",
"@answeroverflow/reacord": "^0.5.7",
"@sapphire/decorators": "6.0.0",
"@sapphire/discord-utilities": "3.0.0",
Expand All @@ -29,6 +30,7 @@
"tsx": "^3.12.1"
},
"devDependencies": {
"@answeroverflow/eslint-config-custom": "*",
"@answeroverflow/tsconfig": "*",
"@sapphire/eslint-config": "^4.3.8",
"@swc/core": "^1.3.22",
Expand All @@ -37,6 +39,7 @@
"@types/node": "^18.11.10",
"@types/ws": "^8.5.3",
"dotenv-cli": "^6.0.0",
"eslint-plugin-jest": "^27.2.1",
"jest": "^29.3.1",
"npm-run-all": "^4.1.5",
"ts-jest": "^29.0.3",
Expand All @@ -47,16 +50,16 @@
},
"scripts": {
"with-env": "dotenv -e ../../.env --",
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist",
"clean": "rm -rf .turbo node_modules dist",
"watch": "tsup --silent --format cjs --watch . --watch ../../packages/api/src/",
"build": "tsup --format cjs",
"lint": "eslint src && tsc --noEmit",
"lint:fix": "eslint src --fix",
"format": "prettier --write \"src/**/*.ts\"",
"dev": "yarn with-env rm -rf dist && yarn run build && run-p watch start",
"start": "yarn with-env node dist/index.js",
"test:discord-bot": "yarn with-env jest --runInBand",
"test:watch": "yarn with-env jest --watch --runInBand",
"dev": "yarn run with-env rm -rf dist && yarn run build && run-p watch start",
"start": "yarn run with-env node dist/index.js",
"test:discord-bot": "yarn run with-env jest --runInBand",
"test:watch": "yarn run with-env jest --watch --runInBand",
"install-local-reacord": "cp -r ~/repos/reacord/packages/reacord/dist/ /workspace/node_modules/@answeroverflow/reacord/"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export class ChannelSettingsCommand extends Command {

public override async chatInputRun(
interaction: ChatInputCommandInteraction,
// eslint-disable-next-line no-unused-vars
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_context: ChatInputCommand.RunContext
) {
if (interaction.guild == null) {
Expand Down
18 changes: 9 additions & 9 deletions apps/discord-bot/src/components/channel-settings-menu.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ describe("ChannelSettingsMenu", () => {
<ChannelSettingsMenu settings={default_settings} channel={text_channel} />
);
expect(
message.hasComponents(
message!.hasComponents(
["Enable Indexing", "Enable Mark Solution", "Enable Send Mark Solution Instructions"],
[]
)
Expand All @@ -59,7 +59,7 @@ describe("ChannelSettingsMenu", () => {
<ChannelSettingsMenu settings={default_settings} channel={forum_thread} />
);
expect(
message.hasComponents(
message!.hasComponents(
["Enable Indexing", "Enable Mark Solution", "Enable Send Mark Solution Instructions"],
["Select a tag to use on mark as solved"]
)
Expand All @@ -75,12 +75,12 @@ describe("Toggle Indexing Button", () => {
reacord,
<ChannelSettingsMenu settings={default_settings} channel={text_channel} />
);
const enable_indexing_button = message.findButtonByLabel("Enable Indexing", reacord);
const enable_indexing_button = message!.findButtonByLabel("Enable Indexing", reacord);
expect(enable_indexing_button).toBeDefined();
await enable_indexing_button!.click(text_channel, members.guild_member_owner);

expect(message.hasButton("Enable Indexing", reacord)).toBeFalsy();
const button = message.findButtonByLabel("Disable Indexing", reacord);
expect(message!.hasButton("Enable Indexing", reacord)).toBeFalsy();
const button = message!.findButtonByLabel("Disable Indexing", reacord);
expect(button).toBeDefined();
});
});
Expand All @@ -92,21 +92,21 @@ describe("Select mark solved tag", () => {
reacord,
<ChannelSettingsMenu settings={default_settings} channel={forum_thread} />
);
const select = message.findSelectByPlaceholder(
const select = message!.findSelectByPlaceholder(
"Select a tag to use on mark as solved",
reacord
);
expect(select).toBeDefined();
await select?.select(
forum_thread,
members.guild_member_owner,
forum_channel.availableTags[0].id
forum_channel.availableTags[0]!.id
);
const select2 = message.findSelectByPlaceholder(
const select2 = message!.findSelectByPlaceholder(
"Select a tag to use on mark as solved",
reacord
);
expect(select2?.values).toHaveLength(1);
expect(select2!.values![0]).toBe(forum_channel.availableTags[0].id);
expect(select2!.values![0]).toBe(forum_channel.availableTags[0]!.id);
});
});
1 change: 0 additions & 1 deletion apps/discord-bot/src/components/toggle-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ export function ToggleButton({
currently_enabled: boolean;
enable_label: string;
disable_label: string;
// eslint-disable-next-line no-unused-vars
onClick: (event: ButtonClickEvent) => void;
}) {
const label = currently_enabled ? disable_label : enable_label;
Expand Down
7 changes: 2 additions & 5 deletions apps/discord-bot/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
import { createClient, login } from "./utils/bot";
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
require("dotenv-mono").load();

const client = createClient();
void login(client);

declare global {
// eslint-disable-next-line no-unused-vars
namespace NodeJS {
// eslint-disable-next-line no-unused-vars
interface ProcessEnv {
/* Discord Bot */
DISCORD_TOKEN: string;
INDEXING_INTERVAL_IN_HOURS: string;
INDEXING_INTERVAL_IN_HOURS: string | undefined;
MAXIMUM_CHANNEL_MESSAGES_PER_INDEX: string | undefined;
BOT_DEV_LOG_LEVEL: string | undefined;
BOT_TEST_LOG_LEVEL: string | undefined;
Expand Down
4 changes: 3 additions & 1 deletion apps/discord-bot/src/listeners/events/indexing-loop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import { indexServers } from "~discord-bot/utils/indexing";
@ApplyOptions<Listener.Options>({ once: true, event: Events.ClientReady })
export class Indexing extends Listener {
public async run(client: Client) {
const interval_in_hours = parseFloat(process.env.INDEXING_INTERVAL_IN_HOURS) ?? 24;
const interval_in_hours = process.env.INDEXING_INTERVAL_IN_HOURS
? parseFloat(process.env.INDEXING_INTERVAL_IN_HOURS)
: 24;
container.logger.info(`Indexing all servers every ${interval_in_hours} hours`);
const interval_in_ms = interval_in_hours * 60 * 60 * 1000;
await indexServers(client); // Do an initial index before the loop kicks in
Expand Down
1 change: 0 additions & 1 deletion apps/discord-bot/src/preconditions/OwnerOnly.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ export class UserPrecondition extends AllFlowsPrecondition {
}

declare module "@sapphire/framework" {
// eslint-disable-next-line no-unused-vars
interface Preconditions {
OwnerOnly: never;
}
Expand Down
3 changes: 1 addition & 2 deletions apps/discord-bot/src/utils/bot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { ClientOptions, Partials } from "discord.js";
import "~discord-bot/utils/setup";

declare module "@sapphire/pieces" {
// eslint-disable-next-line no-unused-vars
interface Container {
reacord: ReacordDiscordJs | ReacordTester;
}
Expand All @@ -16,7 +15,7 @@ function getLogLevel() {
case "development":
return process.env.BOT_DEV_LOG_LEVEL
? parseInt(process.env.BOT_DEV_LOG_LEVEL)
: LogLevel.None;
: LogLevel.Debug;
case "test":
return process.env.BOT_TEST_LOG_LEVEL
? parseInt(process.env.BOT_TEST_LOG_LEVEL)
Expand Down
9 changes: 8 additions & 1 deletion apps/discord-bot/src/utils/conversions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,14 @@ export function toAOMessage(message: Message): AOMessage {
id: message.id,
content: message.content,
channel_id: message.channel.id,
images: message.attachments.map((attachment) => attachment.url),
images: message.attachments.map((attachment) => {
return {
url: attachment.url,
width: attachment.width,
height: attachment.height,
description: attachment.description,
};
}),
replies_to: message.reference?.messageId ?? null,
author_id: message.author.id,
server_id: message.guild?.id,
Expand Down
8 changes: 4 additions & 4 deletions apps/discord-bot/src/utils/indexing.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,15 +132,15 @@ describe("Indexing", () => {
const messages = mockMessages(news_channel, 100);
const thread1 = mockThreadFromParentMessage({
client,
parent_message: messages[0],
parent_message: messages[0]!,
data: {
type: ChannelType.AnnouncementThread,
},
});
messages.push(...mockMessages(thread1, 10));
const thread2 = mockThreadFromParentMessage({
client,
parent_message: messages[1],
parent_message: messages[1]!,
data: {
type: ChannelType.AnnouncementThread,
},
Expand Down Expand Up @@ -581,13 +581,13 @@ describe("Indexing", () => {
const start = 100;
const messages = await fetchAllMesages(text_channel, { limit, start: `${start}` });
expect(messages.length).toBe(limit);
expect(messages[0].id).toBe(`${start + 1}`);
expect(messages[0]!.id).toBe(`${start + 1}`);
});
it("should return the messages sorted from oldest to newest", async () => {
const messages = await fetchAllMesages(text_channel);
expect(messages.length).toBe(number_of_messages);
for (let id = 0; id < number_of_messages; id++) {
expect(messages[id].id).toBe(`${id + 1}`);
expect(messages[id]!.id).toBe(`${id + 1}`);
}
});
});
Expand Down
11 changes: 8 additions & 3 deletions apps/discord-bot/src/utils/indexing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,17 +51,21 @@ export async function indexRootChannel(channel: TextChannel | NewsChannel | Foru
getCtx: createAnswerOveflowBotCtx,
error_message: `Failed to get channel settings for channel ${channel.id}`,
success_message: `Got channel settings for channel ${channel.id}`,
allowed_errors: "NOT_FOUND",
});

if (!settings || !settings.flags.indexing_enabled) {
return;
}

let start = settings.last_indexed_snowflake == null ? undefined : settings.last_indexed_snowflake;
if (process.env.NODE_ENV === "development") {
start = undefined; // always index from the beginning in development for ease of testing
}
// Collect all messages
const { messages: messages_to_parse, threads } = await fetchAllChannelMessagesWithThreads(
channel,
{
start: settings.last_indexed_snowflake == null ? undefined : settings.last_indexed_snowflake,
start,
limit: process.env.MAXIMUM_CHANNEL_MESSAGES_PER_INDEX
? parseInt(process.env.MAXIMUM_CHANNEL_MESSAGES_PER_INDEX)
: undefined,
Expand Down Expand Up @@ -99,7 +103,7 @@ export async function indexRootChannel(channel: TextChannel | NewsChannel | Foru

await router.channel_settings.upsert({
channel_id: channel.id,
last_indexed_snowflake: converted_messages[converted_messages.length - 1].id,
last_indexed_snowflake: converted_messages[converted_messages.length - 1]!.id,
});
},
error_message: `Failed to index channel ${channel.id}`,
Expand Down Expand Up @@ -157,6 +161,7 @@ export async function filterMessages(messages: Message[], channel: GuildBasedCha
);
},
error_message: "Failed to fetch user server settings in indexing",
allowed_errors: "NOT_FOUND",
getCtx: createAnswerOveflowBotCtx,
success_message: `Fetched ${seen_user_ids.length} user server settings`,
});
Expand Down
16 changes: 11 additions & 5 deletions apps/discord-bot/src/utils/trpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,19 @@ import { ephemeralReply } from "./utils";

type TRPCall<T> = {
getCtx: () => Promise<BotContextCreate>;
// eslint-disable-next-line no-unused-vars
ApiCall: (router: BotRouterCaller) => Promise<T>;
// eslint-disable-next-line no-unused-vars
Ok?: (result: T) => void;
// eslint-disable-next-line no-unused-vars
Error?: (error: TRPCError) => void;
allowed_errors?: TRPCError["code"][] | TRPCError["code"];
};

export async function callAPI<T>({ getCtx, ApiCall, Ok = () => {}, Error = () => {} }: TRPCall<T>) {
export async function callAPI<T>({
getCtx,
ApiCall,
Ok = () => {},
Error = () => {},
allowed_errors = "NOT_FOUND",
}: TRPCall<T>) {
try {
const converted_ctx = await createBotContext(await getCtx());
const caller = botRouter.createCaller(converted_ctx);
Expand All @@ -29,7 +33,9 @@ export async function callAPI<T>({ getCtx, ApiCall, Ok = () => {}, Error = () =>
return data;
} catch (error) {
if (error instanceof TRPCError) {
Error(error);
if (!allowed_errors.includes(error.code)) {
Error(error);
}
} else {
throw error;
}
Expand Down
Loading