diff --git a/lib/config.ts b/lib/config.ts index 61feccf..4e361f2 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -1,7 +1,6 @@ import { process591QueryUrl } from "./591House/utils"; const apiSecret = process.env.ZUZUGO_API_SECRET; - const tokenLine = process.env.LINE_API_TOKEN; // const isSubwayStationFilterEnabled = process.env.ENABLE_SUBWAY_STATION_FILTER === "true"; @@ -34,4 +33,9 @@ export const config = { tokenLine, production: process.env.NODE_ENV === "production", cronEnabled: process.env.DISABLE_CRON !== "true", + + line: { + LINE_ACCESS_TOKEN: process.env.LINE_ACCESS_TOKEN, + LINE_CHANNEL_SECRET: process.env.LINE_CHANNEL_SECRET + } }; diff --git a/lib/notification/lineBot.ts b/lib/notification/lineBot.ts new file mode 100644 index 0000000..3aff704 --- /dev/null +++ b/lib/notification/lineBot.ts @@ -0,0 +1,19 @@ +import { Client, TextMessage } from '@line/bot-sdk'; + +import { config } from 'lib/config' +// set Channel Access Token and Channel Secret for Line Bot +const client = new Client({ + channelAccessToken: config.line.LINE_ACCESS_TOKEN || '', + channelSecret: config.line.LINE_CHANNEL_SECRET, +}); + +// @todo: pass rent data from api. +const message: TextMessage = { + type: 'text', + text: 'Zuzugo got a new house!', +}; + +// @todo: get groupId from database. +export default async function sendLineBotMessage(groupId: string) { + await client.pushMessage(groupId, message) +} \ No newline at end of file diff --git a/package.json b/package.json index 7f30e3c..1fe13c8 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ }, "dependencies": { "@next-auth/prisma-adapter": "^1.0.5", + "@line/bot-sdk": "^7.5.2", "@next/font": "13.1.6", "@prisma/client": "^4.10.1", "@seratch_/bolt-http-runner": "^1.0.3", @@ -66,4 +67,4 @@ "engines": { "node": ">=18.12" } -} +} \ No newline at end of file diff --git a/pages/api/line/events.ts b/pages/api/line/events.ts new file mode 100644 index 0000000..4109d76 --- /dev/null +++ b/pages/api/line/events.ts @@ -0,0 +1,35 @@ +import { WebhookEvent, User, Group, Room } from "@line/bot-sdk"; +import type { NextApiRequest, NextApiResponse } from "next"; + +import sendLineBotMessage from 'lib/notification/lineBot' + + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + + if (req.method !== "POST") { + res.status(405).json({ error: "Sorry! This endpoint does not accept your requests." }); + return; + } + + const events: WebhookEvent[] = req.body.events; + const [firstEvent] = events; + + if (firstEvent.type === 'join') { + const { + userId = '', + groupId = '', + roomId = '', + // @todo: save chatType to database. + // type: chatType, + } = firstEvent.source as User & Group & Room; + + const chatId = userId || groupId || roomId; + try { + await sendLineBotMessage(chatId) + } catch (e) { + console.error(e) + } + } + + res.send(200) +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2c5d53c..17b88bc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,6 +7,7 @@ patchedDependencies: specifiers: '@faker-js/faker': ^7.6.0 + '@line/bot-sdk': ^7.5.2 '@next-auth/prisma-adapter': ^1.0.5 '@next/font': 13.1.6 '@prisma/client': ^4.10.1 @@ -49,6 +50,7 @@ specifiers: zod: ^3.20.6 dependencies: + '@line/bot-sdk': 7.5.2 '@next-auth/prisma-adapter': 1.0.5_qpmskah7lm3ildf4stmwh4q42u '@next/font': 13.1.6 '@prisma/client': 4.10.1_prisma@4.10.1 @@ -761,6 +763,21 @@ packages: '@jridgewell/sourcemap-codec': 1.4.14 dev: true + /@line/bot-sdk/7.5.2: + resolution: {integrity: sha512-mMaDnr+mOqQDLYJcUp+fQwZklg/LoOZzNILlWdsj2IFD2nXF+HhAm3KEy5tyUx629Y2bCx6nv9Jl0UlMwBiAiw==} + engines: {node: '>=10'} + dependencies: + '@types/body-parser': 1.19.2 + '@types/node': 16.18.13 + axios: 0.27.2 + body-parser: 1.20.1 + file-type: 16.5.4 + form-data: 4.0.0 + transitivePeerDependencies: + - debug + - supports-color + dev: false + /@mswjs/cookies/0.2.2: resolution: {integrity: sha512-mlN83YSrcFgk7Dm1Mys40DLssI1KdJji2CMKN8eOlBqsTADYzj2+jWzsANsUTFbxDMWPD5e9bfA1RGqBpS3O1g==} engines: {node: '>=14'} @@ -769,8 +786,8 @@ packages: set-cookie-parser: 2.5.1 dev: true - /@mswjs/interceptors/0.17.8: - resolution: {integrity: sha512-hjS5dy8u+Baa5r/SVAQyWZYQr8YZzpeGldNXJlZQBPjt7FQL7Acd1BnXJWmIhl62s5uh0WJNAcUMfAAcnqTchA==} + /@mswjs/interceptors/0.17.9: + resolution: {integrity: sha512-4LVGt03RobMH/7ZrbHqRxQrS9cc2uh+iNKSj8UWr8M26A2i793ju+csaB5zaqYltqJmA2jUq4VeYfKmVqvsXQg==} engines: {node: '>=14'} dependencies: '@open-draft/until': 1.0.3 @@ -1175,6 +1192,10 @@ packages: defer-to-connect: 2.0.1 dev: false + /@tokenizer/token/0.3.0: + resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} + dev: false + /@total-typescript/ts-reset/0.3.7: resolution: {integrity: sha512-yXt2BRRVCJVvzWaxac5n0nCXzIrQEBE/MeYlNQ8/Iq7UeelNmm/AdnUAu18ilSS893mbEQ4u6whPt/HvOPc4rw==} dev: true @@ -1329,6 +1350,10 @@ packages: resolution: {integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==} dev: true + /@types/node/16.18.13: + resolution: {integrity: sha512-l0/3XZ153UTlNOnZK8xSNoJlQda9/WnYgiTdcKKPJSZjdjI9MU+A9oMXOesAWLSnqAaaJhj3qfQsU07Dr8OUwg==} + dev: false + /@types/node/17.0.45: resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==} dev: false @@ -3098,6 +3123,15 @@ packages: dependencies: flat-cache: 3.0.4 + /file-type/16.5.4: + resolution: {integrity: sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==} + engines: {node: '>=10'} + dependencies: + readable-web-to-node-stream: 3.0.2 + strtok3: 6.3.0 + token-types: 4.2.1 + dev: false + /fill-range/7.0.1: resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} engines: {node: '>=8'} @@ -4629,7 +4663,7 @@ packages: optional: true dependencies: '@mswjs/cookies': 0.2.2 - '@mswjs/interceptors': 0.17.8 + '@mswjs/interceptors': 0.17.9 '@open-draft/until': 1.0.3 '@types/cookie': 0.4.1 '@types/js-levenshtein': 1.1.1 @@ -5065,6 +5099,11 @@ packages: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} + /peek-readable/4.1.0: + resolution: {integrity: sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==} + engines: {node: '>=8'} + dev: false + /picocolors/1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} @@ -5280,7 +5319,13 @@ packages: inherits: 2.0.4 string_decoder: 1.1.1 util-deprecate: 1.0.2 - dev: true + + /readable-web-to-node-stream/3.0.2: + resolution: {integrity: sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==} + engines: {node: '>=8'} + dependencies: + readable-stream: 3.6.1 + dev: false /readdirp/3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} @@ -5410,7 +5455,6 @@ packages: /safe-buffer/5.1.2: resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} - dev: true /safe-buffer/5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} @@ -5628,7 +5672,6 @@ packages: resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} dependencies: safe-buffer: 5.1.2 - dev: true /strip-ansi/6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} @@ -5654,6 +5697,14 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + /strtok3/6.3.0: + resolution: {integrity: sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==} + engines: {node: '>=10'} + dependencies: + '@tokenizer/token': 0.3.0 + peek-readable: 4.1.0 + dev: false + /styled-jsx/5.1.1_react@18.2.0: resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==} engines: {node: '>= 12.0.0'} @@ -5770,6 +5821,14 @@ packages: engines: {node: '>=0.6'} dev: false + /token-types/4.2.1: + resolution: {integrity: sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==} + engines: {node: '>=10'} + dependencies: + '@tokenizer/token': 0.3.0 + ieee754: 1.2.1 + dev: false + /tough-cookie/4.1.2: resolution: {integrity: sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==} engines: {node: '>=6'} @@ -5995,7 +6054,6 @@ packages: /util-deprecate/1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - dev: true /util/0.12.5: resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==}