32 Commits

Author SHA1 Message Date
minori e50a13ef69 deps: update fastify to 5.8.1
Node.js CI / CI (pull_request) Failing after 21s
Security Scan and Upload / Security & DefectDojo Upload (pull_request) Successful in 1m14s
2026-03-15 07:01:20 -07:00
minori 8736490d1c Merge pull request 'deps: update @types/node-schedule to 2.1.8' (#10) from dependencies/update--types-node-schedule into main
Node.js CI / CI (push) Failing after 17s
Security Scan and Upload / Security & DefectDojo Upload (push) Successful in 1m22s
2026-03-09 07:01:16 -07:00
minori 52305b575a Merge pull request 'deps: update @slack/oauth to 3.0.4' (#6) from dependencies/update--slack-oauth into main
Node.js CI / CI (push) Successful in 31s
Security Scan and Upload / Security & DefectDojo Upload (push) Successful in 2m22s
2026-03-07 07:01:12 -08:00
minori 099531b520 Merge pull request 'deps: update @atproto/api to 0.19.0' (#14) from dependencies/update--atproto-api into main
Node.js CI / CI (push) Successful in 30s
Security Scan and Upload / Security & DefectDojo Upload (push) Successful in 1m10s
2026-03-06 07:01:23 -08:00
minori 23e3e0df15 deps: update @atproto/api to 0.19.0
Node.js CI / CI (pull_request) Successful in 31s
Security Scan and Upload / Security & DefectDojo Upload (pull_request) Successful in 1m13s
2026-03-05 07:01:54 -08:00
minori 5ca54711b7 Merge pull request 'deps: update fastify to 5.7.1' (#8) from dependencies/update-fastify into main
Node.js CI / CI (push) Successful in 33s
Security Scan and Upload / Security & DefectDojo Upload (push) Successful in 1m12s
2026-03-04 07:01:42 -08:00
hikari 602babb4ca chore: replace .npmrc with pnpm-workspace.yaml
Node.js CI / CI (push) Successful in 30s
Security Scan and Upload / Security & DefectDojo Upload (push) Successful in 1m21s
2026-03-02 16:27:31 -08:00
minori a9d51d40ce Merge pull request 'deps: update @atproto/api to 0.18.21' (#13) from dependencies/update--atproto-api into main
Node.js CI / CI (push) Successful in 28s
Security Scan and Upload / Security & DefectDojo Upload (push) Successful in 1m54s
2026-02-25 07:01:29 -08:00
minori 8478aa3e2f deps: update @atproto/api to 0.18.21
Node.js CI / CI (pull_request) Successful in 30s
Security Scan and Upload / Security & DefectDojo Upload (pull_request) Successful in 1m23s
2026-02-24 07:01:39 -08:00
minori f97bb33c51 Merge pull request 'deps: update @atproto/api to 0.18.17' (#3) from dependencies/update--atproto-api into main
Node.js CI / CI (push) Successful in 30s
Security Scan and Upload / Security & DefectDojo Upload (push) Successful in 1m24s
2026-02-23 20:44:49 -08:00
minori a4f6f0c216 deps: update fastify to 5.7.4
Node.js CI / CI (pull_request) Successful in 31s
Security Scan and Upload / Security & DefectDojo Upload (pull_request) Successful in 1m24s
2026-02-13 07:04:05 -08:00
minori 201408b97c deps: update @atproto/api to 0.18.20
Node.js CI / CI (pull_request) Successful in 25s
Security Scan and Upload / Security & DefectDojo Upload (pull_request) Successful in 1m25s
2026-02-10 07:02:43 -08:00
minori 91b2e4c33e deps: update @atproto/api to 0.18.18
Node.js CI / CI (pull_request) Successful in 46s
Security Scan and Upload / Security & DefectDojo Upload (pull_request) Successful in 2m20s
2026-02-08 07:07:48 -08:00
minori c2d9a8ed8f deps: update fastify to 5.7.2
Node.js CI / CI (pull_request) Successful in 1m2s
Security Scan and Upload / Security & DefectDojo Upload (pull_request) Successful in 2m13s
2026-02-05 07:06:08 -08:00
minori 35a4808ca9 deps: update @types/node-schedule to 2.1.8
Node.js CI / CI (pull_request) Successful in 35s
Security Scan and Upload / Security & DefectDojo Upload (pull_request) Successful in 1m33s
2026-02-04 08:24:23 -08:00
minori b33ba4e6d9 deps: update fastify to 5.7.1
Node.js CI / CI (pull_request) Successful in 29s
Security Scan and Upload / Security & DefectDojo Upload (pull_request) Successful in 1m28s
2026-02-04 08:24:12 -08:00
minori f7274a3e81 deps: update @slack/oauth to 3.0.4
Node.js CI / CI (pull_request) Successful in 31s
Security Scan and Upload / Security & DefectDojo Upload (pull_request) Successful in 1m29s
2026-02-04 08:24:00 -08:00
minori ae045ef881 deps: update @atproto/api to 0.18.17
Node.js CI / CI (pull_request) Successful in 32s
Security Scan and Upload / Security & DefectDojo Upload (pull_request) Successful in 1m32s
2026-02-04 08:23:40 -08:00
hikari c032951865 docs: update feedback section to use support forum
Node.js CI / CI (push) Successful in 23s
Security Scan and Upload / Security & DefectDojo Upload (push) Successful in 1m43s
2026-01-26 12:37:08 -08:00
naomi c128097f64 feat: automated upload of .gitea/workflows/ci.yml
Node.js CI / CI (push) Successful in 22s
Security Scan and Upload / Security & DefectDojo Upload (push) Successful in 56s
2025-12-22 19:42:23 +01:00
naomi c238207db8 feat: automated upload of .gitea/workflows/ci.yml
Node.js CI / CI (push) Has been cancelled
Security Scan and Upload / Security & DefectDojo Upload (push) Has been cancelled
2025-12-22 19:35:52 +01:00
naomi ead3bd18ad feat: automated upload of .gitea/workflows/ci.yml
Node.js CI / Lint and Test (push) Has been cancelled
Security Scan and Upload / Security & DefectDojo Upload (push) Has been cancelled
2025-12-22 19:25:40 +01:00
naomi 5d37ae0aa8 feat: automated upload of .npmrc
Node.js CI / Lint and Test (push) Has been cancelled
Security Scan and Upload / Security & DefectDojo Upload (push) Has been cancelled
2025-12-22 19:16:29 +01:00
naomi d2c3f3d77c feat: automated upload of .gitea/workflows/security.yml
Node.js CI / Lint and Test (push) Successful in 24s
Security Scan and Upload / Security & DefectDojo Upload (push) Successful in 1m7s
2025-12-18 03:08:21 +01:00
naomi adbc67390a feat: automated upload of .gitea/workflows/security.yml
Node.js CI / Lint and Test (push) Successful in 24s
Security Scan and Upload / Security & DefectDojo Upload (push) Successful in 1m0s
2025-12-17 23:26:21 +01:00
naomi b293b5673a feat: automated upload of .gitea/workflows/security.yml
Node.js CI / Lint and Test (push) Successful in 23s
Security Scan / Security Audit (push) Failing after 5m2s
2025-12-12 03:38:06 +01:00
naomi 0739efa7fa feat: automated upload of .gitea/workflows/security.yml
Node.js CI / Lint and Test (push) Successful in 21s
Security Scan / Trivy Security Scan (push) Failing after 4m46s
2025-12-11 20:12:10 +01:00
naomi 68b806ccba feat: avatar
Node.js CI / Lint and Test (push) Successful in 39s
2025-05-22 20:09:59 -07:00
naomi 6158fb73a6 chore: remove fediverse component (#2)
Node.js CI / Lint and Test (push) Successful in 47s
### Explanation

_No response_

### Issue

_No response_

### Attestations

- [x] I have read and agree to the [Code of Conduct](https://docs.nhcarrigan.com/community/coc/)
- [x] I have read and agree to the [Community Guidelines](https://docs.nhcarrigan.com/community/guide/).
- [x] My contribution complies with the [Contributor Covenant](https://docs.nhcarrigan.com/dev/covenant/).

### Dependencies

- [x] I have pinned the dependencies to a specific patch version.

### Style

- [x] I have run the linter and resolved any errors.
- [x] My pull request uses an appropriate title, matching the conventional commit standards.
- [x] My scope of feat/fix/chore/etc. correctly matches the nature of changes in my pull request.

### Tests

- [ ] My contribution adds new code, and I have added tests to cover it.
- [ ] My contribution modifies existing code, and I have updated the tests to reflect these changes.
- [ ] All new and existing tests pass locally with my changes.
- [ ] Code coverage remains at or above the configured threshold.

### Documentation

_No response_

### Versioning

_No response_

Reviewed-on: #2
Co-authored-by: Naomi Carrigan <commits@nhcarrigan.com>
Co-committed-by: Naomi Carrigan <commits@nhcarrigan.com>
2025-03-11 15:19:34 -07:00
naomi ad12241ad0 feat: add installation buttons
Node.js CI / Lint and Test (push) Successful in 47s
2025-03-07 12:04:42 -08:00
naomi deed8e6436 fix: forgot to gitignore prod
Node.js CI / Lint and Test (push) Successful in 41s
2025-03-07 11:55:11 -08:00
naomi 411a647d2d feat: initial prototype (#1)
Node.js CI / Lint and Test (push) Successful in 41s
### Explanation

_No response_

### Issue

_No response_

### Attestations

- [x] I have read and agree to the [Code of Conduct](https://docs.nhcarrigan.com/community/coc/)
- [x] I have read and agree to the [Community Guidelines](https://docs.nhcarrigan.com/community/guide/).
- [x] My contribution complies with the [Contributor Covenant](https://docs.nhcarrigan.com/dev/covenant/).

### Dependencies

- [x] I have pinned the dependencies to a specific patch version.

### Style

- [x] I have run the linter and resolved any errors.
- [x] My pull request uses an appropriate title, matching the conventional commit standards.
- [x] My scope of feat/fix/chore/etc. correctly matches the nature of changes in my pull request.

### Tests

- [ ] My contribution adds new code, and I have added tests to cover it.
- [ ] My contribution modifies existing code, and I have updated the tests to reflect these changes.
- [ ] All new and existing tests pass locally with my changes.
- [ ] Code coverage remains at or above the configured threshold.

### Documentation

_No response_

### Versioning

_No response_

Reviewed-on: #1
Co-authored-by: Naomi Carrigan <commits@nhcarrigan.com>
Co-committed-by: Naomi Carrigan <commits@nhcarrigan.com>
2025-03-07 11:54:07 -08:00
15 changed files with 6081 additions and 1 deletions
+47
View File
@@ -0,0 +1,47 @@
name: Node.js CI
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
ci:
name: CI
runs-on: ubuntu-latest
steps:
- name: Checkout Source Files
uses: actions/checkout@v4
- name: Use Node.js v24
uses: actions/setup-node@v4
with:
node-version: 24
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: 10
- name: Ensure Dependencies are Pinned
uses: naomi-lgbt/dependency-pin-check@main
with:
language: javascript
dev-dependencies: true
peer-dependencies: true
optional-dependencies: true
- 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
+177
View File
@@ -0,0 +1,177 @@
name: Security Scan and Upload
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
schedule:
- cron: '0 0 * * 1'
workflow_dispatch:
jobs:
security-audit:
name: Security & DefectDojo Upload
runs-on: ubuntu-latest
continue-on-error: true
steps:
- name: Checkout code
uses: actions/checkout@v4
# --- AUTO-SETUP PROJECT ---
- name: Ensure DefectDojo Product Exists
env:
DD_URL: ${{ secrets.DD_URL }}
DD_TOKEN: ${{ secrets.DD_TOKEN }}
PRODUCT_NAME: ${{ github.repository }}
PRODUCT_TYPE_ID: 1
run: |
sudo apt-get install jq -y > /dev/null
echo "Checking connection to $DD_URL..."
# Check if product exists - capture HTTP code to debug connection issues
RESPONSE=$(curl --write-out "%{http_code}" --silent --output /tmp/response.json \
-H "Authorization: Token $DD_TOKEN" \
"$DD_URL/api/v2/products/?name=$PRODUCT_NAME")
# If response is not 200, print error
if [ "$RESPONSE" != "200" ]; then
echo "::error::Failed to query DefectDojo. HTTP Code: $RESPONSE"
cat /tmp/response.json
exit 1
fi
COUNT=$(cat /tmp/response.json | jq -r '.count')
if [ "$COUNT" = "0" ]; then
echo "Creating product '$PRODUCT_NAME'..."
curl -s -X POST "$DD_URL/api/v2/products/" \
-H "Authorization: Token $DD_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "name": "'"$PRODUCT_NAME"'", "description": "Auto-created by Gitea Actions", "prod_type": '$PRODUCT_TYPE_ID' }'
else
echo "Product '$PRODUCT_NAME' already exists."
fi
# --- 1. TRIVY (Dependencies & Misconfig) ---
- name: Install Trivy
run: |
sudo apt-get install wget apt-transport-https gnupg lsb-release -y
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | sudo apt-key add -
echo "deb https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main" | sudo tee -a /etc/apt/sources.list.d/trivy.list
sudo apt-get update && sudo apt-get install trivy -y
- name: Run Trivy (FS Scan)
run: |
trivy fs . --scanners vuln,misconfig --format json --output trivy-results.json --exit-code 0
- name: Upload Trivy to DefectDojo
env:
DD_URL: ${{ secrets.DD_URL }}
DD_TOKEN: ${{ secrets.DD_TOKEN }}
run: |
echo "Uploading Trivy results..."
# Generate today's date in YYYY-MM-DD format
TODAY=$(date +%Y-%m-%d)
HTTP_CODE=$(curl --write-out "%{http_code}" --output response.txt --silent -X POST "$DD_URL/api/v2/import-scan/" \
-H "Authorization: Token $DD_TOKEN" \
-F "active=true" \
-F "verified=true" \
-F "scan_type=Trivy Scan" \
-F "engagement_name=CI/CD Pipeline" \
-F "product_name=${{ github.repository }}" \
-F "scan_date=$TODAY" \
-F "auto_create_context=true" \
-F "file=@trivy-results.json")
if [[ "$HTTP_CODE" != "200" && "$HTTP_CODE" != "201" ]]; then
echo "::error::Upload Failed with HTTP $HTTP_CODE"
echo "--- SERVER RESPONSE ---"
cat response.txt
echo "-----------------------"
exit 1
else
echo "Upload Success!"
fi
# --- 2. GITLEAKS (Secrets) ---
- name: Install Gitleaks
run: |
wget -qO gitleaks.tar.gz https://github.com/gitleaks/gitleaks/releases/download/v8.18.0/gitleaks_8.18.0_linux_x64.tar.gz
tar -xzf gitleaks.tar.gz
sudo mv gitleaks /usr/local/bin/ && chmod +x /usr/local/bin/gitleaks
- name: Run Gitleaks
run: gitleaks detect --source . -v --report-path gitleaks-results.json --report-format json --no-git || true
- name: Upload Gitleaks to DefectDojo
env:
DD_URL: ${{ secrets.DD_URL }}
DD_TOKEN: ${{ secrets.DD_TOKEN }}
run: |
echo "Uploading Gitleaks results..."
TODAY=$(date +%Y-%m-%d)
HTTP_CODE=$(curl --write-out "%{http_code}" --output response.txt --silent -X POST "$DD_URL/api/v2/import-scan/" \
-H "Authorization: Token $DD_TOKEN" \
-F "active=true" \
-F "verified=true" \
-F "scan_type=Gitleaks Scan" \
-F "engagement_name=CI/CD Pipeline" \
-F "product_name=${{ github.repository }}" \
-F "scan_date=$TODAY" \
-F "auto_create_context=true" \
-F "file=@gitleaks-results.json")
if [[ "$HTTP_CODE" != "200" && "$HTTP_CODE" != "201" ]]; then
echo "::error::Upload Failed with HTTP $HTTP_CODE"
echo "--- SERVER RESPONSE ---"
cat response.txt
echo "-----------------------"
exit 1
else
echo "Upload Success!"
fi
# --- 3. SEMGREP (SAST) ---
- name: Install Semgrep (via pipx)
run: |
sudo apt-get install pipx -y
pipx install semgrep
# Add pipx binary path to GITHUB_PATH so next steps can see 'semgrep'
echo "$HOME/.local/bin" >> $GITHUB_PATH
- name: Run Semgrep
run: semgrep scan --config=p/security-audit --config=p/owasp-top-ten --json --output semgrep-results.json . || true
- name: Upload Semgrep to DefectDojo
env:
DD_URL: ${{ secrets.DD_URL }}
DD_TOKEN: ${{ secrets.DD_TOKEN }}
run: |
echo "Uploading Semgrep results..."
TODAY=$(date +%Y-%m-%d)
HTTP_CODE=$(curl --write-out "%{http_code}" --output response.txt --silent -X POST "$DD_URL/api/v2/import-scan/" \
-H "Authorization: Token $DD_TOKEN" \
-F "active=true" \
-F "verified=true" \
-F "scan_type=Semgrep JSON Report" \
-F "engagement_name=CI/CD Pipeline" \
-F "product_name=${{ github.repository }}" \
-F "scan_date=$TODAY" \
-F "auto_create_context=true" \
-F "file=@semgrep-results.json")
if [[ "$HTTP_CODE" != "200" && "$HTTP_CODE" != "201" ]]; then
echo "::error::Upload Failed with HTTP $HTTP_CODE"
echo "--- SERVER RESPONSE ---"
cat response.txt
echo "-----------------------"
exit 1
else
echo "Upload Success!"
fi
+2
View File
@@ -0,0 +1,2 @@
node_modules
prod
+6
View File
@@ -0,0 +1,6 @@
{
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
"eslint.validate": ["typescript"]
}
+1 -1
View File
@@ -18,7 +18,7 @@ This page is currently deployed. [View the live website.]
## Feedback and Bugs ## Feedback and Bugs
If you have feedback or a bug report, please feel free to open a GitHub issue! If you have feedback or a bug report, please [log a ticket on our forum](https://support.nhcarrigan.com).
## Contributing ## Contributing
+5
View File
@@ -0,0 +1,5 @@
import NaomisConfig from "@nhcarrigan/eslint-config";
export default [
...NaomisConfig,
];
+35
View File
@@ -0,0 +1,35 @@
{
"name": "mommy-bot",
"version": "0.0.0",
"description": "",
"type": "module",
"main": "prod/index.js",
"scripts": {
"build": "tsc",
"dev": "tsx watch src/index.ts",
"lint": "eslint src --max-warnings 0",
"start": "op run --env-file=prod.env --no-masking -- node prod/index.js",
"test": "echo \"Error: no test specified\" && exit 0"
},
"keywords": [],
"author": "Naomi Carrigan",
"license": "See license in LICENSE.md",
"devDependencies": {
"@nhcarrigan/eslint-config": "5.2.0",
"@nhcarrigan/typescript-config": "4.0.0",
"@types/node": "22.13.9",
"@types/node-schedule": "2.1.8",
"eslint": "9.21.0",
"typescript": "5.8.2"
},
"dependencies": {
"@atproto/api": "0.19.0",
"@nhcarrigan/logger": "1.0.0",
"@slack/bolt": "4.2.1",
"@slack/oauth": "3.0.4",
"discord.js": "14.18.0",
"fastify": "5.8.1",
"no-profanity": "1.5.1",
"node-schedule": "2.1.1"
}
}
+5571
View File
File diff suppressed because it is too large Load Diff
+21
View File
@@ -0,0 +1,21 @@
# Security
# Do not execute any scripts of installed packages (project scripts still run)
ignoreDepScripts: true
# Do not automatically run pre/post scripts (e.g. preinstall, postbuild)
enablePrePostScripts: false
# Only allow packages published at least 10 days ago (reduces risk of compromised packages)
minimumReleaseAge: 14400
# Fail if a package's trust level has decreased compared to previous releases
trustPolicy: no-downgrade
# Ignore trust policy for packages published more than 1 year ago (predates provenance signing)
trustPolicyIgnoreAfter: 525960
# Fail if there are missing or invalid peer dependencies
strictPeerDependencies: true
# Prevent transitive dependencies from using exotic sources (git repos, direct tarball URLs)
blockExoticSubdeps: true
# Lockfile
# Allow the lockfile to be updated during install (set to true in CI for stricter reproducibility)
preferFrozenLockfile: false
+7
View File
@@ -0,0 +1,7 @@
DISCORD_TOKEN="op://Environment Variables - Naomi/Mommy/discord_token"
LOG_TOKEN="op://Environment Variables - Naomi/Alert Server/api_auth"
SLACK_CLIENT_SECRET="op://Environment Variables - Naomi/Mommy/slack_client_secret"
SLACK_SIGNING_SECRET="op://Environment Variables - Naomi/Mommy/slack_signing_secret"
SLACK_CLIENT_ID="op://Environment Variables - Naomi/Mommy/slack_client_id"
BSKY_PASSWORD="op://Environment Variables - Naomi/Mommy/bsky_pass"
SLACK_STATE_SECRET="op://Environment Variables - Naomi/Mommy/slack_state_secret"
+80
View File
@@ -0,0 +1,80 @@
/**
* @copyright nhcarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
import { AtpAgent } from "@atproto/api";
import pkg from "@slack/bolt";
import { Client, Events, MessageFlags } from "discord.js";
import { scheduleJob } from "node-schedule";
import { serve } from "./server/serve.js";
import { getMommy } from "./utils/getMommy.js";
import { logger } from "./utils/logger.js";
// eslint-disable-next-line @typescript-eslint/naming-convention -- Classes.
const { App, FileInstallationStore } = pkg;
const discord = new Client({ intents: [ ] });
discord.on(Events.ClientReady, () => {
void logger.log("info", "Connected to Discord!");
});
// eslint-disable-next-line @typescript-eslint/no-misused-promises -- This is intentional.
discord.on(Events.InteractionCreate, async(interaction) => {
if (!interaction.isChatInputCommand()) {
return;
}
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
const name = interaction.options.getString("name");
const response = await getMommy(name ?? undefined);
await interaction.editReply(response);
});
const slack = new App({
clientId: process.env.SLACK_CLIENT_ID ?? "",
clientSecret: process.env.SLACK_CLIENT_SECRET ?? "",
installationStore: new FileInstallationStore(),
installerOptions: {
directInstall: true,
},
scopes: [ "commands" ],
signingSecret: process.env.SLACK_SIGNING_SECRET ?? "",
stateSecret: process.env.SLACK_STATE_SECRET ?? "",
});
slack.command("/mommy", async({ ack, body, respond }) => {
await ack();
const trimmed = body.text.trim();
const name = trimmed.length > 0
? trimmed
: undefined;
const response = await getMommy(name);
await respond(response);
});
const bsky = new AtpAgent({
service: "https://bsky.social",
});
await discord.login(process.env.DISCORD_TOKEN);
await slack.start(8010);
await logger.log("info", "Connected to Slack!");
await bsky.login({
identifier: "mommy.naomi.party",
password: process.env.BSKY_PASSWORD ?? "",
});
await logger.log("info", "Connected to bsky.social!");
scheduleJob("0 9 * * *", async() => {
const response = await getMommy();
await bsky.post({
text: response,
});
await logger.log("info", "Posted to bsky!");
});
serve();
+88
View File
@@ -0,0 +1,88 @@
/**
* @copyright nhcarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
import fastify from "fastify";
import { logger } from "../utils/logger.js";
const html = `<!DOCTYPE html>
<html>
<head>
<title>Mommy Bot</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content="Mommy loves you~!" />
<script src="https://cdn.nhcarrigan.com/headers/index.js" async defer></script>
</head>
<body>
<main>
<h1>Mommy Bot</h1>
<img src="https://cdn.nhcarrigan.com/new-avatars/mommy-full.png" width="250" alt="Mommy" />
<section>
<p>Mommy loves you~!</p>
<a href="https://mommy-slack.nhcarrigan.com/slack/install" style="display: inline-block; background-color: #ffffff; color: black; padding: 10px 20px; text-decoration: none; border-radius: 4px; margin: 5px;">
<i class="fab fa-slack"></i> Add to Slack
</a>
<a href="https://discord.com/oauth2/authorize?client_id=1347642447643017289" class="social-button discord-button" style="display: inline-block; background-color: #5865F2; color: white; padding: 10px 20px; text-decoration: none; border-radius: 4px; margin: 5px;">
<i class="fab fa-discord"></i> Add to Discord
</a>
<a href="https://bsky.app/profile/mommy.naomi.party" class="social-button bluesky-button" style="display: inline-block; background-color: #0085FF; color: white; padding: 10px 20px; text-decoration: none; border-radius: 4px; margin: 5px;">
<i class="fab fa-bluesky"></i> Follow on Bluesky
</a>
</section>
<section>
<h2>Links</h2>
<p>
<a href="https://git.nhcarrigan.com/nhcarrigan/mommy-bot">
<i class="fa-solid fa-code"></i> Source Code
</a>
</p>
<p>
<a href="https://docs.nhcarrigan.com/">
<i class="fa-solid fa-book"></i> Documentation
</a>
</p>
<p>
<a href="https://chat.nhcarrigan.com">
<i class="fa-solid fa-circle-info"></i> Support
</a>
</p>
</section>
</main>
</body>
</html>`;
/**
* Starts up a web server for health monitoring.
*/
export const serve = (): void => {
try {
const server = fastify({
logger: false,
});
server.get("/", (_request, response) => {
response.header("Content-Type", "text/html");
response.send(html);
});
server.listen({ port: 8009 }, (error) => {
if (error) {
void logger.error("instantiate server", error);
return;
}
void logger.log("debug", "Server listening on port 8009.");
});
} catch (error) {
if (error instanceof Error) {
void logger.error("instantiate server", error);
return;
}
void logger.error("instantiate server", new Error("Unknown error"));
}
};
+22
View File
@@ -0,0 +1,22 @@
/**
* @copyright nhcarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
import { isProfane } from "no-profanity";
/**
* Fetches a mommy quote from the API.
* @param name - The user's name, if provided.
* @returns The mommy quote.
*/
export const getMommy = async(name = "dear"): Promise<string> => {
const finalName = isProfane(name)
? "dear"
: name;
const url = `https://mommy.nhcarrigan.com/api?name=${finalName}`;
const request = await fetch(url);
const response = await request.text();
return response;
};
+12
View File
@@ -0,0 +1,12 @@
/**
* @copyright nhcarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
import { Logger } from "@nhcarrigan/logger";
export const logger = new Logger(
"Mommy",
process.env.LOG_TOKEN ?? "",
);
+7
View File
@@ -0,0 +1,7 @@
{
"extends": "@nhcarrigan/typescript-config",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./prod",
},
}