mirror of
https://github.com/kou029w/daraz-san.git
synced 2025-01-18 08:05:04 +00:00
TypeScriptへの移行
This commit is contained in:
parent
bffb243526
commit
f56f9aca15
34 changed files with 943 additions and 324 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -1 +1,3 @@
|
||||||
/node_modules/
|
.vercel
|
||||||
|
dist
|
||||||
|
node_modules
|
||||||
|
|
50
README.md
50
README.md
|
@ -1,22 +1,44 @@
|
||||||
# だらずさん ⚡
|
# だらずさん ⚡
|
||||||
|
|
||||||
[Bolt](https://github.com/slackapi/bolt-js) で作り直した [だらずさん](https://github.com/daraz-tek/daraz-bot)
|
[Bolt](https://github.com/slackapi/bolt-js) で作られた [だらずさん](https://github.com/daraz-tek/daraz-bot)
|
||||||
|
|
||||||
## つかいかた
|
1. デプロイ
|
||||||
|
2. Slack アプリの作成
|
||||||
|
3. 環境変数の設定
|
||||||
|
|
||||||
### Slack アプリの作成
|
## デプロイ
|
||||||
|
|
||||||
- Redirect URLs を設定 (例: https://{ホスト名}/ )
|
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fkou029w%2Fdaraz-san)
|
||||||
- Bot Users を設定
|
|
||||||
- (デプロイ後) Event Subscriptions を設定
|
|
||||||
- Request URL を設定 (例: https://{ホスト名}/slack/events )
|
|
||||||
- Bot Events を登録
|
|
||||||
- message.channels (パブリックチャンネルのメッセージをリスニング) など
|
|
||||||
|
|
||||||
### 起動
|
## Slack アプリの作成
|
||||||
|
|
||||||
次の環境変数を与えてから `yarn && yarn start`
|
[Slack Applications](https://api.slack.com/apps) → Create New App → From an app manifest
|
||||||
|
|
||||||
- `SLACK_BOT_TOKEN` ... OAuth & Permissions ページにあるボット (xoxb) トークン
|
下記の Manifest の `{Vercelのプロジェクト名}` の部分を変更してコピペして、Slack アプリを作成します。
|
||||||
- `SLACK_SIGNING_SECRET` ... Basic Information ページにある Signing Secret
|
|
||||||
- (Optional) `PORT` ... ポート番号。デフォルトは 8080。
|
```yaml
|
||||||
|
display_information:
|
||||||
|
name: だらずさん
|
||||||
|
features:
|
||||||
|
bot_user:
|
||||||
|
display_name: daraz-san
|
||||||
|
oauth_config:
|
||||||
|
scopes:
|
||||||
|
bot:
|
||||||
|
- channels:history
|
||||||
|
- chat:write
|
||||||
|
settings:
|
||||||
|
event_subscriptions:
|
||||||
|
request_url: https://{Vercelのプロジェクト名}.vercel.app/api/slack/events
|
||||||
|
bot_events:
|
||||||
|
- message.channels
|
||||||
|
```
|
||||||
|
|
||||||
|
## 環境変数の設定
|
||||||
|
|
||||||
|
[Vercel Dashboard](https://vercel.com/dashboard) → 対象のプロジェクト → Settings → Environment Variables
|
||||||
|
|
||||||
|
- `SLACK_BOT_TOKEN` ... [Slack Applications](https://api.slack.com/apps) → だらずさん → Permissions ページにある `xoxb-` から始まるボットトークン
|
||||||
|
- `SLACK_SIGNING_SECRET` ... [Slack Applications](https://api.slack.com/apps) → だらずさん → Basic Information ページにある Signing Secret
|
||||||
|
|
||||||
|
これらの環境変数を設定後、再びデプロイして反映します。
|
||||||
|
|
10
api/slack/events.ts
Normal file
10
api/slack/events.ts
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import { AwsLambdaReceiver } from "@slack/bolt";
|
||||||
|
import App from "../../dist/app";
|
||||||
|
|
||||||
|
const token = process.env.SLACK_BOT_TOKEN;
|
||||||
|
const signingSecret = process.env.SLACK_SIGNING_SECRET;
|
||||||
|
const receiver = new AwsLambdaReceiver({ signingSecret });
|
||||||
|
|
||||||
|
new App({ token, receiver });
|
||||||
|
|
||||||
|
export const handler = receiver.toHandler();
|
17
app.js
17
app.js
|
@ -1,17 +0,0 @@
|
||||||
require("cross-fetch/polyfill");
|
|
||||||
const { App: BoltApp } = require("@slack/bolt");
|
|
||||||
|
|
||||||
class App extends BoltApp {
|
|
||||||
/**
|
|
||||||
* @param {import("@slack/bolt").AppOptions} opt
|
|
||||||
*/
|
|
||||||
constructor(opt) {
|
|
||||||
super(opt);
|
|
||||||
const scripts = require("glob").sync("./scripts/*.js").map(require);
|
|
||||||
scripts.forEach((script) => {
|
|
||||||
if (Array.isArray(script)) return this.message(...script);
|
|
||||||
if (script instanceof Function) return script(this);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
module.exports = App;
|
|
18
main.js
18
main.js
|
@ -1,18 +0,0 @@
|
||||||
const App = require("./app");
|
|
||||||
const Receiver = require("./receiver");
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
const receiver = new Receiver({
|
|
||||||
signingSecret: process.env.SLACK_SIGNING_SECRET,
|
|
||||||
});
|
|
||||||
|
|
||||||
const app = new App({
|
|
||||||
token: process.env.SLACK_BOT_TOKEN,
|
|
||||||
receiver,
|
|
||||||
});
|
|
||||||
|
|
||||||
await app.start(process.env.PORT || 8080);
|
|
||||||
console.log("Daraz-san ⚡ running");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (require.main === module) main();
|
|
27
package.json
27
package.json
|
@ -11,19 +11,24 @@
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node main.js"
|
"build": "echo no front-end build specified",
|
||||||
|
"vercel-build": "tsup src --clean"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"devDependencies": {
|
||||||
"@slack/bolt": "3.x",
|
"@slack/bolt": "^3.11.0",
|
||||||
|
"@types/kuromoji": "^0.1.1",
|
||||||
|
"@types/tldjs": "^2.3.1",
|
||||||
"cross-fetch": "^3.1.5",
|
"cross-fetch": "^3.1.5",
|
||||||
"date-fns": "2.x",
|
"date-fns": "^2.28.0",
|
||||||
"date-fns-tz": "1.x",
|
"date-fns-tz": "^1.3.3",
|
||||||
"get-video-id": "3.x",
|
"get-video-id": "^3.5.3",
|
||||||
"glob": "8.x",
|
"glob": "^8.0.1",
|
||||||
"kuromoji": "0.1.x",
|
"kuromoji": "^0.1.2",
|
||||||
"tldjs": "2.x"
|
"tldjs": "^2.3.1",
|
||||||
|
"tsup": "^5.12.6"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"yarn": "1.x"
|
"node": ">=14.19.0"
|
||||||
}
|
},
|
||||||
|
"packageManager": "yarn@1.22.15"
|
||||||
}
|
}
|
||||||
|
|
1
public/index.txt
Normal file
1
public/index.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
OK
|
18
receiver.js
18
receiver.js
|
@ -1,18 +0,0 @@
|
||||||
const { promisify } = require("util");
|
|
||||||
const { ExpressReceiver } = require("@slack/bolt");
|
|
||||||
|
|
||||||
class Receiver extends ExpressReceiver {
|
|
||||||
/**
|
|
||||||
* @param {import("express").Request} req
|
|
||||||
* @param {import("express").Response} res
|
|
||||||
*/
|
|
||||||
requestHandler(req, res) {
|
|
||||||
// NOTE: See also https://api.slack.com/events-api#errors
|
|
||||||
res.header("x-slack-no-retry", "1");
|
|
||||||
if (req.headers["x-slack-retry-reason"] === "http_timeout")
|
|
||||||
return promisify(res.end)();
|
|
||||||
|
|
||||||
return super.requestHandler(req, res);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
module.exports = Receiver;
|
|
|
@ -1,9 +1 @@
|
||||||
{
|
{ "extends": ["config:base", "config:semverAllMonthly", ":automergeMinor"] }
|
||||||
"extends": [
|
|
||||||
"config:base",
|
|
||||||
":preserveSemverRanges",
|
|
||||||
":maintainLockFilesWeekly"
|
|
||||||
],
|
|
||||||
"automerge": true,
|
|
||||||
"requiredStatusChecks": null
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
const { directMention } = require("@slack/bolt");
|
|
||||||
|
|
||||||
module.exports = [
|
|
||||||
directMention(),
|
|
||||||
/(ニャンちゅう) +(.*)/,
|
|
||||||
({ context, say }) => {
|
|
||||||
const oon = context.matches[2].split("").map((c) => `${c}゛`);
|
|
||||||
return say(`お゛ぉ゛ん!${oon.join("")}た゛に゛ゃあ゛ん! > :nyanchu:`);
|
|
||||||
},
|
|
||||||
];
|
|
|
@ -1,30 +0,0 @@
|
||||||
const { useTokenize } = require("./util/morpheme");
|
|
||||||
const random = require("./util/random");
|
|
||||||
const tellme = require("./util/tellme");
|
|
||||||
|
|
||||||
// ときどきうんちくを語ります
|
|
||||||
|
|
||||||
module.exports = [
|
|
||||||
/[^\x01-\x7e]{4,}/,
|
|
||||||
async ({ context, say }) => {
|
|
||||||
if (random([...Array(15).keys()]) !== 0) return;
|
|
||||||
try {
|
|
||||||
const tokenize = await useTokenize();
|
|
||||||
const words = tokenize(context.matches[0])
|
|
||||||
.filter(({ pos }) => pos === "名詞")
|
|
||||||
.map(({ surface_form }) => surface_form)
|
|
||||||
.filter((t) => !/^[\u3040-\u309F]$/.test(t)) //ひらがな1文字 http://www.unicode.org/charts/PDF/U3040.pdf
|
|
||||||
.filter((t) => !/^[\u30A0-\u30FF]$/.test(t)) //かたかな1文字 http://www.unicode.org/charts/PDF/U30A0.pdf
|
|
||||||
.filter((t) => !/^[\uFF65-\uFF9F]$/.test(t)) //半角カナ1文字 http://www.unicode.org/charts/PDF/UFF00.pdf
|
|
||||||
.filter((t) => !/[、・…]/.test(t));
|
|
||||||
const word = random(words);
|
|
||||||
const ans = await tellme(word);
|
|
||||||
if (/^(|…|しらないにゃーん)$/.test(ans)) {
|
|
||||||
throw new Error(`don't know ${word} : ${ans}`);
|
|
||||||
}
|
|
||||||
return say(`:nya-n: < 【う・ん・ち・く】${ans}`);
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
];
|
|
|
@ -1,5 +0,0 @@
|
||||||
const random = require("./random");
|
|
||||||
|
|
||||||
const nyans = [":nya-n1:", ":nya-n2:", ":nya-n3:", ":nya-n4:", ":nya-n5:"];
|
|
||||||
|
|
||||||
module.exports = nyanco = () => random(nyans);
|
|
|
@ -1 +0,0 @@
|
||||||
module.exports = (array) => array[Math.floor(Math.random() * array.length)];
|
|
23
src/app.ts
Normal file
23
src/app.ts
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import { App as BoltApp, AppOptions } from "@slack/bolt";
|
||||||
|
import path from "node:path";
|
||||||
|
import glob from "glob";
|
||||||
|
// TODO: Node.js v18+ ならば不要
|
||||||
|
import "cross-fetch/polyfill";
|
||||||
|
|
||||||
|
const scriptsPattern = path.join(__dirname, "scripts/*.js");
|
||||||
|
const scripts = glob
|
||||||
|
.sync(scriptsPattern)
|
||||||
|
.map(require)
|
||||||
|
.map((m) => m.default ?? m);
|
||||||
|
|
||||||
|
class App extends BoltApp {
|
||||||
|
constructor(options: AppOptions) {
|
||||||
|
super(options);
|
||||||
|
scripts.forEach((script) => {
|
||||||
|
if (Array.isArray(script)) return this.message(...script);
|
||||||
|
if (script instanceof Function) return script(this);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App;
|
|
@ -1,10 +1,10 @@
|
||||||
const get_video_id = require("get-video-id");
|
import getVideoId from "get-video-id";
|
||||||
|
|
||||||
const API_KEY = "Q8mtkFkP4Zru4mlDd812iw2vcQwx5B0qIsUKsxit";
|
const API_KEY = "Q8mtkFkP4Zru4mlDd812iw2vcQwx5B0qIsUKsxit";
|
||||||
|
|
||||||
module.exports = [
|
export default [
|
||||||
/apod|galaxy|spa+ce|宇宙|コスモ|銀河/i,
|
/apod|galaxy|spa+ce|宇宙|コスモ|銀河/i,
|
||||||
async ({ say }) => {
|
async ({ say }: any) => {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`https://api.nasa.gov/planetary/apod?api_key=${API_KEY}`
|
`https://api.nasa.gov/planetary/apod?api_key=${API_KEY}`
|
||||||
);
|
);
|
||||||
|
@ -14,7 +14,7 @@ module.exports = [
|
||||||
const dict = await response.json();
|
const dict = await response.json();
|
||||||
const url =
|
const url =
|
||||||
dict.media_type === "video"
|
dict.media_type === "video"
|
||||||
? `https://www.youtube.com/watch?v=${get_video_id(dict.url)}`
|
? `https://www.youtube.com/watch?v=${getVideoId(dict.url)}`
|
||||||
: dict.url;
|
: dict.url;
|
||||||
return say(`宇宙って良いにゃーん\n${dict.title}\n${url}`);
|
return say(`宇宙って良いにゃーん\n${dict.title}\n${url}`);
|
||||||
},
|
},
|
|
@ -1,19 +1,15 @@
|
||||||
const dns = require("dns");
|
import { tldExists } from "tldjs";
|
||||||
const { tldExists } = require("tldjs");
|
import dns from "node:dns";
|
||||||
|
|
||||||
module.exports = [
|
export default [
|
||||||
/(([a-z0-9]|[a-z0-9][a-z0-9\-]*[a-z0-9])\.)+([a-z]+)/,
|
/(([a-z0-9]|[a-z0-9][a-z0-9\-]*[a-z0-9])\.)+([a-z]+)/,
|
||||||
async ({ context, say }) => {
|
async ({ context, say }: any) => {
|
||||||
const domain = context.matches[0];
|
const domain = context.matches[0];
|
||||||
const ignores = ["daraz-tek.slack.com"];
|
const ignores = ["daraz-tek.slack.com"];
|
||||||
if (ignores.includes(domain)) return;
|
if (ignores.includes(domain)) return;
|
||||||
if (!tldExists(domain)) return;
|
if (!tldExists(domain)) return;
|
||||||
try {
|
try {
|
||||||
const records = await new Promise((resolve, reject) =>
|
const records = await dns.promises.resolve(domain);
|
||||||
dns.resolve(domain, (err, records) =>
|
|
||||||
err ? reject(err) : resolve(records)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
return say(
|
return say(
|
||||||
`:nya-n: < ${domain} は ${records.join(" *,* ")} ですにゃん`
|
`:nya-n: < ${domain} は ${records.join(" *,* ")} ですにゃん`
|
||||||
);
|
);
|
|
@ -1,5 +1,5 @@
|
||||||
module.exports = (app) => {
|
export default (app: any) => {
|
||||||
app.message(/555/, ({ say }) =>
|
app.message(/555/, ({ say }: any) =>
|
||||||
say(
|
say(
|
||||||
[
|
[
|
||||||
"Standing by... > :nya-n:",
|
"Standing by... > :nya-n:",
|
||||||
|
@ -10,7 +10,7 @@ module.exports = (app) => {
|
||||||
].join("\n")
|
].join("\n")
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
app.message(/551/, ({ say }) =>
|
app.message(/551/, ({ say }: any) =>
|
||||||
say(
|
say(
|
||||||
[
|
[
|
||||||
"551の豚まんがあるときー? > :nya-n:",
|
"551の豚まんがあるときー? > :nya-n:",
|
|
@ -1,5 +1,5 @@
|
||||||
module.exports = [
|
export default [
|
||||||
/([\s\S]*(ぱい|パイ)[\s\S]*)/,
|
/([\s\S]*(ぱい|パイ)[\s\S]*)/,
|
||||||
({ context, say }) =>
|
({ context, say }: any) =>
|
||||||
say(`:goku: < ${context.matches[0].replace(/ぱい|パイ/g, "ぺぇ")}`),
|
say(`:goku: < ${context.matches[0].replace(/ぱい|パイ/g, "ぺぇ")}`),
|
||||||
];
|
];
|
|
@ -1,6 +1,6 @@
|
||||||
module.exports = [
|
export default [
|
||||||
/(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])/,
|
/(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])/,
|
||||||
async ({ context, say }) => {
|
async ({ context, say }: any) => {
|
||||||
const ip = context.matches[0];
|
const ip = context.matches[0];
|
||||||
const url = `https://ipinfo.io/${ip}`;
|
const url = `https://ipinfo.io/${ip}`;
|
||||||
const options = {
|
const options = {
|
|
@ -1,5 +1,5 @@
|
||||||
const { directMention } = require("@slack/bolt");
|
import { directMention } from "@slack/bolt";
|
||||||
const nyanco = require("./util/nyanco");
|
import nyanco from "./utils/nyanco";
|
||||||
|
|
||||||
// Description:
|
// Description:
|
||||||
// だらずさんは JSON のお掃除がだいすきです
|
// だらずさんは JSON のお掃除がだいすきです
|
||||||
|
@ -7,10 +7,10 @@ const nyanco = require("./util/nyanco");
|
||||||
// Synopsis:
|
// Synopsis:
|
||||||
// json <jsonstring> - あなたの JSON をぷりちーにするにゃん
|
// json <jsonstring> - あなたの JSON をぷりちーにするにゃん
|
||||||
|
|
||||||
module.exports = [
|
export default [
|
||||||
directMention(),
|
directMention(),
|
||||||
/json +(.*)/i,
|
/json +(.*)/i,
|
||||||
({ context, say }) => {
|
({ context, say }: any) => {
|
||||||
try {
|
try {
|
||||||
const json = JSON.parse(context.matches[1]);
|
const json = JSON.parse(context.matches[1]);
|
||||||
const res = JSON.stringify(json, null, 2);
|
const res = JSON.stringify(json, null, 2);
|
|
@ -1,6 +1,6 @@
|
||||||
const { directMention } = require("@slack/bolt");
|
import { directMention } from "@slack/bolt";
|
||||||
const { useTokenize, toCSV } = require("./util/morpheme");
|
import { makeTokenize, toCSV } from "./utils/morpheme";
|
||||||
const nyanco = require("./util/nyanco");
|
import nyanco from "./utils/nyanco";
|
||||||
|
|
||||||
// Description:
|
// Description:
|
||||||
// すもももももももものうち
|
// すもももももももものうち
|
||||||
|
@ -8,14 +8,14 @@ const nyanco = require("./util/nyanco");
|
||||||
// Synopsis:
|
// Synopsis:
|
||||||
// morpheme <phrase> - <phrase> を形態素解析器にかけるにゃーん
|
// morpheme <phrase> - <phrase> を形態素解析器にかけるにゃーん
|
||||||
|
|
||||||
module.exports = [
|
export default [
|
||||||
directMention(),
|
directMention(),
|
||||||
/morpheme (.*)/i,
|
/morpheme (.*)/i,
|
||||||
async ({ context, say }) => {
|
async ({ context, say }: any) => {
|
||||||
try {
|
try {
|
||||||
const tokenize = await useTokenize();
|
const tokenize = await makeTokenize();
|
||||||
const tokens = tokenize(context.matches[1]);
|
const tokens = tokenize(context.matches[1]);
|
||||||
const readings = tokens.map(({ reading }) => reading);
|
const readings = tokens.map(({ reading }: any) => reading);
|
||||||
return say(
|
return say(
|
||||||
[`${nyanco()} < ${readings.join("")}`, toCSV(tokens)].join("\n")
|
[`${nyanco()} < ${readings.join("")}`, toCSV(tokens)].join("\n")
|
||||||
);
|
);
|
|
@ -1,49 +1,49 @@
|
||||||
// @ts-check
|
// @ts-check
|
||||||
const random = require("./util/random");
|
import random from "./utils/random";
|
||||||
const nyanco = require("./util/nyanco");
|
import nyanco from "./utils/nyanco";
|
||||||
|
|
||||||
const patterns = [
|
const patterns = [
|
||||||
[/ぬ.*る.*ぽ/, ({ say }) => say(`${nyanco()} < にゃーん`)],
|
[/ぬ.*る.*ぽ/, ({ say }: any) => say(`${nyanco()} < にゃーん`)],
|
||||||
[
|
[
|
||||||
/だらず((さん)?.*)/,
|
/だらず((さん)?.*)/,
|
||||||
({ context, say }) =>
|
({ context, say }: any) =>
|
||||||
say(
|
say(
|
||||||
`${nyanco()} < ${
|
`${nyanco()} < ${
|
||||||
context.matches[2] ? "にゃーん" : "さんを付けろよデコスケ野郎っ!"
|
context.matches[2] ? "にゃーん" : "さんを付けろよデコスケ野郎っ!"
|
||||||
}`
|
}`
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
[/こたつ/, ({ say }) => say(`${nyanco()} < しまえ`)],
|
[/こたつ/, ({ say }: any) => say(`${nyanco()} < しまえ`)],
|
||||||
[/(しお|塩)/, ({ say }) => say(`${nyanco()} < しお`)],
|
[/(しお|塩)/, ({ say }: any) => say(`${nyanco()} < しお`)],
|
||||||
[
|
[
|
||||||
/(らーめん|ラーメン|拉麺|らうめん)/,
|
/(らーめん|ラーメン|拉麺|らうめん)/,
|
||||||
({ say }) => say(`${nyanco()} < :ramen:`),
|
({ say }: any) => say(`${nyanco()} < :ramen:`),
|
||||||
],
|
],
|
||||||
[/しりとり/, ({ say }) => say(`${nyanco()} < うどん。`)],
|
[/しりとり/, ({ say }: any) => say(`${nyanco()} < うどん。`)],
|
||||||
[
|
[
|
||||||
/(糞|くそ|クソ)(すれ|スレ)/,
|
/(糞|くそ|クソ)(すれ|スレ)/,
|
||||||
({ say }) => say(`${nyanco()} < クソスレで悪かったな!!`),
|
({ say }: any) => say(`${nyanco()} < クソスレで悪かったな!!`),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
/(カレー|かれー|華麗)/,
|
/(カレー|かれー|華麗)/,
|
||||||
({ say }) => say("https://pbs.twimg.com/media/C-RVt9pUAAARRVe.jpg"),
|
({ say }: any) => say("https://pbs.twimg.com/media/C-RVt9pUAAARRVe.jpg"),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
/(すし|鮨|寿司|スシ|まぐろ|マグロ|sushi)/i,
|
/(すし|鮨|寿司|スシ|まぐろ|マグロ|sushi)/i,
|
||||||
({ say }) => say(`${nyanco()} < あいよ っ :sushi:`),
|
({ say }: any) => say(`${nyanco()} < あいよ っ :sushi:`),
|
||||||
],
|
],
|
||||||
[/ちゃ|茶/, ({ say }) => say(`お茶どぞー < ${nyanco()}っ :tea:`)],
|
[/ちゃ|茶/, ({ say }: any) => say(`お茶どぞー < ${nyanco()}っ :tea:`)],
|
||||||
[
|
[
|
||||||
/風邪|かぜ|カゼ|体調|つらい|くるしい|痛い|ひぎぃ|うぐぅ/,
|
/風邪|かぜ|カゼ|体調|つらい|くるしい|痛い|ひぎぃ|うぐぅ/,
|
||||||
({ say }) => say(`おくすりどぞー < ${nyanco()}っ :pill:`),
|
({ say }: any) => say(`おくすりどぞー < ${nyanco()}っ :pill:`),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
/(ちらし|チラシ|広告)/,
|
/(ちらし|チラシ|広告)/,
|
||||||
({ say }) => say(`${nyanco()} < スタンプラリーやめれ`),
|
({ say }: any) => say(`${nyanco()} < スタンプラリーやめれ`),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
/進捗どうですか/,
|
/進捗どうですか/,
|
||||||
({ message, say }) => {
|
({ message, say }: any) => {
|
||||||
const from = `<@${message.user}>`;
|
const from = `<@${message.user}>`;
|
||||||
return say(
|
return say(
|
||||||
[
|
[
|
||||||
|
@ -56,7 +56,7 @@ const patterns = [
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
/(のむら|さちよ|野村|沙知代|さっちー|サッチー|のむさん|ノムサン)/,
|
/(のむら|さちよ|野村|沙知代|さっちー|サッチー|のむさん|ノムサン)/,
|
||||||
({ say }) =>
|
({ say }: any) =>
|
||||||
say(
|
say(
|
||||||
[
|
[
|
||||||
":nomura-exodia-1::nomura-exodia-2::nomura-exodia-3:",
|
":nomura-exodia-1::nomura-exodia-2::nomura-exodia-3:",
|
||||||
|
@ -67,7 +67,7 @@ const patterns = [
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
/(肉|にく|ニク)/,
|
/(肉|にく|ニク)/,
|
||||||
({ say }) => {
|
({ say }: any) => {
|
||||||
if (random([...Array(3).keys()]) !== 0) return;
|
if (random([...Array(3).keys()]) !== 0) return;
|
||||||
return say(
|
return say(
|
||||||
[
|
[
|
||||||
|
@ -81,7 +81,7 @@ const patterns = [
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
/(野球|やきゅう|やきう)/,
|
/(野球|やきゅう|やきう)/,
|
||||||
({ say }) => {
|
({ say }: any) => {
|
||||||
if (random([...Array(10).keys()]) !== 0) return;
|
if (random([...Array(10).keys()]) !== 0) return;
|
||||||
return say(
|
return say(
|
||||||
[
|
[
|
||||||
|
@ -94,7 +94,7 @@ const patterns = [
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
/.*(ね|ネ).+(ハム|はむ)(たろう|たろー|タロウ|タロー|太郎)/,
|
/.*(ね|ネ).+(ハム|はむ)(たろう|たろー|タロウ|タロー|太郎)/,
|
||||||
({ context, say }) => {
|
({ context, say }: any) => {
|
||||||
if (
|
if (
|
||||||
/(死|亡|殺)/.test(context.matches[0]) ||
|
/(死|亡|殺)/.test(context.matches[0]) ||
|
||||||
random([...Array(10).keys()]) === 0
|
random([...Array(10).keys()]) === 0
|
||||||
|
@ -107,9 +107,9 @@ const patterns = [
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
/^(?=.*[eE]macs)(?=.*[vV]i)/,
|
/^(?=.*[eE]macs)(?=.*[vV]i)/,
|
||||||
({ say }) => say(`${nyanco()} < Emacs vs. Vi ファイ!`),
|
({ say }: any) => say(`${nyanco()} < Emacs vs. Vi ファイ!`),
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
module.exports = (app) =>
|
export default (app: any) =>
|
||||||
patterns.forEach((pattern) => app.message(...pattern));
|
patterns.forEach((pattern) => app.message(...pattern));
|
10
src/scripts/nyanchu.ts
Normal file
10
src/scripts/nyanchu.ts
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import { directMention } from "@slack/bolt";
|
||||||
|
|
||||||
|
export default [
|
||||||
|
directMention(),
|
||||||
|
/(ニャンちゅう) +(.*)/,
|
||||||
|
({ context, say }: any) => {
|
||||||
|
const oon = context.matches[2].split("").map((c: any) => `${c}゛`);
|
||||||
|
return say(`お゛ぉ゛ん!${oon.join("")}た゛に゛ゃあ゛ん! > :nyanchu:`);
|
||||||
|
},
|
||||||
|
];
|
|
@ -1,5 +1,5 @@
|
||||||
const { directMention } = require("@slack/bolt");
|
import { directMention } from "@slack/bolt";
|
||||||
const random = require("./util/random");
|
import random from "./utils/random";
|
||||||
|
|
||||||
// Description:
|
// Description:
|
||||||
// だらずさんはランダムにどれか選べます
|
// だらずさんはランダムにどれか選べます
|
||||||
|
@ -10,10 +10,10 @@ const random = require("./util/random");
|
||||||
// えらべ <word1> <word2> ... <wordN> - どれか選ぶにゃん、 word の区切りは空白あるいはカンマにゃん。
|
// えらべ <word1> <word2> ... <wordN> - どれか選ぶにゃん、 word の区切りは空白あるいはカンマにゃん。
|
||||||
// 選べ <word1> <word2> ... <wordN> - どれか選ぶにゃん、 word の区切りは空白あるいはカンマにゃん。
|
// 選べ <word1> <word2> ... <wordN> - どれか選ぶにゃん、 word の区切りは空白あるいはカンマにゃん。
|
||||||
|
|
||||||
module.exports = [
|
export default [
|
||||||
directMention(),
|
directMention(),
|
||||||
/(choice|random|えらべ|選べ) +(.*)/,
|
/(choice|random|えらべ|選べ) +(.*)/,
|
||||||
({ context, say }) => {
|
({ context, say }: any) => {
|
||||||
const words = [
|
const words = [
|
||||||
...context.matches[2].split(/(?:,|\s)+/),
|
...context.matches[2].split(/(?:,|\s)+/),
|
||||||
"人に決められるだけの人生でいいのか?自分で決めようず",
|
"人に決められるだけの人生でいいのか?自分で決めようず",
|
|
@ -1,6 +1,6 @@
|
||||||
const { directMention } = require("@slack/bolt");
|
import { directMention } from "@slack/bolt";
|
||||||
const tellme = require("./util/tellme");
|
import tellme from "./utils/tellme";
|
||||||
const nyanco = require("./util/nyanco");
|
import nyanco from "./utils/nyanco";
|
||||||
|
|
||||||
// Description:
|
// Description:
|
||||||
// だらずさんは何でも知っているので教えてくれます
|
// だらずさんは何でも知っているので教えてくれます
|
||||||
|
@ -8,10 +8,10 @@ const nyanco = require("./util/nyanco");
|
||||||
// Synopsis:
|
// Synopsis:
|
||||||
// tell me <phrase> - <phrase> について教えてあげよう、妖怪ウィキウィキペディアは使ってないよ!
|
// tell me <phrase> - <phrase> について教えてあげよう、妖怪ウィキウィキペディアは使ってないよ!
|
||||||
|
|
||||||
module.exports = [
|
export default [
|
||||||
directMention(),
|
directMention(),
|
||||||
/tell( ?me)? (.*)/i,
|
/tell( ?me)? (.*)/i,
|
||||||
async ({ context, say }) => {
|
async ({ context, say }: any) => {
|
||||||
const ans = await tellme(context.matches[2]);
|
const ans = await tellme(context.matches[2]);
|
||||||
if (ans != null) return say(`${nyanco()} < ${ans}`);
|
if (ans != null) return say(`${nyanco()} < ${ans}`);
|
||||||
},
|
},
|
30
src/scripts/unchiku.ts
Normal file
30
src/scripts/unchiku.ts
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import { makeTokenize } from "./utils/morpheme";
|
||||||
|
import random from "./utils/random";
|
||||||
|
import tellme from "./utils/tellme";
|
||||||
|
|
||||||
|
// ときどきうんちくを語ります
|
||||||
|
|
||||||
|
export default [
|
||||||
|
/[^\x01-\x7e]{4,}/,
|
||||||
|
async ({ context, say }: any) => {
|
||||||
|
if (random([...Array(15).keys()]) !== 0) return;
|
||||||
|
try {
|
||||||
|
const tokenize = await makeTokenize();
|
||||||
|
const words = tokenize(context.matches[0])
|
||||||
|
.filter(({ pos }: any) => pos === "名詞")
|
||||||
|
.map(({ surface_form }: any) => surface_form)
|
||||||
|
.filter((t: any) => !/^[\u3040-\u309F]$/.test(t)) //ひらがな1文字 http://www.unicode.org/charts/PDF/U3040.pdf
|
||||||
|
.filter((t: any) => !/^[\u30A0-\u30FF]$/.test(t)) //かたかな1文字 http://www.unicode.org/charts/PDF/U30A0.pdf
|
||||||
|
.filter((t: any) => !/^[\uFF65-\uFF9F]$/.test(t)) //半角カナ1文字 http://www.unicode.org/charts/PDF/UFF00.pdf
|
||||||
|
.filter((t: any) => !/[、・…]/.test(t));
|
||||||
|
const word = random(words);
|
||||||
|
const ans = await tellme(word);
|
||||||
|
if (/^(|…|しらないにゃーん)$/.test(ans)) {
|
||||||
|
throw new Error(`don't know ${word} : ${ans}`);
|
||||||
|
}
|
||||||
|
return say(`:nya-n: < 【う・ん・ち・く】${ans}`);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
];
|
|
@ -1,11 +1,11 @@
|
||||||
const kuromoji = require("kuromoji");
|
import kuromoji, { Tokenizer, IpadicFeatures } from "kuromoji";
|
||||||
|
import path from "node:path";
|
||||||
|
|
||||||
const dicPath = require("path").resolve(
|
const dicPath = path.resolve(require.resolve("kuromoji"), "../../dict");
|
||||||
require.resolve("kuromoji"),
|
|
||||||
"../../dict"
|
|
||||||
);
|
|
||||||
|
|
||||||
const useTokenize = () =>
|
export const makeTokenize = (): Promise<
|
||||||
|
Tokenizer<IpadicFeatures>["tokenize"]
|
||||||
|
> =>
|
||||||
new Promise((resolve, reject) =>
|
new Promise((resolve, reject) =>
|
||||||
kuromoji
|
kuromoji
|
||||||
.builder({ dicPath })
|
.builder({ dicPath })
|
||||||
|
@ -27,12 +27,10 @@ const features = new Map([
|
||||||
["pronunciation", "発音"],
|
["pronunciation", "発音"],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const toCSV = (tokens) =>
|
export const toCSV = (tokens: any) =>
|
||||||
[
|
[
|
||||||
[...features.values()].join(","),
|
[...features.values()].join(","),
|
||||||
...tokens.map((token) =>
|
...tokens.map((token: any) =>
|
||||||
[...features.keys()].map((feature) => token[feature]).join(",")
|
[...features.keys()].map((feature) => token[feature]).join(",")
|
||||||
),
|
),
|
||||||
].join("\n");
|
].join("\n");
|
||||||
|
|
||||||
module.exports = { useTokenize, toCSV };
|
|
5
src/scripts/utils/nyanco.ts
Normal file
5
src/scripts/utils/nyanco.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import random from "./random";
|
||||||
|
|
||||||
|
const nyans = [":nya-n1:", ":nya-n2:", ":nya-n3:", ":nya-n4:", ":nya-n5:"];
|
||||||
|
|
||||||
|
export default () => random(nyans);
|
1
src/scripts/utils/random.ts
Normal file
1
src/scripts/utils/random.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export default (array: any) => array[Math.floor(Math.random() * array.length)];
|
|
@ -1,4 +1,4 @@
|
||||||
module.exports = async (titles) => {
|
export default async (titles: any) => {
|
||||||
const url = new URL("https://ja.wikipedia.org/w/api.php");
|
const url = new URL("https://ja.wikipedia.org/w/api.php");
|
||||||
const params = new URLSearchParams({
|
const params = new URLSearchParams({
|
||||||
action: "query",
|
action: "query",
|
||||||
|
@ -6,7 +6,7 @@ module.exports = async (titles) => {
|
||||||
prop: "extracts",
|
prop: "extracts",
|
||||||
titles,
|
titles,
|
||||||
redirects: "",
|
redirects: "",
|
||||||
exchars: 120,
|
exchars: "120",
|
||||||
explaintext: "",
|
explaintext: "",
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
|
@ -18,12 +18,13 @@ module.exports = async (titles) => {
|
||||||
if (json == null || json.query == null || json.query.pages == null) {
|
if (json == null || json.query == null || json.query.pages == null) {
|
||||||
throw new Error(["response is invalid", JSON.stringify(json)].join(":"));
|
throw new Error(["response is invalid", JSON.stringify(json)].join(":"));
|
||||||
}
|
}
|
||||||
const pages = new Map(Object.entries(json.query.pages));
|
const pages: Map<string, { extract: string }> = new Map(
|
||||||
|
Object.entries(json.query.pages)
|
||||||
|
);
|
||||||
return pages.size > 0
|
return pages.size > 0
|
||||||
? [...pages.values()].map(({ extract }) => extract).join("\n")
|
? [...pages.values()].map(({ extract }) => extract).join("\n")
|
||||||
: "しらないにゃーん";
|
: "しらないにゃーん";
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
};
|
};
|
|
@ -1,22 +1,20 @@
|
||||||
const subMinutes = require("date-fns/subMinutes");
|
import { subMinutes, roundToNearestMinutes } from "date-fns";
|
||||||
const roundToNearestMinutes = require("date-fns/roundToNearestMinutes");
|
import { format, utcToZonedTime } from "date-fns-tz";
|
||||||
const format = require("date-fns-tz/format");
|
import nyanco from "./utils/nyanco";
|
||||||
const utcToZonedTime = require("date-fns-tz/utcToZonedTime");
|
|
||||||
const nyanco = require("./util/nyanco");
|
|
||||||
|
|
||||||
const timeZone = "Asia/Tokyo";
|
const timeZone = "Asia/Tokyo";
|
||||||
const prefNumber = 34;
|
const prefNumber = 34;
|
||||||
const pageURL = `https://www.tenki.jp/radar/7/${prefNumber}/`;
|
const pageURL = `https://www.tenki.jp/radar/7/${prefNumber}/`;
|
||||||
const imgURL = (target) =>
|
const imgURL = (target: any) =>
|
||||||
[
|
[
|
||||||
"https://static.tenki.jp/static-images/radar/",
|
"https://static.tenki.jp/static-images/radar/",
|
||||||
format(target, "yyyy/MM/dd/HH/mm/ss", { timeZone }),
|
format(target, "yyyy/MM/dd/HH/mm/ss", { timeZone }),
|
||||||
`/pref-${prefNumber}-large.jpg`,
|
`/pref-${prefNumber}-large.jpg`,
|
||||||
].join("");
|
].join("");
|
||||||
|
|
||||||
module.exports = [
|
export default [
|
||||||
/天気/,
|
/天気/,
|
||||||
async ({ say }) => {
|
async ({ say }: any) => {
|
||||||
const target = utcToZonedTime(
|
const target = utcToZonedTime(
|
||||||
roundToNearestMinutes(subMinutes(new Date(), 5), {
|
roundToNearestMinutes(subMinutes(new Date(), 5), {
|
||||||
nearestTo: 5,
|
nearestTo: 5,
|
||||||
|
@ -26,11 +24,9 @@ module.exports = [
|
||||||
|
|
||||||
return say(
|
return say(
|
||||||
[
|
[
|
||||||
`${nyanco()} < ${format(
|
`${nyanco()} < ${format(target, "HH時mm分の雨雲の様子にゃーん", {
|
||||||
target,
|
timeZone,
|
||||||
"HH時mm分の雨雲の様子にゃーん",
|
})}`,
|
||||||
timeZone
|
|
||||||
)}`,
|
|
||||||
imgURL(target),
|
imgURL(target),
|
||||||
pageURL,
|
pageURL,
|
||||||
].join("\n")
|
].join("\n")
|
|
@ -1,5 +1,5 @@
|
||||||
const random = require("./util/random");
|
import random from "./utils/random";
|
||||||
const nyanco = require("./util/nyanco");
|
import nyanco from "./utils/nyanco";
|
||||||
|
|
||||||
const yakitoris = [
|
const yakitoris = [
|
||||||
"―ロ@ロ@ロ- ヤキトリ",
|
"―ロ@ロ@ロ- ヤキトリ",
|
||||||
|
@ -13,8 +13,8 @@ const yakitoris = [
|
||||||
"―>゚)))彡- 魚丸焼き",
|
"―>゚)))彡- 魚丸焼き",
|
||||||
];
|
];
|
||||||
|
|
||||||
module.exports = [
|
export default [
|
||||||
/焼鳥|焼き鳥|やきとり|ヤキトリ|串|プロキシ|プロクシ|proxy|Proxy|PROXY|ピロシキ/,
|
/焼鳥|焼き鳥|やきとり|ヤキトリ|串|プロキシ|プロクシ|proxy|Proxy|PROXY|ピロシキ/,
|
||||||
({ say }) =>
|
({ say }: any) =>
|
||||||
say(`串焼きでも食べるにゃん < ${nyanco()}っ${random(yakitoris)}`),
|
say(`串焼きでも食べるにゃん < ${nyanco()}っ${random(yakitoris)}`),
|
||||||
];
|
];
|
6
vercel.json
Normal file
6
vercel.json
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"functions": {
|
||||||
|
"api/slack/events.ts": { "includeFiles": "{dist,node_modules/kuromoji}/**" }
|
||||||
|
},
|
||||||
|
"build": { "env": { "NODEJS_AWS_HANDLER_NAME": "handler" } }
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue