@book000/node-utils 1.2.28 → 1.2.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintignore +5 -0
- package/.eslintrc.yml +21 -0
- package/.github/FUNDING.yml +1 -0
- package/.github/workflows/nodejs-ci.yml +18 -0
- package/.github/workflows/release.yml +63 -0
- package/.gitignore +130 -0
- package/.node-version +1 -0
- package/.prettierrc.yml +12 -0
- package/package.json +3 -2
- package/renovate.json +12 -0
- package/src/configuration.ts +111 -0
- package/src/cycle.d.ts +4 -0
- package/src/discord.ts +219 -0
- package/src/examples/example-configuration.ts +33 -0
- package/src/examples/example-discord.ts +28 -0
- package/src/examples/example-logger.ts +6 -0
- package/src/examples/main.ts +38 -0
- package/src/index.ts +6 -0
- package/src/logger.ts +168 -0
- package/tsconfig.json +32 -0
- package/yarn.lock +3323 -0
package/.eslintrc.yml
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
env:
|
|
2
|
+
es2020: true
|
|
3
|
+
node: true
|
|
4
|
+
extends:
|
|
5
|
+
- standard
|
|
6
|
+
- plugin:@typescript-eslint/recommended
|
|
7
|
+
- plugin:unicorn/recommended
|
|
8
|
+
- prettier
|
|
9
|
+
parser: '@typescript-eslint/parser'
|
|
10
|
+
parserOptions:
|
|
11
|
+
ecmaVersion: latest
|
|
12
|
+
sourceType: module
|
|
13
|
+
project: ./tsconfig.json
|
|
14
|
+
plugins:
|
|
15
|
+
- '@typescript-eslint'
|
|
16
|
+
rules:
|
|
17
|
+
'@typescript-eslint/no-explicit-any': off
|
|
18
|
+
'@typescript-eslint/ban-ts-comment': off
|
|
19
|
+
'unicorn/prefer-top-level-await': off
|
|
20
|
+
'unicorn/no-null': off
|
|
21
|
+
'no-console': error
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
github: book000
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Node.js でビルド・テストを実行する。バージョンは .node-version に記載されているものを利用する
|
|
2
|
+
|
|
3
|
+
name: Node CI
|
|
4
|
+
|
|
5
|
+
on:
|
|
6
|
+
push:
|
|
7
|
+
branches:
|
|
8
|
+
- main
|
|
9
|
+
- master
|
|
10
|
+
pull_request:
|
|
11
|
+
branches:
|
|
12
|
+
- main
|
|
13
|
+
- master
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
node-ci:
|
|
17
|
+
name: Node CI
|
|
18
|
+
uses: book000/templates/.github/workflows/reusable-nodejs-ci.yml@master
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request_target:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
- master
|
|
8
|
+
types:
|
|
9
|
+
- closed
|
|
10
|
+
|
|
11
|
+
concurrency:
|
|
12
|
+
group: ${{ github.workflow }}
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
release:
|
|
16
|
+
name: Release
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
if: github.event.pull_request.merged == true
|
|
19
|
+
|
|
20
|
+
steps:
|
|
21
|
+
- name: 🛎 Checkout
|
|
22
|
+
uses: actions/checkout@v3
|
|
23
|
+
with:
|
|
24
|
+
fetch-depth: 0
|
|
25
|
+
|
|
26
|
+
- name: 🏗 Setup node
|
|
27
|
+
uses: actions/setup-node@v3
|
|
28
|
+
with:
|
|
29
|
+
node-version-file: .node-version
|
|
30
|
+
cache: yarn
|
|
31
|
+
cache-dependency-path: yarn.lock
|
|
32
|
+
registry-url: 'https://registry.npmjs.org'
|
|
33
|
+
|
|
34
|
+
- name: 👨🏻💻 Install dependencies
|
|
35
|
+
run: yarn install --frozen-lockfile
|
|
36
|
+
|
|
37
|
+
- name: 🏃 Build
|
|
38
|
+
run: yarn build
|
|
39
|
+
|
|
40
|
+
- name: 🏷 Bump version and push tag
|
|
41
|
+
id: tag-version
|
|
42
|
+
uses: mathieudutour/github-tag-action@v6.1
|
|
43
|
+
with:
|
|
44
|
+
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
45
|
+
default_bump: 'minor'
|
|
46
|
+
custom_release_rules: 'feat:minor:✨ Features,fix:patch:🐛 Fixes,docs:patch:📰 Docs,chore:patch:🎨 Chore,pref:patch:🎈 Performance improvements,refactor:patch:🧹 Refactoring,build:patch:🔍 Build,ci:patch:🔍 CI,revert:patch:⏪ Revert,style:patch:🧹 Style,test:patch:👀 Test,release:major:📦 Release'
|
|
47
|
+
|
|
48
|
+
- name: 📦 Publish
|
|
49
|
+
run: yarn publish --access public --non-interactive --no-git-tag-version --no-git-reset --no-commit-hooks --new-version ${{ steps.tag-version.outputs.new_version }}
|
|
50
|
+
env:
|
|
51
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
52
|
+
|
|
53
|
+
- name: 📃 Create Release
|
|
54
|
+
id: create_release
|
|
55
|
+
uses: actions/create-release@v1
|
|
56
|
+
env:
|
|
57
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
58
|
+
with:
|
|
59
|
+
tag_name: ${{ steps.tag-version.outputs.new_tag }}
|
|
60
|
+
release_name: ${{ steps.tag-version.outputs.new_tag }}
|
|
61
|
+
body: ${{ steps.tag-version.outputs.changelog }}
|
|
62
|
+
draft: false
|
|
63
|
+
prerelease: false
|
package/.gitignore
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# Logs
|
|
2
|
+
logs
|
|
3
|
+
*.log
|
|
4
|
+
npm-debug.log*
|
|
5
|
+
yarn-debug.log*
|
|
6
|
+
yarn-error.log*
|
|
7
|
+
lerna-debug.log*
|
|
8
|
+
.pnpm-debug.log*
|
|
9
|
+
|
|
10
|
+
# Diagnostic reports (https://nodejs.org/api/report.html)
|
|
11
|
+
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
|
12
|
+
|
|
13
|
+
# Runtime data
|
|
14
|
+
pids
|
|
15
|
+
*.pid
|
|
16
|
+
*.seed
|
|
17
|
+
*.pid.lock
|
|
18
|
+
|
|
19
|
+
# Directory for instrumented libs generated by jscoverage/JSCover
|
|
20
|
+
lib-cov
|
|
21
|
+
|
|
22
|
+
# Coverage directory used by tools like istanbul
|
|
23
|
+
coverage
|
|
24
|
+
*.lcov
|
|
25
|
+
|
|
26
|
+
# nyc test coverage
|
|
27
|
+
.nyc_output
|
|
28
|
+
|
|
29
|
+
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
|
30
|
+
.grunt
|
|
31
|
+
|
|
32
|
+
# Bower dependency directory (https://bower.io/)
|
|
33
|
+
bower_components
|
|
34
|
+
|
|
35
|
+
# node-waf configuration
|
|
36
|
+
.lock-wscript
|
|
37
|
+
|
|
38
|
+
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
|
39
|
+
build/Release
|
|
40
|
+
|
|
41
|
+
# Dependency directories
|
|
42
|
+
node_modules/
|
|
43
|
+
jspm_packages/
|
|
44
|
+
|
|
45
|
+
# Snowpack dependency directory (https://snowpack.dev/)
|
|
46
|
+
web_modules/
|
|
47
|
+
|
|
48
|
+
# TypeScript cache
|
|
49
|
+
*.tsbuildinfo
|
|
50
|
+
|
|
51
|
+
# Optional npm cache directory
|
|
52
|
+
.npm
|
|
53
|
+
|
|
54
|
+
# Optional eslint cache
|
|
55
|
+
.eslintcache
|
|
56
|
+
|
|
57
|
+
# Optional stylelint cache
|
|
58
|
+
.stylelintcache
|
|
59
|
+
|
|
60
|
+
# Microbundle cache
|
|
61
|
+
.rpt2_cache/
|
|
62
|
+
.rts2_cache_cjs/
|
|
63
|
+
.rts2_cache_es/
|
|
64
|
+
.rts2_cache_umd/
|
|
65
|
+
|
|
66
|
+
# Optional REPL history
|
|
67
|
+
.node_repl_history
|
|
68
|
+
|
|
69
|
+
# Output of 'npm pack'
|
|
70
|
+
*.tgz
|
|
71
|
+
|
|
72
|
+
# Yarn Integrity file
|
|
73
|
+
.yarn-integrity
|
|
74
|
+
|
|
75
|
+
# dotenv environment variable files
|
|
76
|
+
.env
|
|
77
|
+
.env.development.local
|
|
78
|
+
.env.test.local
|
|
79
|
+
.env.production.local
|
|
80
|
+
.env.local
|
|
81
|
+
|
|
82
|
+
# parcel-bundler cache (https://parceljs.org/)
|
|
83
|
+
.cache
|
|
84
|
+
.parcel-cache
|
|
85
|
+
|
|
86
|
+
# Next.js build output
|
|
87
|
+
.next
|
|
88
|
+
out
|
|
89
|
+
|
|
90
|
+
# Nuxt.js build / generate output
|
|
91
|
+
.nuxt
|
|
92
|
+
dist
|
|
93
|
+
|
|
94
|
+
# Gatsby files
|
|
95
|
+
.cache/
|
|
96
|
+
# Comment in the public line in if your project uses Gatsby and not Next.js
|
|
97
|
+
# https://nextjs.org/blog/next-9-1#public-directory-support
|
|
98
|
+
# public
|
|
99
|
+
|
|
100
|
+
# vuepress build output
|
|
101
|
+
.vuepress/dist
|
|
102
|
+
|
|
103
|
+
# vuepress v2.x temp and cache directory
|
|
104
|
+
.temp
|
|
105
|
+
.cache
|
|
106
|
+
|
|
107
|
+
# Docusaurus cache and generated files
|
|
108
|
+
.docusaurus
|
|
109
|
+
|
|
110
|
+
# Serverless directories
|
|
111
|
+
.serverless/
|
|
112
|
+
|
|
113
|
+
# FuseBox cache
|
|
114
|
+
.fusebox/
|
|
115
|
+
|
|
116
|
+
# DynamoDB Local files
|
|
117
|
+
.dynamodb/
|
|
118
|
+
|
|
119
|
+
# TernJS port file
|
|
120
|
+
.tern-port
|
|
121
|
+
|
|
122
|
+
# Stores VSCode versions used for testing VSCode extensions
|
|
123
|
+
.vscode-test
|
|
124
|
+
|
|
125
|
+
# yarn v2
|
|
126
|
+
.yarn/cache
|
|
127
|
+
.yarn/unplugged
|
|
128
|
+
.yarn/build-state.yml
|
|
129
|
+
.yarn/install-state.gz
|
|
130
|
+
.pnp.*
|
package/.node-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
18.16.0
|
package/.prettierrc.yml
ADDED
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@book000/node-utils",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.29",
|
|
4
4
|
"description": "Self-Utility library",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"files": [
|
|
8
|
-
"dist/"
|
|
8
|
+
"dist/",
|
|
9
|
+
"!dist/examples/"
|
|
9
10
|
],
|
|
10
11
|
"repository": "git@github.com:book000/node-utils.git",
|
|
11
12
|
"author": "Tomachi <tomachi@tomacheese.com>",
|
package/renovate.json
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import { parse } from 'jsonc-parser'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 設定ファイルを管理するフレームワーククラス
|
|
6
|
+
*
|
|
7
|
+
* 設定ファイルは JSONC 形式でパースされる。
|
|
8
|
+
*
|
|
9
|
+
* @template IConfig 設定ファイルの型
|
|
10
|
+
*/
|
|
11
|
+
export abstract class ConfigFramework<IConfig> {
|
|
12
|
+
/** 設定ファイルのパス */
|
|
13
|
+
private path: string
|
|
14
|
+
private config: IConfig | undefined
|
|
15
|
+
private validateFailures: string[] = []
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* コンストラクタ
|
|
19
|
+
*
|
|
20
|
+
* ファイルの読み込みは行わない。{@link load} を呼び出すことで読み込む。
|
|
21
|
+
*
|
|
22
|
+
* 原則、以下の環境変数を利用する。値の場所にファイルがあればそれを利用する。
|
|
23
|
+
* - CONFIG_PATH
|
|
24
|
+
* - CONFIG_FILE
|
|
25
|
+
* 環境変数に値が設定されておらず、設定ファイルのパスが path に指定されていてそのファイルがある場合はそのファイルを設定ファイルとして使用する
|
|
26
|
+
* いずれの方法でもパスを取得できない場合はエラーを投げる
|
|
27
|
+
*
|
|
28
|
+
* @param path 設定ファイルのパス
|
|
29
|
+
* @returns インスタンス
|
|
30
|
+
*/
|
|
31
|
+
constructor(path?: string) {
|
|
32
|
+
const paths = [
|
|
33
|
+
process.env.CONFIG_PATH,
|
|
34
|
+
process.env.CONFIG_FILE,
|
|
35
|
+
path,
|
|
36
|
+
].filter((p) => p !== undefined && fs.existsSync(p)) as string[]
|
|
37
|
+
if (paths.length === 0) {
|
|
38
|
+
throw new Error('Config path not found')
|
|
39
|
+
}
|
|
40
|
+
this.path = paths[0]
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 設定ファイルの検証ルールを定義する
|
|
45
|
+
*
|
|
46
|
+
* key には検証ルールの名前を指定する。
|
|
47
|
+
* value には検証ルールを定義する関数を指定する。
|
|
48
|
+
*
|
|
49
|
+
* @returns 検証ルール
|
|
50
|
+
*/
|
|
51
|
+
protected abstract validates(): {
|
|
52
|
+
[key: string]: (config: IConfig) => boolean
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 設定ファイルを読み込む
|
|
57
|
+
*
|
|
58
|
+
* 設定ファイルのパスはコンストラクタで指定されたものが利用される。
|
|
59
|
+
* この関数内ではバリデーションは行わないので、{@link validate} を呼び出す必要がある。
|
|
60
|
+
*/
|
|
61
|
+
public load(): void {
|
|
62
|
+
const data = fs.readFileSync(this.path, 'utf8')
|
|
63
|
+
const json = parse(data) as IConfig
|
|
64
|
+
this.config = json
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 設定ファイルのバリデーションを行う
|
|
69
|
+
*
|
|
70
|
+
* この関数を呼び出す前に、{@link load} を呼び出して設定ファイルを読み込んでおく必要がある。
|
|
71
|
+
* バリデーションに失敗した場合は、{@link getValidateFailures} で失敗した項目を取得できる。
|
|
72
|
+
*
|
|
73
|
+
* @returns バリデーションに成功した場合は true、失敗した場合は false
|
|
74
|
+
*/
|
|
75
|
+
public validate(): boolean {
|
|
76
|
+
if (!this.config) throw new Error('Config not loaded')
|
|
77
|
+
|
|
78
|
+
this.validateFailures = []
|
|
79
|
+
const validates = this.validates()
|
|
80
|
+
for (const key in validates) {
|
|
81
|
+
try {
|
|
82
|
+
if (!validates[key](this.config)) {
|
|
83
|
+
this.validateFailures.push(key)
|
|
84
|
+
}
|
|
85
|
+
} catch {
|
|
86
|
+
this.validateFailures.push(key)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return this.validateFailures.length === 0
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* バリデーションに失敗した項目を取得する
|
|
94
|
+
*
|
|
95
|
+
* @returns バリデーションに失敗した項目の配列
|
|
96
|
+
*/
|
|
97
|
+
public getValidateFailures(): string[] {
|
|
98
|
+
return this.validateFailures
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* 設定の値を取得する
|
|
103
|
+
*
|
|
104
|
+
* @param key 設定のキー
|
|
105
|
+
* @returns 設定の値
|
|
106
|
+
*/
|
|
107
|
+
public get<T extends keyof IConfig>(key: T): IConfig[T] {
|
|
108
|
+
if (!this.config) throw new Error('Config not loaded')
|
|
109
|
+
return this.config[key]
|
|
110
|
+
}
|
|
111
|
+
}
|
package/src/cycle.d.ts
ADDED
package/src/discord.ts
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import axios from 'axios'
|
|
2
|
+
import FormData from 'form-data'
|
|
3
|
+
|
|
4
|
+
interface DiscordBotOptions {
|
|
5
|
+
token: string
|
|
6
|
+
channelId: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface DiscordWebhookOptions {
|
|
10
|
+
webhookUrl: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type DiscordOptions = DiscordBotOptions | DiscordWebhookOptions
|
|
14
|
+
export interface DiscordEmbedFooter {
|
|
15
|
+
text: string
|
|
16
|
+
icon_url?: string
|
|
17
|
+
proxy_icon_url?: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface DiscordEmbedImage {
|
|
21
|
+
url?: string
|
|
22
|
+
proxy_url?: string
|
|
23
|
+
height?: number
|
|
24
|
+
width?: number
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface DiscordEmbedThumbnail {
|
|
28
|
+
url?: string
|
|
29
|
+
proxy_url?: string
|
|
30
|
+
height?: number
|
|
31
|
+
width?: number
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface DiscordEmbedVideo {
|
|
35
|
+
url?: string
|
|
36
|
+
proxy_url?: string
|
|
37
|
+
height?: number
|
|
38
|
+
width?: number
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface DiscordEmbedProvider {
|
|
42
|
+
name?: string
|
|
43
|
+
url?: string
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface DiscordEmbedAuthor {
|
|
47
|
+
name?: string
|
|
48
|
+
url?: string
|
|
49
|
+
icon_url?: string
|
|
50
|
+
proxy_icon_url?: string
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface DiscordEmbedField {
|
|
54
|
+
name: string
|
|
55
|
+
value: string
|
|
56
|
+
inline?: boolean
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface DiscordEmbed {
|
|
60
|
+
title?: string
|
|
61
|
+
type?: 'rich' | 'image' | 'video' | 'gifv' | 'article' | 'link'
|
|
62
|
+
description?: string
|
|
63
|
+
url?: string
|
|
64
|
+
timestamp?: string
|
|
65
|
+
color?: number
|
|
66
|
+
footer?: DiscordEmbedFooter
|
|
67
|
+
image?: DiscordEmbedImage
|
|
68
|
+
thumbnail?: DiscordEmbedThumbnail
|
|
69
|
+
video?: DiscordEmbedVideo
|
|
70
|
+
provider?: DiscordEmbedProvider
|
|
71
|
+
author?: DiscordEmbedAuthor
|
|
72
|
+
fields?: DiscordEmbedField[]
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
interface DiscordNormalMessage {
|
|
76
|
+
content: string
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
interface DiscordEmbedMessage {
|
|
80
|
+
embeds: DiscordEmbed[]
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
interface DiscordFile {
|
|
84
|
+
name: string
|
|
85
|
+
file: ArrayBuffer
|
|
86
|
+
contentType?: string
|
|
87
|
+
isSpoiler?: boolean
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
interface DiscordFileMessage {
|
|
91
|
+
file: DiscordFile
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export type DiscordMessage =
|
|
95
|
+
| DiscordNormalMessage
|
|
96
|
+
| DiscordEmbedMessage
|
|
97
|
+
| DiscordFileMessage
|
|
98
|
+
|
|
99
|
+
export class Discord {
|
|
100
|
+
private options: DiscordOptions
|
|
101
|
+
|
|
102
|
+
constructor(options: DiscordOptions) {
|
|
103
|
+
// token があれば Bot として動作する
|
|
104
|
+
// webhookUrl と channelId があれば Webhook として動作する
|
|
105
|
+
// どちらもなければエラーを投げる
|
|
106
|
+
|
|
107
|
+
if (this.isDiscordBotOptions(options)) {
|
|
108
|
+
this.options = options
|
|
109
|
+
} else if (this.isDiscordWebhookOptions(options)) {
|
|
110
|
+
this.options = options
|
|
111
|
+
} else {
|
|
112
|
+
throw new Error('Invalid options')
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
public static get validations(): {
|
|
117
|
+
[key: string]: (options: any) => boolean
|
|
118
|
+
} {
|
|
119
|
+
return {
|
|
120
|
+
'token or webhookUrl and channelId': (options: any) =>
|
|
121
|
+
'token' in options ||
|
|
122
|
+
('webhookUrl' in options && 'channelId' in options),
|
|
123
|
+
'token is valid': (options: any) => typeof options.token === 'string',
|
|
124
|
+
'webhookUrl is valid': (options: any) =>
|
|
125
|
+
typeof options.webhookUrl === 'string',
|
|
126
|
+
'channelId is valid': (options: any) =>
|
|
127
|
+
typeof options.channelId === 'string',
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
public async sendMessage(message: string | DiscordMessage): Promise<void> {
|
|
132
|
+
const formData = new FormData()
|
|
133
|
+
|
|
134
|
+
if (typeof message === 'string') {
|
|
135
|
+
formData.append('payload_json', JSON.stringify({ content: message }))
|
|
136
|
+
} else {
|
|
137
|
+
formData.append(
|
|
138
|
+
'payload_json',
|
|
139
|
+
JSON.stringify({
|
|
140
|
+
content: 'content' in message ? message.content : undefined,
|
|
141
|
+
embeds: 'embeds' in message ? message.embeds : undefined,
|
|
142
|
+
})
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
if ('file' in message) {
|
|
146
|
+
formData.append('file', message.file.file, {
|
|
147
|
+
filename: `${message.file.isSpoiler === true ? 'SPOILER_' : ''}${
|
|
148
|
+
message.file.name
|
|
149
|
+
}`,
|
|
150
|
+
contentType: message.file.contentType,
|
|
151
|
+
})
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
await (this.isDiscordBotOptions(this.options)
|
|
156
|
+
? this.sendBot(formData)
|
|
157
|
+
: this.sendWebhook(formData))
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
private async sendBot(formData: FormData): Promise<void> {
|
|
161
|
+
if (!this.isDiscordBotOptions(this.options)) {
|
|
162
|
+
throw new Error('Invalid bot options')
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const response = await axios.post(
|
|
166
|
+
`https://discord.com/api/channels/${this.options}/messages`,
|
|
167
|
+
formData,
|
|
168
|
+
{
|
|
169
|
+
headers: {
|
|
170
|
+
...formData.getHeaders(),
|
|
171
|
+
Authorization: `Bot ${this.options.token}`,
|
|
172
|
+
},
|
|
173
|
+
validateStatus: () => true,
|
|
174
|
+
}
|
|
175
|
+
)
|
|
176
|
+
if (response.status !== 200) {
|
|
177
|
+
throw new Error(`Discord API returned ${response.status}`)
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
private async sendWebhook(formData: FormData): Promise<void> {
|
|
182
|
+
if (!this.isDiscordWebhookOptions(this.options)) {
|
|
183
|
+
throw new Error('Invalid webhook options')
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const response = await axios.post(this.options.webhookUrl, formData, {
|
|
187
|
+
headers: {
|
|
188
|
+
...formData.getHeaders(),
|
|
189
|
+
},
|
|
190
|
+
validateStatus: () => true,
|
|
191
|
+
})
|
|
192
|
+
if (response.status !== 200 && response.status !== 204) {
|
|
193
|
+
throw new Error(`Discord API returned ${response.status}`)
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
private isDiscordBotOptions(
|
|
198
|
+
options: DiscordOptions
|
|
199
|
+
): options is DiscordBotOptions {
|
|
200
|
+
return (
|
|
201
|
+
'token' in options &&
|
|
202
|
+
typeof options.token === 'string' &&
|
|
203
|
+
options.token.length > 0 &&
|
|
204
|
+
'channelId' in options &&
|
|
205
|
+
typeof options.channelId === 'string' &&
|
|
206
|
+
options.channelId.length > 0
|
|
207
|
+
)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
private isDiscordWebhookOptions(
|
|
211
|
+
options: DiscordOptions
|
|
212
|
+
): options is DiscordWebhookOptions {
|
|
213
|
+
return (
|
|
214
|
+
'webhookUrl' in options &&
|
|
215
|
+
typeof options.webhookUrl === 'string' &&
|
|
216
|
+
options.webhookUrl.length > 0
|
|
217
|
+
)
|
|
218
|
+
}
|
|
219
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { ConfigFramework, Logger } from '..'
|
|
2
|
+
|
|
3
|
+
export interface Configuration {
|
|
4
|
+
foo: string
|
|
5
|
+
bar: number
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
class ExampleConfiguration extends ConfigFramework<Configuration> {
|
|
9
|
+
protected validates(): { [key: string]: (config: Configuration) => boolean } {
|
|
10
|
+
return {
|
|
11
|
+
// ...Discord.validations, // When using a message transmission to Discord
|
|
12
|
+
'foo is required': (config) => config.foo !== undefined,
|
|
13
|
+
'foo is string': (config) => typeof config.foo === 'string',
|
|
14
|
+
'foo is 3 or more characters': (config) => config.foo.length >= 3,
|
|
15
|
+
'bar is required': (config) => config.bar !== undefined,
|
|
16
|
+
'bar is number': (config) => typeof config.bar === 'number',
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function exampleConfiguration() {
|
|
22
|
+
const logger = Logger.configure('exampleConfiguration')
|
|
23
|
+
const config = new ExampleConfiguration()
|
|
24
|
+
config.load()
|
|
25
|
+
if (!config.validate()) {
|
|
26
|
+
logger.error('Configuration validation failed')
|
|
27
|
+
logger.error(config.getValidateFailures().join(', '))
|
|
28
|
+
return
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
logger.info(`foo: ${config.get('foo')}`)
|
|
32
|
+
logger.info(`bar: ${config.get('bar')}`)
|
|
33
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Discord, Logger } from '..'
|
|
2
|
+
|
|
3
|
+
export async function exampleDiscord() {
|
|
4
|
+
const logger = Logger.configure('exampleDiscord')
|
|
5
|
+
|
|
6
|
+
const discordWebhookUrl = process.env.DISCORD_WEBHOOK_URL
|
|
7
|
+
if (!discordWebhookUrl) {
|
|
8
|
+
logger.error('DISCORD_WEBHOOK_URL are required')
|
|
9
|
+
return
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const discord = new Discord({
|
|
13
|
+
webhookUrl: discordWebhookUrl,
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
await discord.sendMessage('Hello world!')
|
|
17
|
+
|
|
18
|
+
await discord.sendMessage({
|
|
19
|
+
embeds: [
|
|
20
|
+
{
|
|
21
|
+
title: 'Hello world!',
|
|
22
|
+
description:
|
|
23
|
+
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',
|
|
24
|
+
color: 0x00_ff_00,
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
})
|
|
28
|
+
}
|