Merge remote-tracking branch 'cffnpwr/nodev18' into dev

This commit is contained in:
nullnyat 2023-02-15 21:25:15 +09:00
commit d0fc7f7f0b
26 changed files with 4808 additions and 130 deletions

2
.npmrc
View File

@ -1,2 +0,0 @@
save-exact = true
package-lock = false

6
.swcrc
View File

@ -11,10 +11,14 @@
"transform": { "transform": {
"legacyDecorator": false, "legacyDecorator": false,
"decoratorMetadata": false "decoratorMetadata": false
},
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
} }
}, },
"module": { "module": {
"type": "commonjs", "type": "commonjs",
"noInterop": true "noInterop": false
} }
} }

View File

@ -1,19 +1,19 @@
FROM node:16 AS module FROM node:18 AS module
WORKDIR /app WORKDIR /app
RUN apt-get update RUN apt-get update
RUN apt-get install -y build-essential libcairo2-dev libpango1.0-dev libjpeg-dev libgif-dev librsvg2-dev RUN apt-get install -y build-essential libcairo2-dev libpango1.0-dev libjpeg-dev libgif-dev librsvg2-dev
COPY package.json . COPY package.json ./
RUN corepack enable RUN corepack enable
RUN pnpm install --prod RUN pnpm i --frozen-lockfile --prod
FROM module AS build FROM module AS build
RUN pnpm install RUN pnpm i --frozen-lockfile
COPY . . COPY . .
@ -37,7 +37,7 @@ RUN apk add tini-static
RUN mv /sbin/tini-static /tini RUN mv /sbin/tini-static /tini
FROM node:16-slim FROM node:18-slim
ENV NODE_ENV="production" ENV NODE_ENV="production"

View File

