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