generated from nhcarrigan/template
This commit is contained in:
parent
546ea428fd
commit
46ae51bd33
38
.gitea/workflows/ci.yml
Normal file
38
.gitea/workflows/ci.yml
Normal file
@ -0,0 +1,38 @@
|
||||
name: Node.js CI
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: Lint and Test
|
||||
|
||||
steps:
|
||||
- name: Checkout Source Files
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Use Node.js v22
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
with:
|
||||
version: 10
|
||||
|
||||
- name: Install Dependencies
|
||||
run: pnpm install
|
||||
|
||||
- name: Lint Source Files
|
||||
run: pnpm run lint
|
||||
|
||||
- name: Verify Build
|
||||
run: pnpm run build
|
||||
|
||||
- name: Run Tests
|
||||
run: pnpm run test
|
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
node_modules
|
||||
prod
|
||||
bin/*
|
||||
!bin/.gitkeep
|
10
.vscode/settings.json
vendored
Normal file
10
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit"
|
||||
},
|
||||
"eslint.validate": ["typescript"],
|
||||
"sonarlint.connectedMode.project": {
|
||||
"connectionId": "nhcarrigan",
|
||||
"projectKey": "nhcarrigan_forms-api"
|
||||
}
|
||||
}
|
0
bin/.gitkeep
Normal file
0
bin/.gitkeep
Normal file
14
eslint.config.js
Normal file
14
eslint.config.js
Normal file
@ -0,0 +1,14 @@
|
||||
import NaomisConfig from "@nhcarrigan/eslint-config";
|
||||
|
||||
export default [
|
||||
...NaomisConfig,
|
||||
{
|
||||
rules: {
|
||||
"max-lines-per-function": "off",
|
||||
"max-statements": "off",
|
||||
"unicorn/prefer-top-level-await": "off",
|
||||
"@typescript-eslint/strict-boolean-expressions": "off",
|
||||
"complexity": "off",
|
||||
}
|
||||
}
|
||||
]
|
33
package.json
Normal file
33
package.json
Normal file
@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "discord-rpc",
|
||||
"version": "0.0.0",
|
||||
"description": "",
|
||||
"type": "module",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"lint": "eslint src --max-warnings 0",
|
||||
"prepkg:main": "esbuild src/index.ts --bundle --platform=node --outfile=prod/index.js",
|
||||
"prepkg:installer": "esbuild src/setup.ts --bundle --platform=node --outfile=prod/setup.js",
|
||||
"package": "pnpm run pkg:main && pnpm run pkg:installer",
|
||||
"pkg:main": "pkg prod/index.js --target latest-linux-x64 --output ./bin/naomis-drpc",
|
||||
"pkg:installer": "pkg prod/setup.js --target latest-linux-x64 --output ./bin/naomis-drpc-setup",
|
||||
"test": "echo \"Error: no test specified\" && exit 0"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "See License in LICENSE.md",
|
||||
"devDependencies": {
|
||||
"@nhcarrigan/eslint-config": "5.2.0",
|
||||
"@nhcarrigan/typescript-config": "4.0.0",
|
||||
"@types/discord-rpc": "4.0.8",
|
||||
"@types/node": "22.13.4",
|
||||
"@yao-pkg/pkg": "6.3.1",
|
||||
"esbuild": "0.25.0",
|
||||
"eslint": "9.20.1",
|
||||
"typescript": "5.7.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"discord-rpc": "4.0.1"
|
||||
}
|
||||
}
|
5165
pnpm-lock.yaml
generated
Normal file
5165
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
86
src/index.ts
Normal file
86
src/index.ts
Normal file
@ -0,0 +1,86 @@
|
||||
/**
|
||||
* @copyright nhcarrigan
|
||||
* @license Naomi's Public License
|
||||
* @author Naomi Carrigan
|
||||
*/
|
||||
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { homedir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { Client } from "discord-rpc";
|
||||
import type { Config } from "./types.ts";
|
||||
|
||||
void (async(): Promise<void> => {
|
||||
const configFile = join(homedir(), ".config", "naomis-drpc", "config.json");
|
||||
|
||||
const rawConfig = await readFile(configFile, "utf-8").catch(() => {
|
||||
return null;
|
||||
});
|
||||
if (!rawConfig) {
|
||||
process.stderr.write(
|
||||
`Config file not found. Please run naomis-drpc-setup first, or manually create a config in ${configFile}.\n`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- JSON doesn't take a generic.
|
||||
const config = JSON.parse(rawConfig) as Config;
|
||||
const {
|
||||
appId,
|
||||
details,
|
||||
state,
|
||||
largeImageKey,
|
||||
largeImageText,
|
||||
smallImageKey,
|
||||
smallImageText,
|
||||
buttonOneLabel,
|
||||
buttonOneUrl,
|
||||
buttonTwoLabel,
|
||||
buttonTwoUrl,
|
||||
} = config;
|
||||
|
||||
if (
|
||||
!appId
|
||||
|| !details
|
||||
|| !state
|
||||
|| !largeImageKey
|
||||
|| !largeImageText
|
||||
|| !smallImageKey
|
||||
|| !smallImageText
|
||||
) {
|
||||
process.stderr.write(
|
||||
// eslint-disable-next-line stylistic/max-len -- This is a long string.
|
||||
"Config file is missing required fields. Please run naomis-drpc-setup to reconfigure.\n",
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
process.stdout.write("Config loaded successfully.\n");
|
||||
|
||||
const buttons: Array<{ label: string; url: string }> = [];
|
||||
if (buttonOneLabel && buttonOneUrl) {
|
||||
buttons.push({ label: buttonOneLabel, url: buttonOneUrl });
|
||||
}
|
||||
if (buttonTwoLabel && buttonTwoUrl) {
|
||||
buttons.push({ label: buttonTwoLabel, url: buttonTwoUrl });
|
||||
}
|
||||
|
||||
const client = new Client({ transport: "ipc" });
|
||||
|
||||
const startTimestamp = Date.now();
|
||||
|
||||
client.on("ready", () => {
|
||||
void client.setActivity({
|
||||
buttons,
|
||||
details,
|
||||
largeImageKey,
|
||||
largeImageText,
|
||||
smallImageKey,
|
||||
smallImageText,
|
||||
startTimestamp,
|
||||
state,
|
||||
});
|
||||
});
|
||||
|
||||
await client.login({ clientId: appId });
|
||||
})();
|
121
src/setup.ts
Normal file
121
src/setup.ts
Normal file
@ -0,0 +1,121 @@
|
||||
/**
|
||||
* @copyright nhcarrigan
|
||||
* @license Naomi's Public License
|
||||
* @author Naomi Carrigan
|
||||
*/
|
||||
|
||||
import { mkdir, stat, writeFile } from "node:fs/promises";
|
||||
import { homedir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { createInterface } from "node:readline/promises";
|
||||
import type { Config } from "./types.ts";
|
||||
|
||||
void (async(): Promise<void> => {
|
||||
const reader = createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout,
|
||||
});
|
||||
|
||||
const configDirectory = join(homedir(), ".config");
|
||||
const appconfigDirectory = join(configDirectory, "naomis-drpc");
|
||||
const directoryExists = await stat(appconfigDirectory).catch(() => {
|
||||
return null;
|
||||
});
|
||||
if (!directoryExists?.isDirectory()) {
|
||||
await mkdir(appconfigDirectory, { recursive: true });
|
||||
}
|
||||
const configFile = join(appconfigDirectory, "config.json");
|
||||
|
||||
process.stdout.write(`Config path: ${configFile}\n`);
|
||||
|
||||
const configExists = await stat(configFile).catch(() => {
|
||||
return null;
|
||||
});
|
||||
if (configExists?.isFile()) {
|
||||
const confirmOverwrite = await reader.question(
|
||||
"Config file already exists. Overwrite? (y/N) ",
|
||||
);
|
||||
if (confirmOverwrite.toLowerCase() !== "y") {
|
||||
process.stdout.write("Declined to overwrite config file. Exiting...\n");
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
const config: Partial<Config> = {};
|
||||
|
||||
process.stdout.write("Welcome to the Discord Rich Presence Setup Wizard!\n");
|
||||
process.stdout.write(
|
||||
// eslint-disable-next-line stylistic/max-len -- This is a long string.
|
||||
"Before you get started, you need to create a Discord application on their developer portal. https://discord.dev\n",
|
||||
);
|
||||
process.stdout.write(
|
||||
// eslint-disable-next-line stylistic/max-len -- This is a long string.
|
||||
"The name you assign your new application will show up in your Discord STATUS when you are connected with this tool.",
|
||||
);
|
||||
const appId = await reader.question(
|
||||
// eslint-disable-next-line stylistic/max-len -- This is a long string.
|
||||
"Enter your Discord application ID. You can obtain this from the Discord Developer Portal:",
|
||||
);
|
||||
config.appId = appId;
|
||||
const details = await reader.question(
|
||||
"Enter the first line of text you'd like to show in your presence:",
|
||||
);
|
||||
config.details = details;
|
||||
const state = await reader.question(
|
||||
"Enter the second line of text you'd like to show in your presence:",
|
||||
);
|
||||
config.state = state;
|
||||
process.stdout.write(
|
||||
// eslint-disable-next-line stylistic/max-len -- This is a long string.
|
||||
"The next section sets up the images to display in your presence. You can upload these images to your application under Rich Presence -> Art Assets.\n",
|
||||
);
|
||||
const largeImageKey = await reader.question(
|
||||
// eslint-disable-next-line stylistic/max-len -- This is a long string.
|
||||
"Enter the DISCORD name of the image you uploaded to display as the large image:",
|
||||
);
|
||||
config.largeImageKey = largeImageKey;
|
||||
const largeImageText = await reader.question(
|
||||
"Enter the text to display when hovering over the large image:",
|
||||
);
|
||||
config.largeImageText = largeImageText;
|
||||
const smallImageKey = await reader.question(
|
||||
// eslint-disable-next-line stylistic/max-len -- This is a long string.
|
||||
"Enter the DISCORD name of the image you uploaded to display as the small image:",
|
||||
);
|
||||
config.smallImageKey = smallImageKey;
|
||||
const smallImageText = await reader.question(
|
||||
"Enter the text to display when hovering over the small image:",
|
||||
);
|
||||
config.smallImageText = smallImageText;
|
||||
process.stdout.write(
|
||||
// eslint-disable-next-line stylistic/max-len -- This is a long string.
|
||||
"You can optionally display buttons that other users can click on in your presence.\n",
|
||||
);
|
||||
const wantsButtons = await reader.question(
|
||||
"Do you want to display buttons in your presence? (y/N) ",
|
||||
);
|
||||
if (wantsButtons.toLowerCase() === "y") {
|
||||
const buttonOneLabel = await reader.question(
|
||||
"Enter the text to display on the first button:",
|
||||
);
|
||||
config.buttonOneLabel = buttonOneLabel;
|
||||
const buttonOneUrl = await reader.question(
|
||||
"Enter the URL to open when the first button is clicked:",
|
||||
);
|
||||
config.buttonOneUrl = buttonOneUrl;
|
||||
const buttonTwoLabel = await reader.question(
|
||||
"Enter the text to display on the second button:",
|
||||
);
|
||||
config.buttonTwoLabel = buttonTwoLabel;
|
||||
const buttonTwoUrl = await reader.question(
|
||||
"Enter the URL to open when the second button is clicked:",
|
||||
);
|
||||
config.buttonTwoUrl = buttonTwoUrl;
|
||||
}
|
||||
|
||||
await writeFile(configFile, JSON.stringify(config, null, 4));
|
||||
process.stdout.write(
|
||||
// eslint-disable-next-line stylistic/max-len -- This is a long string.
|
||||
"Config file written successfully! You can now run naomis-drpc to start your Discord Rich Presence.\n",
|
||||
);
|
||||
process.exit(0);
|
||||
})();
|
19
src/types.ts
Normal file
19
src/types.ts
Normal file
@ -0,0 +1,19 @@
|
||||
/**
|
||||
* @copyright nhcarrigan
|
||||
* @license Naomi's Public License
|
||||
* @author Naomi Carrigan
|
||||
*/
|
||||
|
||||
export interface Config {
|
||||
appId: string;
|
||||
details: string;
|
||||
state: string;
|
||||
largeImageKey: string;
|
||||
largeImageText: string;
|
||||
smallImageKey: string;
|
||||
smallImageText: string;
|
||||
buttonOneLabel?: string;
|
||||
buttonOneUrl?: string;
|
||||
buttonTwoLabel?: string;
|
||||
buttonTwoUrl?: string;
|
||||
}
|
7
tsconfig.json
Normal file
7
tsconfig.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "@nhcarrigan/typescript-config",
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./prod",
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user