@ -1,5 +1,7 @@
## これってなに? ## これってなに?
Misskey用の[Aiベース](https://github.com/syuilo/ai)のBotです。 Misskey用の[Aiベース](https://github.com/syuilo/ai)のBotです。
``` ```
_ __ ____ __ ________ __ _ __ ____ __ ________ __
/ | / /_ __/ / /________ _/ /_/ ____/ /_ ____ _____ / / / | / /_ __/ / /________ _/ /_/ ____/ /_ ____ _____ / /
@ -9,6 +11,7 @@ Misskey用の[Aiベース](https://github.com/syuilo/ai)のBotです。
``` ```
## 大きな変更点 ## 大きな変更点
- 自動投稿の内容 - 自動投稿の内容
- 自動返信の内容 - 自動返信の内容
- ゴママヨに反応 - ゴママヨに反応
@ -23,10 +26,21 @@ Misskey用の[Aiベース](https://github.com/syuilo/ai)のBotです。
- アニメ予想機能 - アニメ予想機能
## 導入方法 ## 導入方法
> Node.js と pnpm と MeCab がインストールされている必要があります。
まず適当なディレクトリに `git clone` します。 ### Dockerを使わずに動かす
次にそのディレクトリに `config.json` を作成します。中身は次のようにします:
#### 必要要件
- [Node.js](https://nodejs.org/) v18
- [pnpm](https://pnpm.io/) v7
- [MeCab](https://taku910.github.io/mecab/)
#### インストール
まず適当なディレクトリにこのリポジトリをクローンします。
次にそのディレクトリに`config.json`を作成します。中身は次のようにします。
``` json ``` json
{ {
"host": "https:// + あなたのインスタンスのURL (末尾の / は除く)", "host": "https:// + あなたのインスタンスのURL (末尾の / は除く)",
@ -48,12 +62,22 @@ Misskey用の[Aiベース](https://github.com/syuilo/ai)のBotです。
"ti": "ちの意味をを表す絵文字を入れる(デフォルトだと:_ti:)" "ti": "ちの意味をを表す絵文字を入れる(デフォルトだと:_ti:)"
} }
``` ```
`pnpm install` して `pnpm run build` して `pnpm start` すれば起動できます。 `pnpm fetch`して`pnpm build`して`pnpm start`すれば起動できます。
### Dockerで動かす ### Dockerで動かす
まず適当なディレクトリに `git clone` します。<br>
次にそのディレクトリに `config.json` を作成します。中身は次のようにします: #### 必要要件
- [Docker](https://www.docker.com/)
- [Docker Compose](https://docs.docker.com/compose/) v2
#### インストール
まず適当なディレクトリにこのリポジトリをクローンします。
次にそのディレクトリに`config.json`を作成します。中身は次のようにします。
MeCabの設定、memoryDirについては触らないでください MeCabの設定、memoryDirについては触らないでください
``` json ``` json
{ {
"host": "https:// + あなたのインスタンスのURL (末尾の / は除く)", "host": "https:// + あなたのインスタンスのURL (末尾の / は除く)",
@ -62,7 +86,7 @@ Misskey用の[Aiベース](https://github.com/syuilo/ai)のBotです。
"notingEnabled": "ランダムにートを投稿する機能。true(on) or false(off)", "notingEnabled": "ランダムにートを投稿する機能。true(on) or false(off)",
"keywordEnabled": "キーワードを覚える機能 (MeCab が必要) true or false", "keywordEnabled": "キーワードを覚える機能 (MeCab が必要) true or false",
"mecab": "/usr/bin/mecab", "mecab": "/usr/bin/mecab",
"mecabDic": "/usr/lib/x86_64-linux-gnu/mecab/dic/mecab-ipadic-neologd/", "mecabDic": "/min",
"memoryDir": "data", "memoryDir": "data",
"shellgeiUrl": "シェル芸BotのAPIのURLです(オプション、デフォルトではhttps://websh.jiro4989.com/api/shellgei)", "shellgeiUrl": "シェル芸BotのAPIのURLです(オプション、デフォルトではhttps://websh.jiro4989.com/api/shellgei)",
"love": "いいねやloveの意味をを表す絵文字を入れる(デフォルトだと:love_nullcatchan:)", "love": "いいねやloveの意味をを表す絵文字を入れる(デフォルトだと:love_nullcatchan:)",
@ -74,12 +98,8 @@ Misskey用の[Aiベース](https://github.com/syuilo/ai)のBotです。
"ti": "ちの意味をを表す絵文字を入れる(デフォルトだと:_ti:)" "ti": "ちの意味をを表す絵文字を入れる(デフォルトだと:_ti:)"
} }
``` ```
`pnpm install` して `pnpm run docker` すれば起動できます。<br>
`docker-compose.yml``enable_mecab``0` にすると、MeCabをインストールしないようにもできます。メモリが少ない環境など
#### dockerimageを配布しています
[ここ](https://hub.docker.com/r/nullnyat/nullcatchan)にある
`docker compose up -d`すれば起動できます。
#### 一部の機能にはフォントが必要です。NullcatChan!にはフォントは同梱されていないので、ご自身でフォントをインストールしてそのフォントを`font.ttf`という名前でインストールディレクトリに設置してください。 #### 一部の機能にはフォントが必要です。NullcatChan!にはフォントは同梱されていないので、ご自身でフォントをインストールしてそのフォントを`font.ttf`という名前でインストールディレクトリに設置してください。
#### NullcatChan!は記憶の保持にインメモリデータベースを使用しており、nullcatchanのインストールディレクトリに `memory.json` という名前で永続化されます。 #### NullcatChan!は記憶の保持にインメモリデータベースを使用しており、nullcatchanのインストールディレクトリに `memory.json` という名前で永続化されます。

View File

@ -1,74 +1,69 @@
{ {
"version": "2.2.0", "version": "2.3.0",
"main": "./built/index.js", "main": "./built/index.js",
"packageManager": "pnpm@7.26.3",
"scripts": { "scripts": {
"docker:dev": "cross-env DOCKER_ENV=development docker-compose -f docker-compose.yml -f docker-compose_development.yml up -d --build && docker-compose logs -f", "dev": "cross-env NODE_ENV=development nodemon --exec ts-node -r tsconfig-paths/register src",
"docker": "cross-env DOCKER_ENV=production docker-compose up -d --build && docker-compose logs -f",
"dev": "cross-env NODE_ENV=development node ./built",
"start": "cross-env NODE_ENV=production node ./built", "start": "cross-env NODE_ENV=production node ./built",
"lint": "prettier --write ./src/", "lint": "prettier --write ./src/",
"build": "swc src -d built", "build": "swc src -d built",
"test": "jest" "test": "jest"
}, },
"dependencies": { "dependencies": {
"accurate-interval": "1.0.9", "accurate-interval": "^1.0.9",
"autobind-decorator": "2.4.0", "autobind-decorator": "^2.4.0",
"canvas": "2.8.0", "canvas": "^2.11.0",
"chalk": "4.1.1", "chalk": "^4.1.1",
"cjp": "1.2.3", "cjp": "^2.3.0",
"cross-env": "7.0.3", "cross-env": "^7.0.3",
"gomamayo-js": "0.2.1", "gomamayo-js": "^0.2.1",
"humanize-duration": "3.27.1", "humanize-duration": "^3.28.0",
"koa": "2.13.1", "koa": "^2.14.1",
"koa-json-body": "5.3.0", "koa-json-body": "^5.3.0",
"lokijs": "1.5.12", "lokijs": "^1.5.12",
"memory-streams": "0.1.3", "memory-streams": "^0.1.3",
"misskey-reversi": "0.0.5", "misskey-reversi": "^0.0.5",
"module-alias": "2.2.2", "module-alias": "^2.2.2",
"moji": "0.5.1", "moji": "^0.5.1",
"node-fetch": "2.6.7",
"promise-retry": "2.0.1", "promise-retry": "2.0.1",
"random-seed": "0.3.0", "random-seed": "^0.3.0",
"reconnecting-websocket": "4.4.0", "reconnecting-websocket": "4.4.0",
"request": "2.88.2", "seedrandom": "^3.0.5",
"request-promise-native": "1.0.9", "timeout-as-promise": "^1.0.0",
"seedrandom": "3.0.5", "twemoji-parser": "^14.0.0",
"timeout-as-promise": "1.0.0", "uuid": "^9.0.0",
"twemoji-parser": "13.1.0", "websocket": "^1.0.34",
"uuid": "8.3.2", "ws": "8.12.0",
"ws": "7.5.2", "zod": "^3.20.2"
"zod": "3.11.6"
}, },
"devDependencies": { "devDependencies": {
"@koa/router": "9.4.0", "@koa/router": "^12.0.0",
"@swc/cli": "0.1.59", "@swc/cli": "^0.1.60",
"@swc/core": "1.3.30", "@swc/core": "^1.3.32",
"@types/accurate-interval": "1.0.0", "@swc/helpers": "0.4.14",
"@types/chalk": "2.2.0", "@types/accurate-interval": "^1.0.0",
"@types/humanize-duration": "3.27.1", "@types/humanize-duration": "^3.27.1",
"@types/jest": "26.0.23", "@types/jest": "^29.4.0",
"@types/koa": "2.13.1", "@types/koa": "^2.13.5",
"@types/koa__router": "8.0.4", "@types/koa__router": "^12.0.0",
"@types/lokijs": "1.5.4", "@types/lokijs": "^1.5.7",
"@types/moji": "0.5.0", "@types/moji": "^0.5.0",
"@types/node": "16.0.1", "@types/node": "^18.11.18",
"@types/promise-retry": "1.1.3", "@types/promise-retry": "^1.1.3",
"@types/random-seed": "0.3.3", "@types/random-seed": "^0.3.3",
"@types/request-promise-native": "1.0.18", "@types/seedrandom": "^3.0.4",
"@types/seedrandom": "2.4.28", "@types/twemoji-parser": "^13.1.1",
"@types/twemoji-parser": "13.1.1", "@types/uuid": "^9.0.0",
"@types/uuid": "8.3.1", "@types/websocket": "^1.0.5",
"@types/websocket": "1.0.2", "@types/ws": "^8.5.4",
"@types/ws": "7.4.6", "jest": "^29.4.1",
"jest": "26.6.3", "nodemon": "2.0.20",
"prettier": "2.5.1", "prettier": "^2.8.3",
"ts-jest": "26.5.6", "regenerator-runtime": "0.13.11",
"ts-node": "10.0.0", "ts-jest": "^29.0.5",
"typescript": "4.5.5", "ts-node": "^10.9.1",
"websocket": "1.0.34" "tsconfig-paths": "4.1.2",
}, "typescript": "^4.9.5"
"_moduleAliases": {
"@": "built"
}, },
"jest": { "jest": {
"testRegex": "/test/.*", "testRegex": "/test/.*",

4654
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -2,11 +2,10 @@
import * as fs from 'fs'; import * as fs from 'fs';
import autobind from 'autobind-decorator'; import autobind from 'autobind-decorator';
import * as loki from 'lokijs'; import loki from 'lokijs';
import * as request from 'request-promise-native'; import chalk from 'chalk';
import * as chalk from 'chalk';
import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
const delay = require('timeout-as-promise'); import delay from 'timeout-as-promise';
import config from '@/config'; import config from '@/config';
import Module from '@/module'; import Module from '@/module';
@ -363,16 +362,18 @@ export default class 藍 {
*/ */
@autobind @autobind
public async upload(file: Buffer | fs.ReadStream, meta: any) { public async upload(file: Buffer | fs.ReadStream, meta: any) {
const res = await request.post({ const res = await fetch(`${config.apiUrl}/drive/files/create`, {
url: `${config.apiUrl}/drive/files/create`, method: "POST",
formData: { body: JSON.stringify({
i: config.i, i: config.i,
file: { file: {
value: file, value: file,
options: meta options: meta
} }
}, }),
json: true headers: {
"Content-Type": "application/json",
}
}); });
return res; return res;
} }
@ -418,13 +419,21 @@ export default class 藍 {
*/ */
@autobind @autobind
public api(endpoint: string, param?: any) { public api(endpoint: string, param?: any) {
return request.post(`${config.apiUrl}/${endpoint}`, { return fetch(`${config.apiUrl}/${endpoint}`, {
json: Object.assign( method: "POST",
{ headers: {
i: config.i "Content-Type": "application/json",
}, },
param body: JSON.stringify({
) ...{ i: config.i }, ...param
}),
}).then(res => {
if(res.status != 204)
return res.json();
return new Promise((resolve, reject) => {
resolve(JSON.parse("{}"));
})
}); });
} }

View File

@ -2,9 +2,8 @@
import 'module-alias/register'; import 'module-alias/register';
import * as chalk from 'chalk'; import chalk from 'chalk';
import * as request from 'request-promise-native'; import Retry from 'promise-retry';
const promiseRetry = require('promise-retry');
import from './ai'; import from './ai';
import config from './config'; import config from './config';
@ -53,23 +52,26 @@ function log(msg: string): void {
log(chalk.bold(`Nullcat chan! v${pkg.version}`)); log(chalk.bold(`Nullcat chan! v${pkg.version}`));
promiseRetry( Retry(
retry => { retry => {
log(`Account fetching... ${chalk.gray(config.host)}`); log(`Account fetching... ${chalk.gray(config.host)}`);
// アカウントをフェッチ // アカウントをフェッチ
return request return fetch(`${config.apiUrl}/i`, {
.post(`${config.apiUrl}/i`, { method: "POST",
json: { headers: {
i: config.i "Content-Type": "application/json",
} },
body: JSON.stringify({
i: config.i,
}) })
.catch(retry); }).catch(retry);
}, },
{ {
retries: 3 retries: 3
} }
) )
.then(res => res.json())
.then(account => { .then(account => {
const acct = `@${account.username}`; const acct = `@${account.username}`;
log(chalk.green(`Account fetched successfully: ${chalk.underline(acct)}`)); log(chalk.green(`Account fetched successfully: ${chalk.underline(acct)}`));

View File

@ -1,6 +1,6 @@
import autobind from 'autobind-decorator'; import autobind from 'autobind-decorator';
import * as chalk from 'chalk'; import chalk from 'chalk';
const delay = require('timeout-as-promise'); import delay from 'timeout-as-promise';
import from '@/ai'; import from '@/ai';
import Friend from '@/friend'; import Friend from '@/friend';

View File

@ -2,7 +2,6 @@ import config from '@/config';
import Message from '@/message'; import Message from '@/message';
import Module from '@/module'; import Module from '@/module';
import autobind from 'autobind-decorator'; import autobind from 'autobind-decorator';
import fetch from 'node-fetch';
import { z } from 'zod'; import { z } from 'zod';
export default class extends Module { export default class extends Module {

View File

@ -1,7 +1,6 @@
import autobind from 'autobind-decorator'; import autobind from 'autobind-decorator';
import { parse } from 'twemoji-parser'; import delay from 'timeout-as-promise';
import config from '@/config'; import config from '@/config';
const delay = require('timeout-as-promise');
import { Note } from '@/misskey/note'; import { Note } from '@/misskey/note';
import Module from '@/module'; import Module from '@/module';
import Stream from '@/stream'; import Stream from '@/stream';

View File

@ -1,7 +1,7 @@
import Message from '@/message'; import Message from '@/message';
import Module from '@/module'; import Module from '@/module';
import autobind from 'autobind-decorator'; import autobind from 'autobind-decorator';
import * as seedrandom from 'seedrandom'; import seedrandom from 'seedrandom';
export const feelings = ['つらい', 'ねむい', 'るんるん', '虚無']; export const feelings = ['つらい', 'ねむい', 'るんるん', '虚無'];

View File

@ -2,7 +2,7 @@ import autobind from 'autobind-decorator';
import Module from '@/module'; import Module from '@/module';
import Message from '@/message'; import Message from '@/message';
import serifs from '@/serifs'; import serifs from '@/serifs';
import * as seedrandom from 'seedrandom'; import seedrandom from 'seedrandom';
import { genItem } from '@/vocabulary'; import { genItem } from '@/vocabulary';
export const blessing = ['にゃん吉🐈', 'みゃ~吉🐾', 'ぬるきゃっと吉💙', 'なんかすごい吉✨', '特大吉✨', '大大吉🎊', '大吉🎊', '吉🎉', '中吉🎉', '小吉🎉', '凶🗿', '大凶🗿']; export const blessing = ['にゃん吉🐈', 'みゃ~吉🐾', 'ぬるきゃっと吉💙', 'なんかすごい吉✨', '特大吉✨', '大大吉🎊', '大吉🎊', '吉🎉', '中吉🎉', '小吉🎉', '凶🗿', '大凶🗿'];

View File

@ -2,7 +2,6 @@ import config from '@/config';
import Message from '@/message'; import Message from '@/message';
import Module from '@/module'; import Module from '@/module';
import autobind from 'autobind-decorator'; import autobind from 'autobind-decorator';
import fetch from 'node-fetch';
import { z } from 'zod'; import { z } from 'zod';
export default class extends Module { export default class extends Module {

View File

@ -1,7 +1,7 @@
import Module from '@/module'; import Module from '@/module';
import autobind from 'autobind-decorator'; import autobind from 'autobind-decorator';
const accurateInterval = require('accurate-interval'); import accurateInterval from 'accurate-interval';
export default class extends Module { export default class extends Module {
public readonly name = 'jihou'; public readonly name = 'jihou';

View File

@ -1,5 +1,5 @@
import autobind from 'autobind-decorator'; import autobind from 'autobind-decorator';
import * as loki from 'lokijs'; import loki from 'lokijs';
import Message from '@/message'; import Message from '@/message';
import Module from '@/module'; import Module from '@/module';
import NGWord from '@/ng-words'; import NGWord from '@/ng-words';

View File

@ -1,7 +1,6 @@
import Message from '@/message'; import Message from '@/message';
import Module from '@/module'; import Module from '@/module';
import autobind from 'autobind-decorator'; import autobind from 'autobind-decorator';
import fetch from 'node-fetch';
import { z } from 'zod'; import { z } from 'zod';
export default class extends Module { export default class extends Module {

View File

@ -2,7 +2,7 @@ import Module from '@/module';
import serifs from '@/serifs'; import serifs from '@/serifs';
import autobind from 'autobind-decorator'; import autobind from 'autobind-decorator';
const accurateInterval = require('accurate-interval'); import accurateInterval from 'accurate-interval';
export default class extends Module { export default class extends Module {
public readonly name = 'rogubo'; public readonly name = 'rogubo';

View File

@ -2,7 +2,6 @@ import autobind from 'autobind-decorator';
import Module from '@/module'; import Module from '@/module';
import Message from '@/message'; import Message from '@/message';
import config from '@/config'; import config from '@/config';
import fetch from 'node-fetch';
export default class extends Module { export default class extends Module {
public readonly name = 'shellgei'; public readonly name = 'shellgei';

View File

@ -1,9 +1,8 @@
import Message from '@/message'; import Message from '@/message';
import Module from '@/module'; import Module from '@/module';
import autobind from 'autobind-decorator'; import autobind from 'autobind-decorator';
import fetch from 'node-fetch';
import { z } from 'zod'; import { z } from 'zod';
import humanizeDuration = require('humanize-duration'); import humanizeDuration from 'humanize-duration';
export default class extends Module { export default class extends Module {
public readonly name = 'trace-moe'; public readonly name = 'trace-moe';

View File

@ -48,8 +48,6 @@ export default class extends Module {
this.log(`Version changed: ${v}`); this.log(`Version changed: ${v}`);
this.ai.post({ text: `ぼくのおうちが${v}にリフォームされたよ!!` }); this.ai.post({ text: `ぼくのおうちが${v}にリフォームされたよ!!` });
} else {
// 変更なし
} }
} }

View File

@ -1,7 +1,7 @@
import toHiragana from './utils/to-hiragana'; import toHiragana from './utils/to-hiragana';
const fs = require('fs'); import fs from 'fs';
const readline = require('readline'); import readline from 'readline';
export default class NGWord { export default class NGWord {
private excludedWords: string[] = []; private excludedWords: string[] = [];

View File

@ -1,7 +1,7 @@
import autobind from 'autobind-decorator'; import autobind from 'autobind-decorator';
import { EventEmitter } from 'events'; import { EventEmitter } from 'events';
import * as WebSocket from 'ws'; import WebSocket from 'ws';
const ReconnectingWebsocket = require('reconnecting-websocket'); import ReconnectingWebsocket from 'reconnecting-websocket';
import config from './config'; import config from './config';
/** /**

View File

@ -1,4 +1,4 @@
import * as chalk from 'chalk'; import chalk from 'chalk';
export default function (msg: string) { export default function (msg: string) {
const now = new Date(); const now = new Date();

View File

@ -1,4 +1,4 @@
import * as seedrandom from 'seedrandom'; import seedrandom from 'seedrandom';
export const itemPrefixes = [ export const itemPrefixes = [
'そこらへんの', 'そこらへんの',

View File

@ -8,6 +8,7 @@
"noFallthroughCasesInSwitch": true, "noFallthroughCasesInSwitch": true,
"strictNullChecks": true, "strictNullChecks": true,
"experimentalDecorators": true, "experimentalDecorators": true,
"esModuleInterop": true,
"sourceMap": false, "sourceMap": false,
"target": "es2020", "target": "es2020",
"module": "commonjs", "module": "commonjs",
@ -23,5 +24,8 @@
"compileOnSave": false, "compileOnSave": false,
"include": [ "include": [
"./src/**/*.ts" "./src/**/*.ts"
] ],
"ts-node": {
"swc": true
}
} }