Compare commits
53 Commits
gh-pages
...
feature/mo
| Author | SHA1 | Date | |
|---|---|---|---|
|
5e570201bd
|
|||
|
c5ce1bfbcd
|
|||
|
f0b3957536
|
|||
|
8984a61ec8
|
|||
| a8e03e5912 | |||
|
421821f1ee
|
|||
|
|
ae34160b12 | ||
|
61ac47fd05
|
|||
|
1ea099ff98
|
|||
|
93716ba3f8
|
|||
|
98cd99e27a
|
|||
|
|
f4f6046b78 | ||
|
d66c54d25d
|
|||
|
85663272c4
|
|||
|
4f6015a0c2
|
|||
|
5e4645097a
|
|||
|
|
9b88ae87cc | ||
|
|
8d711af84d | ||
|
143796f817
|
|||
|
37fdebdcab
|
|||
|
609e6dc507
|
|||
|
6d2743e7e8
|
|||
|
15beebf196
|
|||
|
5cfbc8d441
|
|||
|
f9385c9cf0
|
|||
|
64829441e5
|
|||
|
b09b0bcdae
|
|||
|
2007e906a4
|
|||
|
65524da6dc
|
|||
|
52b11eeffb
|
|||
|
6a8cf528e3
|
|||
|
2106734b88
|
|||
|
a72a2c3358
|
|||
|
e5a3e04e0c
|
|||
|
d520d52da8
|
|||
|
f48bd35319
|
|||
|
ac239fd090
|
|||
|
09645c30e6
|
|||
|
4750b3baf9
|
|||
|
7db7cad18f
|
|||
|
df8d0a65d3
|
|||
|
13ee710c74
|
|||
|
418f857298
|
|||
|
8f11bdc43e
|
|||
|
|
4f308f07f6 | ||
|
937358558d
|
|||
|
f61558cb18
|
|||
|
c55da7b33a
|
|||
|
782ac97028
|
|||
|
1f1762ff0c
|
|||
|
e57a9cb309
|
|||
|
4d4706c4ba
|
|||
|
e91ce19c03
|
34
.github/workflows/ci.yaml
vendored
34
.github/workflows/ci.yaml
vendored
@@ -1,12 +1,22 @@
|
|||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check:
|
check:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
strategy:
|
||||||
- name: Checkout
|
matrix:
|
||||||
uses: actions/checkout@v4
|
node-version: [20, 22]
|
||||||
- name: Install Deps
|
steps:
|
||||||
run: npm install -g pnpm && pnpm install
|
- name: Checkout
|
||||||
- name: Typecheck & Lint
|
uses: actions/checkout@v5
|
||||||
run: pnpm check
|
- name: Install Deps
|
||||||
|
uses: pnpm/action-setup@v4
|
||||||
|
- name: Setup Node
|
||||||
|
uses: actions/setup-node@v5
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node-version }}
|
||||||
|
cache: "pnpm"
|
||||||
|
- name: Install
|
||||||
|
run: pnpm install
|
||||||
|
- name: Typecheck & Lint
|
||||||
|
run: SKIP_ENV_VALIDATION=true turbo check
|
||||||
|
|||||||
32
.github/workflows/docs.yaml
vendored
32
.github/workflows/docs.yaml
vendored
@@ -9,7 +9,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: get the gh-pages repo
|
- name: get the gh-pages repo
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
ref: gh-pages
|
ref: gh-pages
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@ jobs:
|
|||||||
tar -cvf documentation.tar ./docs
|
tar -cvf documentation.tar ./docs
|
||||||
|
|
||||||
- name: create a document artifact
|
- name: create a document artifact
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: documentation
|
name: documentation
|
||||||
path: documentation.tar
|
path: documentation.tar
|
||||||
@@ -29,31 +29,29 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout src
|
- name: Checkout src
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- run: mkdir -p ./docs
|
- run: mkdir -p ./docs
|
||||||
- name: Download the existing documents artifact
|
- name: Download the existing documents artifact
|
||||||
uses: actions/download-artifact@v3
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: documentation
|
name: documentation
|
||||||
- run: tar -xf documentation.tar ./docs -C ./docs
|
- run: tar -xf documentation.tar ./docs -C ./docs
|
||||||
|
|
||||||
- name: Build
|
- name: Install Deps
|
||||||
uses: actions/setup-node@v3
|
run: npm install -g pnpm && pnpm install
|
||||||
with:
|
|
||||||
node-version: 16.x
|
|
||||||
cache: 'npm'
|
|
||||||
- run: npm ci
|
|
||||||
- run: npm run build # set up 'build' script in your package.json
|
|
||||||
|
|
||||||
- name: Build documents
|
- name: Build lib
|
||||||
run: npm run docs #set up 'docs' build script in your package.json
|
run: pnpm run build
|
||||||
|
|
||||||
|
- name: Build docs
|
||||||
|
run: pnpm run docs:generate
|
||||||
|
|
||||||
- name: tar the new docs
|
- name: tar the new docs
|
||||||
run: tar -cvf newdocumentation.tar ./docs
|
run: tar -cvf newdocumentation.tar ./docs
|
||||||
|
|
||||||
- name: create a new document artifact
|
- name: create a new document artifact
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: newdocumentation
|
name: newdocumentation
|
||||||
path: newdocumentation.tar
|
path: newdocumentation.tar
|
||||||
@@ -63,13 +61,13 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: checkout the gh-pages repo
|
- name: checkout the gh-pages repo
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
ref: gh-pages
|
ref: gh-pages
|
||||||
|
|
||||||
- run: mkdir -p ./docs
|
- run: mkdir -p ./docs
|
||||||
- name: Download the new documents artifact
|
- name: Download the new documents artifact
|
||||||
uses: actions/download-artifact@v3
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: newdocumentation
|
name: newdocumentation
|
||||||
- run: tar -xf newdocumentation.tar ./docs -C ./docs
|
- run: tar -xf newdocumentation.tar ./docs -C ./docs
|
||||||
@@ -78,6 +76,6 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
git config --global user.email "cis-oss@users.noreply.github.com"
|
git config --global user.email "cis-oss@users.noreply.github.com"
|
||||||
git config --global user.name "Continuous Integration"
|
git config --global user.name "Continuous Integration"
|
||||||
git add .
|
git add docs/ -f
|
||||||
git commit -m "CI updated the documentation"
|
git commit -m "CI updated the documentation"
|
||||||
git push
|
git push
|
||||||
76
.gitignore
vendored
76
.gitignore
vendored
@@ -1,38 +1,38 @@
|
|||||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
|
|
||||||
# dependencies
|
# dependencies
|
||||||
/node_modules
|
/**/node_modules
|
||||||
/.pnp
|
/.pnp
|
||||||
.pnp.js
|
.pnp.js
|
||||||
|
|
||||||
# testing
|
# testing
|
||||||
/coverage
|
/coverage
|
||||||
|
|
||||||
# production
|
# production
|
||||||
docs/
|
docs/
|
||||||
dist/
|
dist/
|
||||||
|
|
||||||
# misc
|
# misc
|
||||||
.DS_Store
|
.DS_Store
|
||||||
*.pem
|
*.pem
|
||||||
|
|
||||||
# debug
|
# debug
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
.pnpm-debug.log*
|
.pnpm-debug.log*
|
||||||
|
|
||||||
# local env files
|
# local env files
|
||||||
# do not commit any .env files to git, except for the .env.example file.
|
# do not commit any .env files to git, except for the .env.example file.
|
||||||
.env
|
.env
|
||||||
.env*.local
|
.env*.local
|
||||||
|
|
||||||
# typescript
|
# typescript
|
||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
|
|
||||||
# eslint
|
# eslint
|
||||||
.eslintcache
|
.eslintcache
|
||||||
|
|
||||||
# idea files
|
# idea files
|
||||||
.idea
|
.idea
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
npx lint-staged
|
pnpm exec lint-staged
|
||||||
|
|||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2025 Alix von Schirp
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
24
README.md
24
README.md
@@ -1,24 +0,0 @@
|
|||||||
# CIS Pushover Client
|
|
||||||
|
|
||||||
A client for Pushover, a service for sending notifications. Typesafe.
|
|
||||||
Supports sending the same message to multiple users.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
⚠️ This package is **not yet published** to npm.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pnpm add @cis-oss/pushover
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm install @cis-oss/pushover
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash
|
|
||||||
yarn add @cis-oss/pushover
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash
|
|
||||||
bun add @cis-oss/pushover
|
|
||||||
```
|
|
||||||
18
config/eslint.config.mjs
Normal file
18
config/eslint.config.mjs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import globals from "globals";
|
||||||
|
import pluginJs from "@eslint/js";
|
||||||
|
import tseslint from "typescript-eslint";
|
||||||
|
import eslintConfigPrettier from "eslint-config-prettier";
|
||||||
|
import onlyWarn from "eslint-plugin-only-warn";
|
||||||
|
|
||||||
|
/** @type {import('eslint').Linter.Config[]} */
|
||||||
|
export default [
|
||||||
|
{ languageOptions: { globals: globals.node } },
|
||||||
|
pluginJs.configs.recommended,
|
||||||
|
...tseslint.configs.recommended,
|
||||||
|
eslintConfigPrettier,
|
||||||
|
{
|
||||||
|
plugins: {
|
||||||
|
onlyWarn,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
7
config/jest.config.js
Normal file
7
config/jest.config.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
/** @type {import('ts-jest').JestConfigWithTsJest} **/
|
||||||
|
export default {
|
||||||
|
testEnvironment: "node",
|
||||||
|
transform: {
|
||||||
|
"^.+\.tsx?$": ["ts-jest", {}],
|
||||||
|
},
|
||||||
|
};
|
||||||
32
config/package.json
Normal file
32
config/package.json
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"name": "@repo/configs",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"exports": {
|
||||||
|
"./eslint": "./eslint.config.mjs",
|
||||||
|
"./jest": "./jest.config.mjs",
|
||||||
|
"./typedoc": "./typedoc.config.mjs"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@eslint/js": "^9.22.0",
|
||||||
|
"@shipgirl/typedoc-plugin-versions": "^0.3.0",
|
||||||
|
"@types/jest": "^29.5.14",
|
||||||
|
"eslint": "^9.22.0",
|
||||||
|
"eslint-config-prettier": "^10.1.1",
|
||||||
|
"eslint-plugin-only-warn": "^1.1.0",
|
||||||
|
"globals": "^16.4.0",
|
||||||
|
"jest": "^29.7.0",
|
||||||
|
"prettier": "3.5.3",
|
||||||
|
"ts-jest": "^29.2.6",
|
||||||
|
"typedoc": "^0.27.9",
|
||||||
|
"typedoc-github-theme": "^0.2.1",
|
||||||
|
"typedoc-plugin-coverage": "^3.4.1",
|
||||||
|
"typedoc-plugin-extras": "^4.0.0",
|
||||||
|
"typedoc-plugin-include-example": "^2.0.2",
|
||||||
|
"typedoc-plugin-inline-sources": "^1.2.1",
|
||||||
|
"typedoc-plugin-mdn-links": "^5.0.1",
|
||||||
|
"typedoc-plugin-zod": "^1.4.0",
|
||||||
|
"typescript": "^5.5.3",
|
||||||
|
"typescript-eslint": "^8.26.0"
|
||||||
|
},
|
||||||
|
"private": true
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"target": "es2022",
|
"target": "es2022",
|
||||||
|
"sourceMap": true,
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"moduleDetection": "force",
|
"moduleDetection": "force",
|
||||||
@@ -11,6 +12,7 @@
|
|||||||
|
|
||||||
/* Strictness */
|
/* Strictness */
|
||||||
"strict": true,
|
"strict": true,
|
||||||
|
"strictNullChecks": true,
|
||||||
"noUncheckedIndexedAccess": true,
|
"noUncheckedIndexedAccess": true,
|
||||||
"checkJs": true,
|
"checkJs": true,
|
||||||
|
|
||||||
@@ -20,6 +22,7 @@
|
|||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"incremental": true,
|
"incremental": true,
|
||||||
"outDir": "dist",
|
"outDir": "dist",
|
||||||
|
"declaration": true,
|
||||||
|
|
||||||
/* Path Aliases */
|
/* Path Aliases */
|
||||||
"baseUrl": "./src",
|
"baseUrl": "./src",
|
||||||
@@ -27,6 +30,6 @@
|
|||||||
"~/*": ["./src/*"]
|
"~/*": ["./src/*"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"include": ["**/*.ts", "**/*.tsx", "**/*.cjs", "**/*.js"],
|
"include": ["src/**/*.ts", "src/**/*.tsx"],
|
||||||
"exclude": ["node_modules", "docs", "dist"]
|
"exclude": ["node_modules/**/*", "docs/**/*", "dist/**/*"]
|
||||||
}
|
}
|
||||||
@@ -1,14 +1,7 @@
|
|||||||
import globals from "globals";
|
import repoConfig from "@repo/configs/eslint";
|
||||||
import pluginJs from "@eslint/js";
|
|
||||||
import tseslint from "typescript-eslint";
|
export default [
|
||||||
import eslintConfigPrettier from "eslint-config-prettier";
|
...repoConfig,
|
||||||
|
{ ignores: ["dist/**", "docs/**"] },
|
||||||
/** @type {import('eslint').Linter.Config[]} */
|
{ files: ["**/*.{ts}"] },
|
||||||
export default [
|
];
|
||||||
{ ignores: ["dist/**", "docs/**", ".*/**", "**/.*", "**/*.config.*"] },
|
|
||||||
{ files: ["**/*.{js,mjs,cjs,ts}"] },
|
|
||||||
{ languageOptions: { globals: globals.node } },
|
|
||||||
pluginJs.configs.recommended,
|
|
||||||
...tseslint.configs.recommended,
|
|
||||||
eslintConfigPrettier,
|
|
||||||
];
|
|
||||||
7
meta/notify-push/eslint.config.mjs
Normal file
7
meta/notify-push/eslint.config.mjs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import repoConfig from "@repo/configs/eslint";
|
||||||
|
|
||||||
|
export default [
|
||||||
|
...repoConfig,
|
||||||
|
{ ignores: ["dist/**", "docs/**"] },
|
||||||
|
{ files: ["**/*.{ts}"] },
|
||||||
|
];
|
||||||
3
meta/notify-push/jest.config.js
Normal file
3
meta/notify-push/jest.config.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import repoCofig from "@repo/configs/jest";
|
||||||
|
|
||||||
|
export default [...repoCofig];
|
||||||
90
meta/notify-push/package.json
Normal file
90
meta/notify-push/package.json
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
{
|
||||||
|
"name": "@cis-oss/notify-push",
|
||||||
|
"description": "Send push notifications to your users. Meta package wrapping several @cis-oss packages. See README.md for more information.",
|
||||||
|
"homepage": "https://cis-oss.github.io/notify",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"main": "src/index.ts",
|
||||||
|
"author": {
|
||||||
|
"email": "hi@b00tload.space",
|
||||||
|
"name": "Alix von Schirp",
|
||||||
|
"url": "https://b00tload.space"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/cis-oss/pushover.git"
|
||||||
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/cis-oss/pushover/issues"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"mobile",
|
||||||
|
"notification",
|
||||||
|
"notifications",
|
||||||
|
"push",
|
||||||
|
"pushover"
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"@cis-oss/pushover": "workspace:"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@eslint/js": "^9.22.0",
|
||||||
|
"@repo/configs": "workspace:",
|
||||||
|
"@shipgirl/typedoc-plugin-versions": "^0.3.0",
|
||||||
|
"@types/jest": "^29.5.14",
|
||||||
|
"@types/node": "^22.13.10",
|
||||||
|
"eslint": "^9.22.0",
|
||||||
|
"eslint-config-prettier": "^10.1.1",
|
||||||
|
"eslint-plugin-only-warn": "^1.1.0",
|
||||||
|
"globals": "^16.0.0",
|
||||||
|
"jest": "^29.7.0",
|
||||||
|
"shx": "^0.4.0",
|
||||||
|
"ts-jest": "^29.2.6",
|
||||||
|
"tsx": "^4.19.3",
|
||||||
|
"typedoc": "^0.27.9",
|
||||||
|
"typedoc-github-theme": "^0.2.1",
|
||||||
|
"typedoc-plugin-coverage": "^3.4.1",
|
||||||
|
"typedoc-plugin-extras": "^4.0.0",
|
||||||
|
"typedoc-plugin-include-example": "^2.0.2",
|
||||||
|
"typedoc-plugin-inline-sources": "^1.2.1",
|
||||||
|
"typedoc-plugin-mdn-links": "^5.0.1",
|
||||||
|
"typedoc-plugin-zod": "^1.4.0",
|
||||||
|
"typescript": "^5.5.3",
|
||||||
|
"typescript-eslint": "^8.26.0"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist/**/*.{js,ts,map}"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc",
|
||||||
|
"build:watch": "tsc --watch",
|
||||||
|
"check": "pnpm run lint:ci && pnpm run typecheck",
|
||||||
|
"clean": "shx rm -rf dist/",
|
||||||
|
"docs:generate": "typedoc",
|
||||||
|
"format:check": "prettier --check .",
|
||||||
|
"lint": "eslint .",
|
||||||
|
"lint:ci": "eslint --max-warnings 0",
|
||||||
|
"prepublishOnly": "pnpm run clean && pnpm run build",
|
||||||
|
"test": "jest",
|
||||||
|
"typecheck": "tsc --noEmit"
|
||||||
|
},
|
||||||
|
"private": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"maintainers": [
|
||||||
|
{
|
||||||
|
"name": "Alix von Schirp",
|
||||||
|
"email": "hi@b00tload.space",
|
||||||
|
"url": "https://b00tload.space"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Ole",
|
||||||
|
"email": "jateute123@gmail.com",
|
||||||
|
"url": "https://github.com/jateute"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"packageManager": "pnpm@10.6.5",
|
||||||
|
"publishConfig": {
|
||||||
|
"main": "dist/index.js",
|
||||||
|
"types": "dist/index.d.ts"
|
||||||
|
},
|
||||||
|
"type": "module"
|
||||||
|
}
|
||||||
3
meta/notify-push/src/index.ts
Normal file
3
meta/notify-push/src/index.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import { Pushover } from "@cis-oss/pushover";
|
||||||
|
|
||||||
|
export { Pushover };
|
||||||
12
meta/notify-push/tsconfig.json
Normal file
12
meta/notify-push/tsconfig.json
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"extends": "@repo/configs/tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
/* Path Aliases */
|
||||||
|
"baseUrl": "./src",
|
||||||
|
"paths": {
|
||||||
|
"~/*": ["./src/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["src/**/*.ts", "src/**/*.tsx"],
|
||||||
|
"exclude": ["node_modules/**/*", "docs/**/*", "dist/**/*"]
|
||||||
|
}
|
||||||
7
meta/notify/eslint.config.mjs
Normal file
7
meta/notify/eslint.config.mjs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import repoConfig from "@repo/configs/eslint";
|
||||||
|
|
||||||
|
export default [
|
||||||
|
...repoConfig,
|
||||||
|
{ ignores: ["dist/**", "docs/**"] },
|
||||||
|
{ files: ["**/*.{ts}"] },
|
||||||
|
];
|
||||||
3
meta/notify/jest.config.js
Normal file
3
meta/notify/jest.config.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import repoCofig from "@repo/configs/jest";
|
||||||
|
|
||||||
|
export default [...repoCofig];
|
||||||
90
meta/notify/package.json
Normal file
90
meta/notify/package.json
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
{
|
||||||
|
"name": "@cis-oss/notify",
|
||||||
|
"description": "Send push notifications to your users. Meta package wrapping several @cis-oss packages. See README.md for more information.",
|
||||||
|
"homepage": "https://cis-oss.github.io/notify",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"main": "src/index.ts",
|
||||||
|
"author": {
|
||||||
|
"email": "hi@b00tload.space",
|
||||||
|
"name": "Alix von Schirp",
|
||||||
|
"url": "https://b00tload.space"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/cis-oss/pushover.git"
|
||||||
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/cis-oss/pushover/issues"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"mobile",
|
||||||
|
"notification",
|
||||||
|
"notifications",
|
||||||
|
"push",
|
||||||
|
"pushover"
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"@cis-oss/pushover": "workspace:"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@repo/configs": "workspace:",
|
||||||
|
"@eslint/js": "^9.22.0",
|
||||||
|
"@shipgirl/typedoc-plugin-versions": "^0.3.0",
|
||||||
|
"@types/jest": "^29.5.14",
|
||||||
|
"@types/node": "^22.13.10",
|
||||||
|
"eslint": "^9.22.0",
|
||||||
|
"eslint-config-prettier": "^10.1.1",
|
||||||
|
"eslint-plugin-only-warn": "^1.1.0",
|
||||||
|
"globals": "^16.0.0",
|
||||||
|
"jest": "^29.7.0",
|
||||||
|
"shx": "^0.4.0",
|
||||||
|
"ts-jest": "^29.2.6",
|
||||||
|
"tsx": "^4.19.3",
|
||||||
|
"typedoc": "^0.27.9",
|
||||||
|
"typedoc-github-theme": "^0.2.1",
|
||||||
|
"typedoc-plugin-coverage": "^3.4.1",
|
||||||
|
"typedoc-plugin-extras": "^4.0.0",
|
||||||
|
"typedoc-plugin-include-example": "^2.0.2",
|
||||||
|
"typedoc-plugin-inline-sources": "^1.2.1",
|
||||||
|
"typedoc-plugin-mdn-links": "^5.0.1",
|
||||||
|
"typedoc-plugin-zod": "^1.4.0",
|
||||||
|
"typescript": "^5.5.3",
|
||||||
|
"typescript-eslint": "^8.26.0"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist/**/*.{js,ts,map}"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc",
|
||||||
|
"build:watch": "tsc --watch",
|
||||||
|
"check": "pnpm run lint:ci && pnpm run typecheck",
|
||||||
|
"clean": "shx rm -rf dist/",
|
||||||
|
"docs:generate": "typedoc",
|
||||||
|
"format:check": "prettier --check .",
|
||||||
|
"lint": "eslint .",
|
||||||
|
"lint:ci": "eslint --max-warnings 0",
|
||||||
|
"prepublishOnly": "pnpm run clean && pnpm run build",
|
||||||
|
"test": "jest",
|
||||||
|
"typecheck": "tsc --noEmit"
|
||||||
|
},
|
||||||
|
"private": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"maintainers": [
|
||||||
|
{
|
||||||
|
"name": "Alix von Schirp",
|
||||||
|
"email": "hi@b00tload.space",
|
||||||
|
"url": "https://b00tload.space"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Ole",
|
||||||
|
"email": "jateute123@gmail.com",
|
||||||
|
"url": "https://github.com/jateute"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"packageManager": "pnpm@10.6.5",
|
||||||
|
"publishConfig": {
|
||||||
|
"main": "dist/index.js",
|
||||||
|
"types": "dist/index.d.ts"
|
||||||
|
},
|
||||||
|
"type": "module"
|
||||||
|
}
|
||||||
3
meta/notify/src/index.ts
Normal file
3
meta/notify/src/index.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import { Pushover } from "@cis-oss/pushover";
|
||||||
|
|
||||||
|
export { Pushover };
|
||||||
12
meta/notify/tsconfig.json
Normal file
12
meta/notify/tsconfig.json
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"extends": "@repo/configs/tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
/* Path Aliases */
|
||||||
|
"baseUrl": "./src",
|
||||||
|
"paths": {
|
||||||
|
"~/*": ["./src/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["src/**/*.ts", "src/**/*.tsx"],
|
||||||
|
"exclude": ["node_modules/**/*", "docs/**/*", "dist/**/*"]
|
||||||
|
}
|
||||||
29
meta/notify/typedoc.config.mjs
Normal file
29
meta/notify/typedoc.config.mjs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
/** @type {import("typedoc").TypeDocOptions &
|
||||||
|
* import("typedoc-plugin-extras").ExtrasOptions &
|
||||||
|
* import("typedoc-plugin-coverage").CoverageOptions &
|
||||||
|
* import("typedoc-plugin-mdn-links").MdnLinksOptions &
|
||||||
|
* import("typedoc-plugin-zod").ZodOptions} */
|
||||||
|
const config = {
|
||||||
|
compilerOptions: {
|
||||||
|
skipLibCheck: true,
|
||||||
|
strict: false,
|
||||||
|
},
|
||||||
|
entryPoints: ["src/index.ts"],
|
||||||
|
out: "docs",
|
||||||
|
plugin: [
|
||||||
|
"typedoc-plugin-coverage",
|
||||||
|
"typedoc-plugin-extras",
|
||||||
|
"typedoc-plugin-inline-sources",
|
||||||
|
"typedoc-plugin-mdn-links",
|
||||||
|
"typedoc-plugin-zod",
|
||||||
|
"typedoc-plugin-include-example",
|
||||||
|
"@shipgirl/typedoc-plugin-versions",
|
||||||
|
"typedoc-github-theme",
|
||||||
|
],
|
||||||
|
customFooterHtml: '<p class="tsd-generator" style="display: inline-flex; flex-direction: row; justify-content: space-around; width: 100%;"> <span> Made with ❤ by <a href="https://b00tload.space">Alix von Schirp</a> @ <a href="https://github.com/cis-oss">CISLabs OSS</a> </span> <span> <a href="https://github.com/cis-oss/pushover" target="_blank">GitHub</a> | <a href="https://github.com/cis-oss/pushover/issues" target="_blank">Issues</a> | <a href="https://github.com/cis-oss/pushover/blob/main/LICENSE" target="_blank">License</a> </span> <span>Generated using <a href="https://typedoc.org/" target="_blank">TypeDoc </a> with <a href="https://github.com/JulianWowra/typedoc-github-theme" target="_blank">typedoc-github-theme</a></span></p>',
|
||||||
|
coverageOutputType: "json",
|
||||||
|
hideGenerator: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
68
package.json
68
package.json
@@ -1,66 +1,34 @@
|
|||||||
{
|
{
|
||||||
"name": "@cis-oss/pushover",
|
"name": "@repo/root",
|
||||||
"version": "0.0.1",
|
"description": "",
|
||||||
"type": "module",
|
"files": [],
|
||||||
"description": "A client for Pushover, a service for sending notifications. Written in TypeScript. Supports sending to multiple users.",
|
"private": true,
|
||||||
"keywords": [
|
|
||||||
"pushover",
|
|
||||||
"notifications",
|
|
||||||
"mobile",
|
|
||||||
"push",
|
|
||||||
"notification"
|
|
||||||
],
|
|
||||||
"homepage": "https://github.com/cis-oss/pushover#readme",
|
|
||||||
"bugs": "https://github.com/cis-oss/pushover/issues",
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "github:cis-oss/pushover"
|
|
||||||
},
|
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Alix von Schirp",
|
|
||||||
"email": "hi@b00tload.space",
|
"email": "hi@b00tload.space",
|
||||||
|
"name": "Alix von Schirp",
|
||||||
"url": "https://b00tload.space"
|
"url": "https://b00tload.space"
|
||||||
},
|
},
|
||||||
"main": "dist/index.js",
|
|
||||||
"scripts": {
|
|
||||||
"build": "tsc && typedoc",
|
|
||||||
"lint": "eslint --cache .",
|
|
||||||
"typecheck": "tsc --noEmit",
|
|
||||||
"format:check": "prettier --check .",
|
|
||||||
"check": "pnpm lint && pnpm typecheck && pnpm format:check",
|
|
||||||
"prepare": "husky",
|
|
||||||
"docs:create": "typedoc"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.22.0",
|
"@repo/configs": "workspace:",
|
||||||
"@shipgirl/typedoc-plugin-versions": "^0.3.0",
|
"@turbo/gen": "^2.5.6",
|
||||||
"@types/node": "^22.13.10",
|
|
||||||
"eslint": "^9.22.0",
|
"eslint": "^9.22.0",
|
||||||
"eslint-config-prettier": "^10.1.1",
|
"finepack": "^2.12.7",
|
||||||
"globals": "^16.0.0",
|
|
||||||
"husky": "^9.1.7",
|
"husky": "^9.1.7",
|
||||||
"lint-staged": "^15.4.3",
|
"lint-staged": "^15.4.3",
|
||||||
"prettier": "3.5.3",
|
"prettier": "3.5.3",
|
||||||
"typedoc": "^0.27.9",
|
"turbo": "^2.5.6"
|
||||||
"typedoc-plugin-coverage": "^3.4.1",
|
|
||||||
"typedoc-plugin-extras": "^4.0.0",
|
|
||||||
"typedoc-plugin-include-example": "^2.0.2",
|
|
||||||
"typedoc-plugin-inline-sources": "^1.2.1",
|
|
||||||
"typedoc-plugin-mdn-links": "^5.0.1",
|
|
||||||
"typedoc-plugin-zod": "^1.4.0",
|
|
||||||
"typescript": "^5.5.3",
|
|
||||||
"typescript-eslint": "^8.26.0"
|
|
||||||
},
|
},
|
||||||
"private": true,
|
"scripts": {
|
||||||
"lint-staged": {
|
"prepare": "husky"
|
||||||
"*.{js,ts,jsx,tsx}": "eslint --cache --fix . || true",
|
|
||||||
"*.{js,ts,jsx,tsx,json,css,md}": "prettier --write"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"zod": "^3.24.2"
|
|
||||||
},
|
},
|
||||||
"husky": {
|
"husky": {
|
||||||
"shell": "bash"
|
"shell": "bash"
|
||||||
}
|
},
|
||||||
|
"lint-staged": {
|
||||||
|
"package.json": "finepack",
|
||||||
|
"*.{js,ts,jsx,tsx}": "eslint --fix .",
|
||||||
|
"*.{js,ts,jsx,tsx,json,css,md}": "prettier --write"
|
||||||
|
},
|
||||||
|
"version": "0.0.0"
|
||||||
}
|
}
|
||||||
|
|||||||
48
packages/pushover/README.md
Normal file
48
packages/pushover/README.md
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
# CIS Pushover Client
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
A client for Pushover, a service for sending notifications. Typesafe.
|
||||||
|
Supports sending the same message to multiple users.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm add @cis-oss/pushover
|
||||||
|
```
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Or using npm</summary>
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install @cis-oss/pushover
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Or use our meta-packages</summary>
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm add @cis-oss/notify
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm add @cis-oss/notify-push
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
# Documentation
|
||||||
|
|
||||||
|
Documentation can be found at [https://cis-oss.github.io/pushover](https://cis-oss.github.io/pushover).
|
||||||
|
|
||||||
|
# Contributing
|
||||||
|
|
||||||
|
Contributions are welcome! Please see [CONTRIBUTING.md](https://github.com/cis-oss/.github/blob/main/CONTRIBUTING.md) for details.
|
||||||
|
|
||||||
|
Please make sure to read our [Code of Conduct](https://github.com/cis-oss/.github/blob/main/CODE_OF_CONDUCT.md) and [Support Policy](https://github.com/cis-oss/.github/blob/main/SUPPORT.md).
|
||||||
|
|
||||||
|
Disclose security issues responsibly by following our [Security Policy](https://github.com/cis-oss/.github/blob/main/SECURITY.md).
|
||||||
7
packages/pushover/eslint.config.mjs
Normal file
7
packages/pushover/eslint.config.mjs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import repoConfig from "@repo/configs/eslint";
|
||||||
|
|
||||||
|
export default [
|
||||||
|
...repoConfig,
|
||||||
|
{ ignores: ["dist/**", "docs/**"] },
|
||||||
|
{ files: ["**/*.{ts}"] },
|
||||||
|
];
|
||||||
3
packages/pushover/jest.config.js
Normal file
3
packages/pushover/jest.config.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import repoCofig from "@repo/configs/jest";
|
||||||
|
|
||||||
|
export default [...repoCofig];
|
||||||
90
packages/pushover/package.json
Normal file
90
packages/pushover/package.json
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
{
|
||||||
|
"name": "@cis-oss/pushover",
|
||||||
|
"description": "A client for Pushover, a service for sending notifications. Written in TypeScript. Supports sending to multiple users.",
|
||||||
|
"homepage": "https://cis-oss.github.io/pushover",
|
||||||
|
"version": "0.0.10",
|
||||||
|
"main": "src/index.ts",
|
||||||
|
"author": {
|
||||||
|
"email": "hi@b00tload.space",
|
||||||
|
"name": "Alix von Schirp",
|
||||||
|
"url": "https://b00tload.space"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/cis-oss/pushover.git"
|
||||||
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/cis-oss/pushover/issues"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"mobile",
|
||||||
|
"notification",
|
||||||
|
"notifications",
|
||||||
|
"push",
|
||||||
|
"pushover"
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"zod": "^3.24.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@repo/configs": "workspace:",
|
||||||
|
"@eslint/js": "^9.22.0",
|
||||||
|
"@shipgirl/typedoc-plugin-versions": "^0.3.0",
|
||||||
|
"@types/jest": "^29.5.14",
|
||||||
|
"@types/node": "^22.13.10",
|
||||||
|
"eslint": "^9.22.0",
|
||||||
|
"eslint-config-prettier": "^10.1.1",
|
||||||
|
"eslint-plugin-only-warn": "^1.1.0",
|
||||||
|
"globals": "^16.0.0",
|
||||||
|
"jest": "^29.7.0",
|
||||||
|
"shx": "^0.4.0",
|
||||||
|
"ts-jest": "^29.2.6",
|
||||||
|
"tsx": "^4.19.3",
|
||||||
|
"typedoc": "^0.27.9",
|
||||||
|
"typedoc-github-theme": "^0.2.1",
|
||||||
|
"typedoc-plugin-coverage": "^3.4.1",
|
||||||
|
"typedoc-plugin-extras": "^4.0.0",
|
||||||
|
"typedoc-plugin-include-example": "^2.0.2",
|
||||||
|
"typedoc-plugin-inline-sources": "^1.2.1",
|
||||||
|
"typedoc-plugin-mdn-links": "^5.0.1",
|
||||||
|
"typedoc-plugin-zod": "^1.4.0",
|
||||||
|
"typescript": "^5.5.3",
|
||||||
|
"typescript-eslint": "^8.26.0"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist/**/*.{js,ts,map}"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc",
|
||||||
|
"build:watch": "tsc --watch",
|
||||||
|
"check": "pnpm run lint:ci && pnpm run typecheck",
|
||||||
|
"clean": "shx rm -rf dist/",
|
||||||
|
"docs:generate": "typedoc",
|
||||||
|
"format:check": "prettier --check .",
|
||||||
|
"lint": "eslint .",
|
||||||
|
"lint:ci": "eslint --max-warnings 0",
|
||||||
|
"prepublishOnly": "pnpm run clean && pnpm run build",
|
||||||
|
"test": "jest",
|
||||||
|
"typecheck": "tsc --noEmit"
|
||||||
|
},
|
||||||
|
"private": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"maintainers": [
|
||||||
|
{
|
||||||
|
"name": "Alix von Schirp",
|
||||||
|
"email": "hi@b00tload.space",
|
||||||
|
"url": "https://b00tload.space"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Ole",
|
||||||
|
"email": "jateute123@gmail.com",
|
||||||
|
"url": "https://github.com/jateute"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"packageManager": "pnpm@10.6.5",
|
||||||
|
"publishConfig": {
|
||||||
|
"main": "dist/index.js",
|
||||||
|
"types": "dist/index.d.ts"
|
||||||
|
},
|
||||||
|
"type": "module"
|
||||||
|
}
|
||||||
718
packages/pushover/src/Pushover.ts
Normal file
718
packages/pushover/src/Pushover.ts
Normal file
@@ -0,0 +1,718 @@
|
|||||||
|
import https from "node:https";
|
||||||
|
import { URLSearchParams } from "node:url";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
* Defines the internal Zod schema for validating Pushover message payloads.
|
||||||
|
* This ensures messages conform to the Pushover API requirements before sending.
|
||||||
|
* Includes validation rules for required fields, formats, and conditional requirements.
|
||||||
|
*/
|
||||||
|
const MessageSchema = z
|
||||||
|
.object({
|
||||||
|
/**
|
||||||
|
* The message content sent to the user. Must be at least 3 characters long.
|
||||||
|
*/
|
||||||
|
message: z.string().min(3),
|
||||||
|
/**
|
||||||
|
* An optional title for the message.
|
||||||
|
*/
|
||||||
|
title: z.string().optional(),
|
||||||
|
/**
|
||||||
|
* An optional link attached to the message.
|
||||||
|
* Can be either a simple URL string or an object containing the URL and an optional display title.
|
||||||
|
*/
|
||||||
|
link: z
|
||||||
|
.string()
|
||||||
|
.url()
|
||||||
|
.or(
|
||||||
|
z.object({
|
||||||
|
/**
|
||||||
|
* The URL of the link.
|
||||||
|
*/
|
||||||
|
url: z.string().url(),
|
||||||
|
/**
|
||||||
|
* The title displayed for the link.
|
||||||
|
*/
|
||||||
|
title: z.string().optional(),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.optional(),
|
||||||
|
/**
|
||||||
|
* Sets the notification priority for the message.
|
||||||
|
* Defaults to 0 (normal priority).
|
||||||
|
*
|
||||||
|
* - -2: Message only, no notification sound/vibration. May increment the notification bubble.
|
||||||
|
* - -1: Silent notification (no sound/vibration).
|
||||||
|
* - 0: Default notification behavior.
|
||||||
|
* - 1: High priority, ignores user's quiet hours.
|
||||||
|
* - 2: Emergency priority, requires acknowledgement. Requires `emergencyOpts`.
|
||||||
|
*/
|
||||||
|
priority: z
|
||||||
|
.union([
|
||||||
|
z.literal(-2),
|
||||||
|
z.literal(-1),
|
||||||
|
z.literal(0),
|
||||||
|
z.literal(1),
|
||||||
|
z.literal(2),
|
||||||
|
])
|
||||||
|
.optional()
|
||||||
|
.default(0),
|
||||||
|
/**
|
||||||
|
* Emergency priority options, required when `priority` is 2.
|
||||||
|
*/
|
||||||
|
emergencyOpts: z
|
||||||
|
.object({
|
||||||
|
/**
|
||||||
|
* Specifies how often (in seconds) the Pushover servers will send the same notification to the user.
|
||||||
|
* Minimum value is 30 seconds.
|
||||||
|
*/
|
||||||
|
retry: z.number().min(30),
|
||||||
|
/**
|
||||||
|
* Specifies how long (in seconds) the notification will continue to be resent.
|
||||||
|
* Maximum value is 10800 seconds (3 hours).
|
||||||
|
*/
|
||||||
|
expire: z.number().max(10800),
|
||||||
|
/**
|
||||||
|
* An optional callback URL that Pushover servers will send a request to when the notification has been acknowledged.
|
||||||
|
*/
|
||||||
|
callback: z.string().url().optional(),
|
||||||
|
/**
|
||||||
|
* Optional tags for emergency notifications. Helps with cancelling retries.
|
||||||
|
*/
|
||||||
|
tags: z.string().array().optional(),
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
|
/**
|
||||||
|
* The name of one of the predefined Pushover sounds or a custom sound uploaded by the user to be played for the notification.
|
||||||
|
*/
|
||||||
|
sound: z.string().optional(),
|
||||||
|
/**
|
||||||
|
* An optional Unix timestamp representing the message's date and time to display to the user, rather than the time Pushover received it.
|
||||||
|
*/
|
||||||
|
timestamp: z.number().optional(),
|
||||||
|
/**
|
||||||
|
* If set to true, the message content will be treated as HTML.
|
||||||
|
* Mutually exclusive with `monospace`.
|
||||||
|
*/
|
||||||
|
html: z.boolean().optional().default(false),
|
||||||
|
/**
|
||||||
|
* If set to true, the message content will be displayed using a monospace font.
|
||||||
|
* Mutually exclusive with `html`.
|
||||||
|
*/
|
||||||
|
monospace: z.boolean().optional().default(false),
|
||||||
|
/**
|
||||||
|
* Time To Live in seconds. Specifies how long the message will be kept until disappearing.
|
||||||
|
*/
|
||||||
|
ttl: z.number().optional(),
|
||||||
|
})
|
||||||
|
/**
|
||||||
|
* Validation rule: Ensures that if the priority is set to 2 (emergency),
|
||||||
|
* the `emergencyOpts` object must be provided.
|
||||||
|
*/
|
||||||
|
.refine(
|
||||||
|
(data) => {
|
||||||
|
// If priority is 2, emergencyOpts must exist.
|
||||||
|
return !(data.priority === 2 && !data.emergencyOpts);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ["priority", "emergencyOpts"], // Path related to the error
|
||||||
|
message: "If priority is set to 2, emergencyOpts must be included.",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
/**
|
||||||
|
* Validation rule: Ensures that `html` and `monospace` formatting options
|
||||||
|
* are mutually exclusive and cannot be enabled simultaneously.
|
||||||
|
*/
|
||||||
|
.refine(
|
||||||
|
(data) => {
|
||||||
|
// Cannot have both html and monospace set to true.
|
||||||
|
return !(data.html && data.monospace);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ["html", "monospace"], // Path related to the error
|
||||||
|
message: "html and monospace are mutually exclusive.",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the structure for a Pushover message object used when calling the `send` method.
|
||||||
|
*
|
||||||
|
* This type represents the complete set of parameters you can provide for a
|
||||||
|
* Pushover notification. It includes the required `message` field and various
|
||||||
|
* optional fields to customize the notification's appearance, behavior, priority,
|
||||||
|
* sound, and delivery options.
|
||||||
|
*
|
||||||
|
* Refer to the official Pushover API documentation for detailed explanations of each field.
|
||||||
|
* Note the specific constraints:
|
||||||
|
* - `emergencyOpts` must be provided if `priority` is set to `2`.
|
||||||
|
* - `html` and `monospace` formatting options cannot be used together.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* import type { PushoverMessage } from '@cis-oss/pushover';
|
||||||
|
*
|
||||||
|
* const standardMessage: PushoverMessage = {
|
||||||
|
* message: "Deployment successful!",
|
||||||
|
* title: "Server Update",
|
||||||
|
* priority: 1, // High priority
|
||||||
|
* sound: "pushover",
|
||||||
|
* link: {
|
||||||
|
* url: "https://example.com/deployment/status",
|
||||||
|
* title: "View Status"
|
||||||
|
* }
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* const emergencyMessage: PushoverMessage = {
|
||||||
|
* message: "System critical: Service down!",
|
||||||
|
* priority: 2,
|
||||||
|
* emergencyOpts: {
|
||||||
|
* retry: 60, // Retry every 60 seconds
|
||||||
|
* expire: 3600, // Expire after 1 hour
|
||||||
|
* tags: ["critical", "infra"]
|
||||||
|
* },
|
||||||
|
* };
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export type PushoverMessage = z.input<typeof MessageSchema>;
|
||||||
|
|
||||||
|
type PushoverMessageParsed = z.output<typeof MessageSchema>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base interface for all Pushover API responses
|
||||||
|
*/
|
||||||
|
interface PushoverResponse {
|
||||||
|
/** Indicates the status of the request. `1` for success, `0` for failure. */
|
||||||
|
status: 0 | 1;
|
||||||
|
/** A unique identifier for the API request, generated by Pushover. */
|
||||||
|
request: string;
|
||||||
|
/** An array of error messages if the request failed (`status` is `0`). */
|
||||||
|
errors?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the response received after successfully sending a Pushover message.
|
||||||
|
*/
|
||||||
|
export interface PushoverMessageResponse extends PushoverResponse {
|
||||||
|
/**
|
||||||
|
* A receipt ID, returned only for messages sent with emergency priority (`priority: 2`).
|
||||||
|
* This ID can be used to check the acknowledgement status or cancel retries.
|
||||||
|
*/
|
||||||
|
receipt?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the response received after validating a user or user/device combination.
|
||||||
|
*/
|
||||||
|
export interface PushoverValidationResponse extends PushoverResponse {
|
||||||
|
/** A list of the user's registered device names, returned on successful validation. */
|
||||||
|
devices?: string[];
|
||||||
|
/** A list of the user's Pushover license types (e.g., 'Android', 'iOS', 'Desktop'). */
|
||||||
|
licenses?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the response received when checking the status of an emergency message receipt.
|
||||||
|
*/
|
||||||
|
export interface PushoverReceiptResponse extends PushoverResponse {
|
||||||
|
/** `true` if the emergency notification has been acknowledged by the user, `false` otherwise. */
|
||||||
|
acknowledged: boolean;
|
||||||
|
/** A Unix timestamp indicating when the notification was acknowledged. `0` if not acknowledged. */
|
||||||
|
acknowledged_at: number;
|
||||||
|
/** The user key of the user who first acknowledged the notification. Empty if not acknowledged. */
|
||||||
|
acknowledged_by: string;
|
||||||
|
/** The name of the device that first acknowledged the notification. Empty if not acknowledged. */
|
||||||
|
acknowledged_by_device: string;
|
||||||
|
/** A Unix timestamp indicating the last time the notification was delivered (due to retries). `0` if not delivered. */
|
||||||
|
last_delivered_at: number;
|
||||||
|
/** `true` if the notification has expired without acknowledgement, `false` otherwise. */
|
||||||
|
expired: boolean;
|
||||||
|
/** A Unix timestamp indicating when the notification expired. `0` if not expired. */
|
||||||
|
expired_at: number;
|
||||||
|
/** `true` if the optional callback URL was successfully contacted, `false` otherwise. */
|
||||||
|
called_back: boolean;
|
||||||
|
/** A Unix timestamp indicating when the callback URL was contacted. `0` if not called back. */
|
||||||
|
called_back_at: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the response received when cancelling emergency message retries by tag.
|
||||||
|
*/
|
||||||
|
export interface PushoverTagCancellationResponse extends PushoverResponse {
|
||||||
|
/** The number of emergency message retries that were successfully cancelled for the given tag. */
|
||||||
|
canceled: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the options for the `send` method, primarily specifying the recipients.
|
||||||
|
*/
|
||||||
|
export interface SendOptions {
|
||||||
|
/** An array of `PushoverRecipient` objects, each specifying a user/group and optional devices. */
|
||||||
|
recipients: PushoverRecipient[];
|
||||||
|
/** If true, enables verbose logging to the console during the send operation. Defaults to false. */
|
||||||
|
verbose?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the options for the `validate` method.
|
||||||
|
*/
|
||||||
|
export interface ValidateOptions {
|
||||||
|
/** The Pushover user key to validate. */
|
||||||
|
user: string;
|
||||||
|
/** An optional device name to validate along with the user key. */
|
||||||
|
deviceName?: string;
|
||||||
|
/** If true, enables verbose logging to the console during the send operation. Defaults to false. */
|
||||||
|
verbose?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a single Pushover recipient, which can be a user or a group.
|
||||||
|
*/
|
||||||
|
export interface PushoverRecipient {
|
||||||
|
/** The Pushover user key or group key. */
|
||||||
|
id: string;
|
||||||
|
/** An optional array of specific device names belonging to the user to send the notification to. If omitted, sends to all user's devices. */
|
||||||
|
devices?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main class for interacting with the Pushover API (v1).
|
||||||
|
* Provides methods for sending notifications, validating users/devices,
|
||||||
|
* and managing emergency priority messages.
|
||||||
|
*
|
||||||
|
* @param token - Your Pushover application's API token.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* import { Pushover } from "@cis-oss/pushover";
|
||||||
|
*
|
||||||
|
* // Initialize the client
|
||||||
|
* const pushover = new Pushover("YOUR_APP_API_TOKEN");
|
||||||
|
*
|
||||||
|
* // Define recipients
|
||||||
|
* const recipients = [
|
||||||
|
* { id: "USER_KEY_1" },
|
||||||
|
* { id: "USER_KEY_2", devices: ["DEVICE_1", "DEVICE_2"] },
|
||||||
|
* { id: "GROUP_KEY_1" },
|
||||||
|
* ];
|
||||||
|
*
|
||||||
|
* // Send a basic message
|
||||||
|
* const responses = pushover.send(
|
||||||
|
* {
|
||||||
|
* message: "Hello from the library!",
|
||||||
|
* title: "Test Message",
|
||||||
|
* },
|
||||||
|
* { recipients },
|
||||||
|
* );
|
||||||
|
*
|
||||||
|
* responses
|
||||||
|
* .then((responses) => {
|
||||||
|
* console.log("Messages sent:", responses);
|
||||||
|
* })
|
||||||
|
* .catch((error) => {
|
||||||
|
* console.error("Failed to send messages:", error);
|
||||||
|
* });
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export class Pushover {
|
||||||
|
private token: string;
|
||||||
|
private apiUrl = "https://api.pushover.net/1/";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance of the Pushover client.
|
||||||
|
* @param token - Your Pushover application's API token. Found on your Pushover dashboard.
|
||||||
|
*/
|
||||||
|
constructor(token: string) {
|
||||||
|
this.token = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a Pushover notification to one or more recipients.
|
||||||
|
*
|
||||||
|
* @param message - A `PushoverMessage` object containing the notification details.
|
||||||
|
* @param options - A `SendOptions` object specifying the recipients and optional settings.
|
||||||
|
* @returns A Promise resolving to an array of `PushoverMessageResponse` objects, one for each recipient.
|
||||||
|
* Rejects if message validation fails or if there's a fundamental issue sending to all recipients.
|
||||||
|
* Individual recipient failures are indicated within their respective response objects (`status: 0`).
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* // Send a message to a specific user and device
|
||||||
|
* const userRecipient: PushoverRecipient = { id: "user-key", devices: ["phone"] };
|
||||||
|
* await pushover.send(
|
||||||
|
* { message: "Targeted message" },
|
||||||
|
* { recipients: [userRecipient] },
|
||||||
|
* );
|
||||||
|
*
|
||||||
|
* // Send an emergency priority message and handle the receipt
|
||||||
|
* const responses = pushover.send(
|
||||||
|
* {
|
||||||
|
* message: "Emergency alert!",
|
||||||
|
* priority: 2,
|
||||||
|
* emergencyOpts: { retry: 30, expire: 3600 },
|
||||||
|
* },
|
||||||
|
* { recipients: [userRecipient] },
|
||||||
|
* );
|
||||||
|
*
|
||||||
|
* responses
|
||||||
|
* .then((responses) => {
|
||||||
|
* console.log(
|
||||||
|
* `Emergency message sent. Receipts: ${responses.map((response) => response.receipt).join(", ")}`,
|
||||||
|
* );
|
||||||
|
* // Store the receipt to check status or cancel later
|
||||||
|
* })
|
||||||
|
* .catch((error) => {
|
||||||
|
* console.error("Failed to send emergency message:", error);
|
||||||
|
* });
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
public async send(
|
||||||
|
message: PushoverMessage,
|
||||||
|
options: SendOptions,
|
||||||
|
): Promise<PushoverMessageResponse[]> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (options.recipients.length === 0) {
|
||||||
|
reject(new Error("No recipients specified."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
success,
|
||||||
|
error,
|
||||||
|
data: parsedMessage,
|
||||||
|
} = MessageSchema.safeParse(message);
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
reject(new Error(`Message validation failed: ${error}`));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.verbose) {
|
||||||
|
console.log("Verbose mode enabled. Logging message and options:");
|
||||||
|
console.log(parsedMessage);
|
||||||
|
console.log(options);
|
||||||
|
console.log("----------------------");
|
||||||
|
console.log("Sending message...");
|
||||||
|
}
|
||||||
|
|
||||||
|
const promises = options.recipients.map((recipient) =>
|
||||||
|
this.sendToSingleRecipient(
|
||||||
|
parsedMessage,
|
||||||
|
recipient,
|
||||||
|
options.verbose ?? false,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
resolve(Promise.all(promises));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
* Sends the validated message payload to a single recipient.
|
||||||
|
*
|
||||||
|
* @param message - The validated PushoverMessage object.
|
||||||
|
* @param recipient - The PushoverRecipient object.
|
||||||
|
* @param verbose - Optional flag for logging.
|
||||||
|
* @returns A Promise resolving to the PushoverMessageResponse.
|
||||||
|
*/
|
||||||
|
private async sendToSingleRecipient(
|
||||||
|
message: PushoverMessageParsed,
|
||||||
|
recipient: PushoverRecipient,
|
||||||
|
verbose?: boolean,
|
||||||
|
): Promise<PushoverMessageResponse> {
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
|
||||||
|
// Add token and user
|
||||||
|
params.append("token", this.token);
|
||||||
|
params.append("user", recipient.id);
|
||||||
|
params.append("device", recipient.devices?.join(",") ?? "");
|
||||||
|
|
||||||
|
// Add message properties
|
||||||
|
params.append("message", message.message);
|
||||||
|
if (message.title) params.append("title", message.title);
|
||||||
|
params.append("priority", "" + message.priority);
|
||||||
|
if (message.priority === 2 && message.emergencyOpts) {
|
||||||
|
params.append("retry", String(message.emergencyOpts.retry));
|
||||||
|
params.append("expire", String(message.emergencyOpts.expire));
|
||||||
|
if (message.emergencyOpts.callback)
|
||||||
|
params.append("callback", message.emergencyOpts.callback);
|
||||||
|
if (message.emergencyOpts.tags)
|
||||||
|
params.append("tags", message.emergencyOpts.tags.join());
|
||||||
|
}
|
||||||
|
if (message.link) {
|
||||||
|
if (typeof message.link === "string") {
|
||||||
|
params.append("url", message.link);
|
||||||
|
} else {
|
||||||
|
params.append("url", message.link.url);
|
||||||
|
if (message.link.title) params.append("url_title", message.link.title);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (message.html) params.append("html", "1");
|
||||||
|
if (message.monospace) params.append("monospace", "1");
|
||||||
|
if (message.sound) params.append("sound", message.sound);
|
||||||
|
if (message.timestamp)
|
||||||
|
params.append("timestamp", String(message.timestamp));
|
||||||
|
if (message.ttl) params.append("ttl", String(message.ttl));
|
||||||
|
|
||||||
|
return this.makeRequest<PushoverMessageResponse>(
|
||||||
|
"messages.json",
|
||||||
|
"POST",
|
||||||
|
params,
|
||||||
|
verbose ?? false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
* Makes an HTTPS request to the Pushover API.
|
||||||
|
*
|
||||||
|
* @param endpoint - The API endpoint path (e.g., "messages.json").
|
||||||
|
* @param method - The HTTP method ("POST" or "GET").
|
||||||
|
* @param params - URLSearchParams for POST body or query string.
|
||||||
|
* @param verbose - Optional flag for logging request/response details.
|
||||||
|
* @returns A Promise resolving to the parsed JSON response.
|
||||||
|
*/
|
||||||
|
private makeRequest<T extends PushoverResponse>(
|
||||||
|
endpoint: string,
|
||||||
|
method: "POST" | "GET",
|
||||||
|
params: URLSearchParams,
|
||||||
|
verbose?: boolean,
|
||||||
|
): Promise<T> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const url = this.apiUrl + endpoint;
|
||||||
|
let requestBody: string | null = null;
|
||||||
|
let requestUrl = url;
|
||||||
|
|
||||||
|
const options: https.RequestOptions = {
|
||||||
|
method: method,
|
||||||
|
headers: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (method === "POST") {
|
||||||
|
requestBody = params.toString();
|
||||||
|
options.headers!["Content-Type"] = "application/x-www-form-urlencoded";
|
||||||
|
options.headers!["Content-Length"] = Buffer.byteLength(requestBody);
|
||||||
|
} else {
|
||||||
|
// Append params to URL for GET requests
|
||||||
|
const queryString = params.toString();
|
||||||
|
if (queryString) {
|
||||||
|
requestUrl += "?" + queryString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
console.log(`Making ${method} request to ${requestUrl}`);
|
||||||
|
if (requestBody) {
|
||||||
|
console.log("Request Body:", requestBody);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const req = https.request(requestUrl, options, (res) => {
|
||||||
|
let data = "";
|
||||||
|
res.setEncoding("utf8"); // Ensure correct encoding
|
||||||
|
|
||||||
|
res.on("data", (chunk) => {
|
||||||
|
data += chunk;
|
||||||
|
});
|
||||||
|
|
||||||
|
res.on("end", () => {
|
||||||
|
if (verbose) {
|
||||||
|
console.log("Received response status:", res.statusCode);
|
||||||
|
console.log("Received response headers:", res.headers);
|
||||||
|
console.log(
|
||||||
|
`Received response body (length: ${data.length}): ${data}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// Handle potential empty responses or non-JSON responses gracefully
|
||||||
|
if (!data) {
|
||||||
|
// Reject promise on empty response
|
||||||
|
reject(
|
||||||
|
new Error(
|
||||||
|
`Request failed with status ${res.statusCode} and empty response.`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = JSON.parse(data) as T;
|
||||||
|
// Basic check for expected structure
|
||||||
|
if (
|
||||||
|
typeof response.status === "undefined" ||
|
||||||
|
typeof response.request === "undefined"
|
||||||
|
) {
|
||||||
|
reject(new Error(`Invalid response structure received: ${data}`));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(response);
|
||||||
|
} catch (error) {
|
||||||
|
if (verbose) console.error("Failed to parse JSON response:", error);
|
||||||
|
reject(
|
||||||
|
new Error(`Failed to parse API response. Raw data: ${data}`),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
req.on("error", (error) => {
|
||||||
|
if (verbose)
|
||||||
|
console.error(`HTTPS request error to ${endpoint}:`, error);
|
||||||
|
reject(new Error(`API request failed: ${error.message}`));
|
||||||
|
});
|
||||||
|
|
||||||
|
if (method === "POST" && requestBody) {
|
||||||
|
req.write(requestBody);
|
||||||
|
}
|
||||||
|
|
||||||
|
req.end();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates a Pushover user key and optionally a specific device name associated with that user.
|
||||||
|
* Useful for verifying recipient details before sending messages.
|
||||||
|
*
|
||||||
|
* @param options - A `ValidateOptions` object containing the `user` key and optional `deviceName`.
|
||||||
|
* @returns A Promise resolving to a `PushoverValidationResponse` object.
|
||||||
|
* Check the `status` field (1 for valid, 0 for invalid) and `errors` for details on failure.
|
||||||
|
* On success, `devices` and `licenses` may be populated.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* // Validate a user key
|
||||||
|
* const validation = await pushover.validate({ user: "user-key" });
|
||||||
|
* if (validation.status === 1) {
|
||||||
|
* console.log(
|
||||||
|
* "User is valid. Devices:",
|
||||||
|
* validation.devices,
|
||||||
|
* ", Licenses:",
|
||||||
|
* validation.licenses,
|
||||||
|
* );
|
||||||
|
* } else {
|
||||||
|
* console.error("Validation failed:", validation.errors);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // Validate a user and device
|
||||||
|
* const deviceValidation = await pushover.validate({
|
||||||
|
* user: "user-key",
|
||||||
|
* deviceName: "phone",
|
||||||
|
* });
|
||||||
|
* console.log("Device validation status:", deviceValidation.status);
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
validate(options: ValidateOptions): Promise<PushoverValidationResponse> {
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
|
||||||
|
params.append("token", this.token);
|
||||||
|
params.append("user", options.user);
|
||||||
|
if (options.deviceName) params.append("device", options.deviceName);
|
||||||
|
|
||||||
|
return this.makeRequest<PushoverValidationResponse>(
|
||||||
|
"users/validate.json",
|
||||||
|
"POST",
|
||||||
|
params,
|
||||||
|
options.verbose ?? false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks the status of an emergency priority message using its receipt ID.
|
||||||
|
* Allows querying whether the message has been acknowledged, expired, or if the callback was triggered.
|
||||||
|
*
|
||||||
|
* @param receipt - The receipt ID obtained from the `PushoverMessageResponse` when sending an emergency message.
|
||||||
|
* @param verbose - Optional flag for logging.
|
||||||
|
* @returns A Promise resolving to a `PushoverReceiptResponse` object containing the status details.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* const receiptId = "RECEIPT_ID_FROM_SEND_RESPONSE";
|
||||||
|
* const status = await pushover.checkReceipt(receiptId);
|
||||||
|
* if (status.status === 1) {
|
||||||
|
* console.log(
|
||||||
|
* `Acknowledged: ${status.acknowledged} by ${status.acknowledged_by}`,
|
||||||
|
* );
|
||||||
|
* console.log(`Expired: ${status.expired}`);
|
||||||
|
* } else {
|
||||||
|
* console.error("Failed to check receipt:", status.errors);
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
checkReceipt(
|
||||||
|
receipt: string,
|
||||||
|
verbose?: boolean,
|
||||||
|
): Promise<PushoverReceiptResponse> {
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
return this.makeRequest<PushoverReceiptResponse>(
|
||||||
|
`receipts/${receipt}.json?token=${this.token}`,
|
||||||
|
"GET",
|
||||||
|
params,
|
||||||
|
verbose ?? false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancels the retries for an emergency priority message that has not yet been acknowledged.
|
||||||
|
*
|
||||||
|
* @param receipt - The receipt ID of the emergency message whose retries should be cancelled.
|
||||||
|
* @param verbose - Optional flag for logging.
|
||||||
|
* @returns A Promise resolving to a basic `PushoverResponse`. Check `status` for success (1) or failure (0).
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* const receiptId = "RECEIPT_ID_TO_CANCEL";
|
||||||
|
* const cancelResponse = await pushover.cancelRetries(receiptId);
|
||||||
|
* if (cancelResponse.status === 1) {
|
||||||
|
* console.log("Successfully cancelled retries for receipt:", receiptId);
|
||||||
|
* } else {
|
||||||
|
* console.error("Failed to cancel retries:", cancelResponse.errors);
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
cancelRetries(receipt: string, verbose?: boolean): Promise<PushoverResponse> {
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
params.append("token", this.token);
|
||||||
|
return this.makeRequest<PushoverResponse>(
|
||||||
|
`receipts/${receipt}/cancel.json`,
|
||||||
|
"POST",
|
||||||
|
params,
|
||||||
|
verbose ?? false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancels the retries for all emergency priority messages associated with a specific tag
|
||||||
|
* that have not yet been acknowledged.
|
||||||
|
*
|
||||||
|
* @param tag - The tag associated with the emergency messages (set in `emergencyOpts.tags` during send).
|
||||||
|
* @param verbose - Optional flag for logging.
|
||||||
|
* @returns A Promise resolving to a `PushoverTagCancellationResponse` indicating the number of messages cancelled.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* const tagName = "critical-db-alert";
|
||||||
|
* const cancelByTagResponse = await pushover.cancelRetriesByTag(tagName);
|
||||||
|
* if (cancelByTagResponse.status === 1) {
|
||||||
|
* console.log(
|
||||||
|
* `Successfully cancelled ${cancelByTagResponse.canceled} messages with tag: ${tagName}`,
|
||||||
|
* );
|
||||||
|
* } else {
|
||||||
|
* console.error("Failed to cancel by tag:", cancelByTagResponse.errors);
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
cancelRetriesByTag(
|
||||||
|
tag: string,
|
||||||
|
verbose?: boolean,
|
||||||
|
): Promise<PushoverTagCancellationResponse> {
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
params.append("token", this.token);
|
||||||
|
return this.makeRequest<PushoverTagCancellationResponse>(
|
||||||
|
`receipts/cancel_by_tag/${tag}.json`,
|
||||||
|
"POST",
|
||||||
|
params,
|
||||||
|
verbose ?? false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
26
packages/pushover/src/index.ts
Normal file
26
packages/pushover/src/index.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { Pushover } from "./Pushover";
|
||||||
|
|
||||||
|
import type {
|
||||||
|
PushoverRecipient,
|
||||||
|
PushoverMessage,
|
||||||
|
PushoverMessageResponse,
|
||||||
|
PushoverValidationResponse,
|
||||||
|
PushoverReceiptResponse,
|
||||||
|
PushoverTagCancellationResponse,
|
||||||
|
ValidateOptions,
|
||||||
|
SendOptions,
|
||||||
|
} from "./Pushover";
|
||||||
|
|
||||||
|
export default Pushover;
|
||||||
|
export { Pushover };
|
||||||
|
|
||||||
|
export type {
|
||||||
|
PushoverRecipient,
|
||||||
|
PushoverMessage,
|
||||||
|
PushoverMessageResponse,
|
||||||
|
PushoverValidationResponse,
|
||||||
|
PushoverReceiptResponse,
|
||||||
|
PushoverTagCancellationResponse,
|
||||||
|
ValidateOptions,
|
||||||
|
SendOptions,
|
||||||
|
};
|
||||||
11
packages/pushover/tests/index.spec.ts
Normal file
11
packages/pushover/tests/index.spec.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import Pushover from "../src";
|
||||||
|
|
||||||
|
test("Pushover is exported", () => {
|
||||||
|
expect(Pushover).toBeDefined();
|
||||||
|
expect(Pushover).toBeInstanceOf(Object);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Pushover has expected functions", () => {
|
||||||
|
expect(Pushover).toHaveProperty("prototype.constructor");
|
||||||
|
expect(Pushover).toHaveProperty("prototype.send");
|
||||||
|
});
|
||||||
12
packages/pushover/tsconfig.json
Normal file
12
packages/pushover/tsconfig.json
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"extends": "@repo/configs/tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
/* Path Aliases */
|
||||||
|
"baseUrl": "./src",
|
||||||
|
"paths": {
|
||||||
|
"~/*": ["./src/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["src/**/*.ts", "src/**/*.tsx"],
|
||||||
|
"exclude": ["node_modules/**/*", "docs/**/*", "dist/**/*"]
|
||||||
|
}
|
||||||
4909
pnpm-lock.yaml
generated
4909
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
7
pnpm-workspace.yaml
Normal file
7
pnpm-workspace.yaml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
packages:
|
||||||
|
- meta/*
|
||||||
|
- packages/*
|
||||||
|
- config
|
||||||
|
|
||||||
|
|
||||||
|
catalogMode: prefer
|
||||||
111
src/index.ts
111
src/index.ts
@@ -1,111 +0,0 @@
|
|||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
const messageSchema = z.object({
|
|
||||||
/**
|
|
||||||
* The message to send.
|
|
||||||
*/
|
|
||||||
message: z.string(),
|
|
||||||
/**
|
|
||||||
* The title of the message.
|
|
||||||
*/
|
|
||||||
title: z.string().optional(),
|
|
||||||
/**
|
|
||||||
* The URL to open when the notification is clicked.
|
|
||||||
*/
|
|
||||||
url: z
|
|
||||||
.object({
|
|
||||||
/**
|
|
||||||
* The URL itself.
|
|
||||||
*/
|
|
||||||
url: z.string(),
|
|
||||||
/**
|
|
||||||
* The title of the URL to be displayed.
|
|
||||||
*/
|
|
||||||
title: z.string().optional(),
|
|
||||||
})
|
|
||||||
.optional(),
|
|
||||||
priority: z
|
|
||||||
.object({
|
|
||||||
level: z.number().default(0),
|
|
||||||
retry: z.number().optional(),
|
|
||||||
expire: z.number().optional(),
|
|
||||||
callback: z.string().optional(),
|
|
||||||
})
|
|
||||||
.optional(),
|
|
||||||
sound: z.string().optional(),
|
|
||||||
timestamp: z.date().optional(),
|
|
||||||
html: z.boolean().default(false),
|
|
||||||
attachment: z
|
|
||||||
.object({
|
|
||||||
data: z.string().or(z.instanceof(File)),
|
|
||||||
type: z.string(),
|
|
||||||
})
|
|
||||||
.optional(),
|
|
||||||
users: z.array(
|
|
||||||
z.object({
|
|
||||||
name: z.string(),
|
|
||||||
token: z.string(),
|
|
||||||
devices: z.array(z.string()),
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
appToken: z.string(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export type PushoverMessage = z.infer<typeof messageSchema>;
|
|
||||||
|
|
||||||
export type Pushover = {
|
|
||||||
// #################################
|
|
||||||
// # Helpers #
|
|
||||||
// #################################
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get all available sounds.
|
|
||||||
*/
|
|
||||||
getSounds: () => { sounds: string[] };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get all available devices for a user.
|
|
||||||
*/
|
|
||||||
getDevices: (userToken: string) => { devices: string[] };
|
|
||||||
|
|
||||||
// #################################
|
|
||||||
// # Sending #
|
|
||||||
// #################################
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send the notification.
|
|
||||||
*/
|
|
||||||
send: () => void;
|
|
||||||
|
|
||||||
// #################################
|
|
||||||
// # Options #
|
|
||||||
// #################################
|
|
||||||
withMessage: (message: string) => Pushover;
|
|
||||||
withTitle: (title: string) => Pushover;
|
|
||||||
withUrl: (url: { url: string; title: string }) => Pushover;
|
|
||||||
withPriority: (priority: {
|
|
||||||
level: number;
|
|
||||||
retry?: number;
|
|
||||||
expire?: number;
|
|
||||||
callback?: string;
|
|
||||||
}) => Pushover;
|
|
||||||
withSound: (sound: string) => Pushover;
|
|
||||||
withTimestamp: (timestamp: number) => Pushover;
|
|
||||||
withHtml: (enable: boolean) => Pushover;
|
|
||||||
withAttachment: (attachment: File) => Pushover;
|
|
||||||
withBase64Attachment: (attachment: {
|
|
||||||
data: string;
|
|
||||||
type: string;
|
|
||||||
}) => Pushover;
|
|
||||||
addUsers: (users: User[]) => Pushover;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type User = {
|
|
||||||
name: string;
|
|
||||||
token: string;
|
|
||||||
devices: string[];
|
|
||||||
};
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
"this is indented obnoxiously far and has no semi at the end. (testing previous commits",
|
|
||||||
);
|
|
||||||
0
templates/package/README.md
Normal file
0
templates/package/README.md
Normal file
7
templates/package/eslint.config.mjs
Normal file
7
templates/package/eslint.config.mjs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import repoConfig from "@repo/configs/eslint";
|
||||||
|
|
||||||
|
export default [
|
||||||
|
...repoConfig,
|
||||||
|
{ ignores: ["dist/**", "docs/**"] },
|
||||||
|
{ files: ["**/*.{ts}"] },
|
||||||
|
];
|
||||||
3
templates/package/jest.config.js
Normal file
3
templates/package/jest.config.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import repoCofig from "@repo/configs/jest";
|
||||||
|
|
||||||
|
export default [...repoCofig];
|
||||||
87
templates/package/package.json
Normal file
87
templates/package/package.json
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
{
|
||||||
|
"name": "{{name}}",
|
||||||
|
"description": "{{description}}",
|
||||||
|
"homepage": "https://cis-oss.github.io/notify",
|
||||||
|
"version": "0.0.10",
|
||||||
|
"main": "src/index.ts",
|
||||||
|
"author": {
|
||||||
|
"email": "hi@b00tload.space",
|
||||||
|
"name": "Alix von Schirp",
|
||||||
|
"url": "https://b00tload.space"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/cis-oss/pushover.git"
|
||||||
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/cis-oss/pushover/issues"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"notification",
|
||||||
|
"notify"
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"zod": "^3.24.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@eslint/js": "^9.22.0",
|
||||||
|
"@repo/configs": "workspace:",
|
||||||
|
"@shipgirl/typedoc-plugin-versions": "^0.3.0",
|
||||||
|
"@types/jest": "^29.5.14",
|
||||||
|
"@types/node": "^22.13.10",
|
||||||
|
"eslint": "^9.22.0",
|
||||||
|
"eslint-config-prettier": "^10.1.1",
|
||||||
|
"eslint-plugin-only-warn": "^1.1.0",
|
||||||
|
"globals": "^16.0.0",
|
||||||
|
"jest": "^29.7.0",
|
||||||
|
"shx": "^0.4.0",
|
||||||
|
"ts-jest": "^29.2.6",
|
||||||
|
"tsx": "^4.19.3",
|
||||||
|
"typedoc": "^0.27.9",
|
||||||
|
"typedoc-github-theme": "^0.2.1",
|
||||||
|
"typedoc-plugin-coverage": "^3.4.1",
|
||||||
|
"typedoc-plugin-extras": "^4.0.0",
|
||||||
|
"typedoc-plugin-include-example": "^2.0.2",
|
||||||
|
"typedoc-plugin-inline-sources": "^1.2.1",
|
||||||
|
"typedoc-plugin-mdn-links": "^5.0.1",
|
||||||
|
"typedoc-plugin-zod": "^1.4.0",
|
||||||
|
"typescript": "^5.5.3",
|
||||||
|
"typescript-eslint": "^8.26.0"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist/**/*.{js,ts,map}"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc",
|
||||||
|
"build:watch": "tsc --watch",
|
||||||
|
"check": "pnpm run lint:ci && pnpm run typecheck",
|
||||||
|
"clean": "shx rm -rf dist/",
|
||||||
|
"docs:generate": "typedoc",
|
||||||
|
"format:check": "prettier --check .",
|
||||||
|
"lint": "eslint .",
|
||||||
|
"lint:ci": "eslint --max-warnings 0",
|
||||||
|
"prepublishOnly": "pnpm run clean && pnpm run build",
|
||||||
|
"test": "jest",
|
||||||
|
"typecheck": "tsc --noEmit"
|
||||||
|
},
|
||||||
|
"private": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"maintainers": [
|
||||||
|
{
|
||||||
|
"name": "Alix von Schirp",
|
||||||
|
"email": "hi@b00tload.space",
|
||||||
|
"url": "https://b00tload.space"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Ole",
|
||||||
|
"email": "jateute123@gmail.com",
|
||||||
|
"url": "https://github.com/jateute"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"packageManager": "pnpm@10.6.5",
|
||||||
|
"publishConfig": {
|
||||||
|
"main": "dist/index.js",
|
||||||
|
"types": "dist/index.d.ts"
|
||||||
|
},
|
||||||
|
"type": "module"
|
||||||
|
}
|
||||||
0
templates/package/src/index.ts
Normal file
0
templates/package/src/index.ts
Normal file
12
templates/package/tsconfig.json
Normal file
12
templates/package/tsconfig.json
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"extends": "@repo/configs/tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
/* Path Aliases */
|
||||||
|
"baseUrl": "./src",
|
||||||
|
"paths": {
|
||||||
|
"~/*": ["./src/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["src/**/*.ts", "src/**/*.tsx"],
|
||||||
|
"exclude": ["node_modules/**/*", "docs/**/*", "dist/**/*"]
|
||||||
|
}
|
||||||
46
turbo/generators/config.ts
Normal file
46
turbo/generators/config.ts
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import type { PlopTypes } from "@turbo/gen";
|
||||||
|
|
||||||
|
export default function generator(plop: PlopTypes.NodePlopAPI): void {
|
||||||
|
// create a generator
|
||||||
|
plop.setGenerator("package", {
|
||||||
|
description: "Generator description",
|
||||||
|
// gather information from the user
|
||||||
|
prompts: [
|
||||||
|
{
|
||||||
|
type: "input",
|
||||||
|
name: "name",
|
||||||
|
message: "Package name",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "input",
|
||||||
|
name: "description",
|
||||||
|
message: "Package description",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "input",
|
||||||
|
name: "author.name",
|
||||||
|
message: "Author name",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "input",
|
||||||
|
name: "author.email",
|
||||||
|
message: "Author email",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "input",
|
||||||
|
name: "author.url",
|
||||||
|
message: "Author URL",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
// perform actions based on the prompts
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
type: "addMany",
|
||||||
|
destination: "packages/{{name}}",
|
||||||
|
base: "templates/package",
|
||||||
|
templateFiles: "templates/package/**/*",
|
||||||
|
abortOnFail: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
// @ts-nocheck
|
|
||||||
/** @type {import("typedoc").TypeDocOptions &
|
|
||||||
* import("typedoc-plugin-extras").ExtrasOptions &
|
|
||||||
* import("typedoc-plugin-coverage").CoverageOptions &
|
|
||||||
* import("typedoc-plugin-mdn-links").MdnLinksOptions &
|
|
||||||
* import("typedoc-plugin-zod").ZodOptions} */
|
|
||||||
const config = {
|
|
||||||
compilerOptions: {
|
|
||||||
skipLibCheck: true,
|
|
||||||
strict: false,
|
|
||||||
},
|
|
||||||
entryPoints: ["src/index.ts"],
|
|
||||||
out: "docs",
|
|
||||||
plugin: [
|
|
||||||
"typedoc-plugin-coverage",
|
|
||||||
"typedoc-plugin-extras",
|
|
||||||
"typedoc-plugin-inline-sources",
|
|
||||||
"typedoc-plugin-mdn-links",
|
|
||||||
"typedoc-plugin-zod",
|
|
||||||
"typedoc-plugin-include-example",
|
|
||||||
"@shipgirl/typedoc-plugin-versions",
|
|
||||||
],
|
|
||||||
footerTypedocVersion: true,
|
|
||||||
footerLastModified: true,
|
|
||||||
coverageLabel: "Docs Coverage",
|
|
||||||
coverageOutputType: "all",
|
|
||||||
coverageSvgWidth: 130,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default config;
|
|
||||||
Reference in New Issue
Block a user