├── .github └── workflows │ └── go.yml ├── .gitignore ├── Dockerfile ├── LICENSE ├── Procfile ├── README.md ├── README_en.md ├── app.json ├── bot.go ├── chatgpt.go ├── data.go ├── dataMem.go ├── datapgsql.go ├── go.mod ├── go.sum ├── img ├── .DS_Store ├── chat_1.png ├── list_all.png └── sum_all.png ├── main.go └── render.yaml /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | 16 | - name: Set up Go 17 | uses: actions/setup-go@v3 18 | with: 19 | go-version-file: 'go.mod' 20 | 21 | - name: Build 22 | run: go build -v ./... 23 | 24 | - name: Test 25 | run: go test -v ./... 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | LINE-Bot-ChatSummarizer 2 | ### core template 3 | *.swp 4 | *.*~ 5 | project.lock.json 6 | .DS_Store 7 | *.pyc 8 | nupkg/ 9 | 10 | # Visual Studio Code 11 | .vscode 12 | 13 | # Rider 14 | .idea 15 | 16 | # User-specific files 17 | *.suo 18 | *.user 19 | *.userosscache 20 | *.sln.docstates 21 | 22 | # Build results 23 | [Dd]ebug/ 24 | [Dd]ebugPublic/ 25 | [Rr]elease/ 26 | [Rr]eleases/ 27 | x64/ 28 | x86/ 29 | build/ 30 | bld/ 31 | [Bb]in/ 32 | [Oo]bj/ 33 | [Oo]ut/ 34 | msbuild.log 35 | msbuild.err 36 | msbuild.wrn 37 | 38 | # Visual Studio 2015 39 | .vs/ 40 | 41 | 42 | ### VisualStudioCode template 43 | .vscode/* 44 | !.vscode/settings.json 45 | !.vscode/tasks.json 46 | !.vscode/launch.json 47 | !.vscode/extensions.json 48 | !.vscode/*.code-snippets 49 | 50 | # Local History for Visual Studio Code 51 | .history/ 52 | 53 | # Built Visual Studio Code Extensions 54 | *.vsix 55 | 56 | ### JetBrains template 57 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 58 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 59 | 60 | # User-specific stuff 61 | .idea/**/workspace.xml 62 | .idea/**/tasks.xml 63 | .idea/**/usage.statistics.xml 64 | .idea/**/dictionaries 65 | .idea/**/shelf 66 | 67 | # AWS User-specific 68 | .idea/**/aws.xml 69 | 70 | # Generated files 71 | .idea/**/contentModel.xml 72 | 73 | # Sensitive or high-churn files 74 | .idea/**/dataSources/ 75 | .idea/**/dataSources.ids 76 | .idea/**/dataSources.local.xml 77 | .idea/**/sqlDataSources.xml 78 | .idea/**/dynamic.xml 79 | .idea/**/uiDesigner.xml 80 | .idea/**/dbnavigator.xml 81 | 82 | # Gradle 83 | .idea/**/gradle.xml 84 | .idea/**/libraries 85 | 86 | # Gradle and Maven with auto-import 87 | # When using Gradle or Maven with auto-import, you should exclude module files, 88 | # since they will be recreated, and may cause churn. Uncomment if using 89 | # auto-import. 90 | # .idea/artifacts 91 | # .idea/compiler.xml 92 | # .idea/jarRepositories.xml 93 | # .idea/modules.xml 94 | # .idea/*.iml 95 | # .idea/modules 96 | # *.iml 97 | # *.ipr 98 | 99 | # CMake 100 | cmake-build-*/ 101 | 102 | # Mongo Explorer plugin 103 | .idea/**/mongoSettings.xml 104 | 105 | # File-based project format 106 | *.iws 107 | 108 | # IntelliJ 109 | out/ 110 | 111 | # mpeltonen/sbt-idea plugin 112 | .idea_modules/ 113 | 114 | # JIRA plugin 115 | atlassian-ide-plugin.xml 116 | 117 | # Cursive Clojure plugin 118 | .idea/replstate.xml 119 | 120 | # SonarLint plugin 121 | .idea/sonarlint/ 122 | 123 | # Crashlytics plugin (for Android Studio and IntelliJ) 124 | com_crashlytics_export_strings.xml 125 | crashlytics.properties 126 | crashlytics-build.properties 127 | fabric.properties 128 | 129 | # Editor-based Rest Client 130 | .idea/httpRequests 131 | 132 | # Android studio 3.1+ serialized cache file 133 | .idea/caches/build_file_checksums.ser 134 | 135 | ### Linux template 136 | *~ 137 | 138 | # temporary files which can be created if a process still has a handle open of a deleted file 139 | .fuse_hidden* 140 | 141 | # KDE directory preferences 142 | .directory 143 | 144 | # Linux trash folder which might appear on any partition or disk 145 | .Trash-* 146 | 147 | # .nfs files are created when an open file is removed but is still being accessed 148 | .nfs* 149 | 150 | ### Go template 151 | # If you prefer the allow list template instead of the deny list, see community template: 152 | # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore 153 | # 154 | # Binaries for programs and plugins 155 | *.exe 156 | *.exe~ 157 | *.dll 158 | *.so 159 | *.dylib 160 | 161 | # Test binary, built with `go test -c` 162 | *.test 163 | 164 | # Output of the go coverage tool, specifically when used with LiteIDE 165 | *.out 166 | 167 | # Dependency directories (remove the comment below to include it) 168 | vendor/ 169 | 170 | # Go workspace file 171 | go.work 172 | 173 | ### Go.AllowList template 174 | # Allowlisting gitignore template for GO projects prevents us 175 | # from adding various unwanted local files, such as generated 176 | # files, developer configurations or IDE-specific files etc. 177 | # 178 | # Recommended: Go.AllowList.gitignore 179 | 180 | # Ignore everything 181 | * 182 | 183 | # But not these files... 184 | !/.gitignore 185 | 186 | !*.go 187 | !go.sum 188 | !go.mod 189 | 190 | !README.md 191 | !LICENSE 192 | 193 | # !Makefile 194 | 195 | # ...even if they are in subdirectories 196 | !*/ 197 | 198 | ### Windows template 199 | # Windows thumbnail cache files 200 | Thumbs.db 201 | Thumbs.db:encryptable 202 | ehthumbs.db 203 | ehthumbs_vista.db 204 | 205 | # Dump file 206 | *.stackdump 207 | 208 | # Folder config file 209 | [Dd]esktop.ini 210 | 211 | # Recycle Bin used on file shares 212 | $RECYCLE.BIN/ 213 | 214 | # Windows Installer files 215 | *.cab 216 | *.msi 217 | *.msix 218 | *.msm 219 | *.msp 220 | 221 | # Windows shortcuts 222 | *.lnk 223 | 224 | ### Vim template 225 | # Swap 226 | [._]*.s[a-v][a-z] 227 | !*.svg # comment out if you don't need vector files 228 | [._]*.sw[a-p] 229 | [._]s[a-rt-v][a-z] 230 | [._]ss[a-gi-z] 231 | [._]sw[a-p] 232 | 233 | # Session 234 | Session.vim 235 | Sessionx.vim 236 | 237 | # Temporary 238 | .netrwhist 239 | # Auto-generated tag files 240 | tags 241 | # Persistent undo 242 | [._]*.un~ 243 | 244 | ### macOS template 245 | # General 246 | .AppleDouble 247 | .LSOverride 248 | 249 | # Icon must end with two \r 250 | Icon 251 | 252 | # Thumbnails 253 | ._* 254 | 255 | # Files that might appear in the root of a volume 256 | .DocumentRevisions-V100 257 | .fseventsd 258 | .Spotlight-V100 259 | .TemporaryItems 260 | .Trashes 261 | .VolumeIcon.icns 262 | .com.apple.timemachine.donotpresent 263 | 264 | # Directories potentially created on remote AFP share 265 | .AppleDB 266 | .AppleDesktop 267 | Network Trash Folder 268 | Temporary Items 269 | .apdisk 270 | 271 | ### OpenSSL template 272 | # OpenSSL-related files best not committed 273 | 274 | ## Certificate Authority 275 | *.ca 276 | 277 | ## Certificate 278 | *.crt 279 | 280 | ## Certificate Sign Request 281 | *.csr 282 | 283 | ## Certificate 284 | *.der 285 | 286 | ## Key database file 287 | *.kdb 288 | 289 | ## OSCP request data 290 | *.org 291 | 292 | ## PKCS #12 293 | *.p12 294 | 295 | ## PEM-encoded certificate data 296 | *.pem 297 | 298 | ## Random number seed 299 | *.rnd 300 | 301 | ## SSLeay data 302 | *.ssleay 303 | 304 | ## S/MIME message 305 | *.smime 306 | 307 | ## Docker files 308 | !Dockerfile 309 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.20-alpine AS builder 2 | 3 | # Set the Current Working Directory inside the container 4 | WORKDIR /app 5 | 6 | # Copy everything from the current directory to the PWD(Present Working Directory) inside the container 7 | COPY . . 8 | 9 | # Download all the dependencies 10 | RUN go mod download 11 | 12 | # Build the Go app 13 | RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main . 14 | 15 | # Start fresh from a smaller image 16 | FROM alpine:latest 17 | 18 | # Copy the Pre-built binary file from the previous stage 19 | COPY --from=builder /app/main /app/main 20 | 21 | # Command to run the executable 22 | CMD ["/app/main"] 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: LINE-Bot-ChatSummarizer 2 | 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | LINE Bot 聊天摘要生成器:使用 ChatGPT 將你的群聊作為聊天摘要生成器,與 LINE Bot 聊天摘要生成器一起使用 2 | ============== 3 | 4 | [![Join the chat at https://gitter.im/kkdai/LINE-Bot-ChatSummarizer](https://badges.gitter.im/kkdai/LINE-Bot-ChatSummarizer.svg)](https://gitter.im/kkdai/LINE-Bot-ChatSummarizer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![GoDoc](https://godoc.org/github.com/kkdai/LINE-Bot-ChatSummarizer.svg?status.svg)](https://godoc.org/github.com/kkdai/LINE-Bot-ChatSummarizer) ![Go](https://github.com/kkdai/LINE-Bot-ChatSummarizer/workflows/Go/badge.svg) [![goreportcard.com](https://goreportcard.com/badge/github.com/kkdai/LINE-Bot-ChatSummarizer)](https://goreportcard.com/report/github.com/kkdai/LineBotTemplate) 5 | 6 | English version, please check [ENGLISH Version](README_en.md) 7 | 8 | 使用方式 9 | ============= 10 | 11 | - 依照「如何安裝」進行相關的部署流程。 12 | 13 | - 將機器人加入群組聊天室。 14 | 15 | ![](img/chat_1.png) 16 | 17 | - 能夠記住群組內的對話。 18 | 19 | ![](img/list_all.png) 20 | 21 | - 直接送群組內容摘要私訊給你。 22 | ![](img/sum_all.png) 23 | 24 | ### 相關指令如下 25 | 26 | - `:gpt xxx`: 直接對 ChatGPT 來對談,可以直接問他。 27 | 28 | - `:draw xxx`: 請他根據你輸入的文字來畫圖給你。 29 | 30 | - `:list_all`: 列出群組的訊息紀錄(all) 31 | 32 | - `:sum_all`: 幫你做訊息摘要。 33 | 34 | ### 相關限制 35 | 36 | 目前該 LINE Bot 原始程式碼還沒有綁定相關資料庫,所以都是使用記憶體來儲存相關對話紀錄。可能重開機(隔天)會有忘記的,或是超過就無法儲存的缺點。 37 | 38 | 如何安裝 39 | ============= 40 | 41 | ### 獲取 LINE Bot API 開發者帳戶 42 | 43 | 如果你想使用 LINE Bot,請確保在 註冊 LINE 開發者控制台。 44 | 45 | 在「基本設定」選項卡上創建新的消息通道並獲取「Channel Secret」。 46 | 47 | 在「Messiging API」選項卡上獲取「Channel Access Token」。 48 | 49 | 從「基本設定」選項卡中打開 LINE OA 管理器,然後轉到 OA 管理器的回復設定。在那裡啟用「webhook」。 50 | 51 | ### 獲取 OpenAI API Token 52 | 53 | 在 註冊帳戶。 54 | 55 | 一旦你有了帳戶,就可以在帳戶設定頁面找到你的 API Access Token。 56 | 57 | 如果你想在開發中使用 OpenAI API,你可以在 API 文檔頁面中找到更多信息和說明。 58 | 59 | 請注意,OpenAI API 只面向滿足某些條件的用戶開放。你可以在 API 文件頁面中找到有關 API 的使用條件和限制的更多信息。 60 | 61 | ### 部署在不同的 Web Platform 62 | 63 | 你可以選擇部署在 [Heroku](https://www.heroku.com/) 或是 [Render](http://render.com/) 64 | 65 | #### 部署在 Heroku 66 | 67 | [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy) 68 | 69 | - 輸入 `Channel Secret` and `Channel Access Token` 還有 `ChatGptToken` . 70 | 71 | #### 部署在 Rener 72 | 73 | [![Deploy to Render](http://render.com/images/deploy-to-render-button.svg)](https://render.com/deploy) 74 | 75 | - 輸入 `Channel Secret` and `Channel Access Token` 還有 `ChatGptToken` . 76 | 77 | ### 部署不同資料庫 78 | 79 | ### PostGresSQL 80 | 81 | 請記得在參數裡面加上 `DATABASE_URL` DB 的連接網址。 82 | 如果留空的話,將會使用記憶體當資料庫。 83 | 84 | ### 接下來 85 | 86 | 記住你的 Web Platform 伺服器 ID。 87 | 88 | 在 LINE Bot 儀表板中設置基本 API: 89 | 90 | 設置你的基本帳戶信息,包括「Webhook URL」在 。 91 | 92 | 這就是它!你完成了。 93 | 94 | License 95 | --------------- 96 | 97 | Licensed under the Apache License, Version 2.0 (the "License"); 98 | you may not use this file except in compliance with the License. 99 | You may obtain a copy of the License at 100 | 101 | 102 | 103 | Unless required by applicable law or agreed to in writing, software 104 | distributed under the License is distributed on an "AS IS" BASIS, 105 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 106 | See the License for the specific language governing permissions and 107 | limitations under the License. 108 | -------------------------------------------------------------------------------- /README_en.md: -------------------------------------------------------------------------------- 1 | LINE Bot Chat Summarizer: Use ChatGPT to summarize your group chat as a chat summarizer with the LINE Bot Chat Summarizer 2 | ============== 3 | 4 | [![Join the chat at https://gitter.im/kkdai/LINE-Bot-ChatSummarizer](https://badges.gitter.im/kkdai/LINE-Bot-ChatSummarizer.svg)](https://gitter.im/kkdai/LINE-Bot-ChatSummarizer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![GoDoc](https://godoc.org/github.com/kkdai/LINE-Bot-ChatSummarizer.svg?status.svg)](https://godoc.org/github.com/kkdai/LINE-Bot-ChatSummarizer) ![Go](https://github.com/kkdai/LINE-Bot-ChatSummarizer/workflows/Go/badge.svg) [![goreportcard.com](https://goreportcard.com/badge/github.com/kkdai/LINE-Bot-ChatSummarizer)](https://goreportcard.com/report/github.com/kkdai/LineBotTemplate) 5 | 6 | 中文 version, please check [Chinese Version](README.md) 7 | 8 | Usage 9 | ============= 10 | 11 | - Follow the deployment process in "How to install". 12 | 13 | - Add the robot to the group chat room. 14 | 15 | ![](img/chat_1.png) 16 | 17 | - Be able to remember the conversation in the group. 18 | 19 | ![](img/list_all.png) 20 | 21 | - Send a summary of the group's content privately to you. 22 | 23 | ![](img/sum_all.png) 24 | 25 | Related commands 26 | ============= 27 | 28 | - `:gpt xxx`: Chat directly with ChatGPT, you can ask him directly. 29 | 30 | - `:list_all`: List the message records (all) in the group 31 | 32 | - `:sum_all`: Do a message summary for you. 33 | 34 | Related limitations 35 | Currently, the original code of this LINE Bot is not bound to a database, so all conversation records are stored in memory. There may be drawbacks such as forgetting or being unable to store after a restart (the next day) or exceeding the limit. 36 | How to use this 37 | ============= 38 | 39 | ### To obtain a LINE Bot API developer account 40 | 41 | Make sure you are registered on the LINE developer console at if you want to use a LINE Bot. 42 | 43 | Create a new Messaging Channel and get the "Channel Secret" on the "Basic Setting" tab. 44 | 45 | Issue a "Channel Access Token" on the "Messaging API" tab. 46 | 47 | Open the LINE OA manager from the "Basic Setting" tab and go to the Reply setting on the OA manager. Enable "webhook" there. 48 | 49 | ### To obtain an OpenAI API token 50 | 51 | Register for an account on the OpenAI website at . 52 | 53 | Once you have an account, you can find your API token in the account settings page. 54 | 55 | If you want to use the OpenAI API for development, you can find more information and instructions in the API documentation page. 56 | 57 | Please note that the OpenAI API is only available to users who meet certain criteria. You can find more information about the usage conditions and limitations of the API in the API documentation page. 58 | 59 | ### Deploy this on Web Platform 60 | 61 | You can choose [Heroku](https://www.heroku.com/) or [Render](http://render.com/) 62 | 63 | #### Deploy this on Heroku 64 | 65 | [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy) 66 | 67 | - Input `Channel Secret` and `Channel Access Token` and `ChatGptToken`. 68 | 69 | #### Deploy this on Rener 70 | 71 | [![Deploy to Render](http://render.com/images/deploy-to-render-button.svg)](https://render.com/deploy) 72 | 73 | - Input `Channel Secret` and `Channel Access Token` and `ChatGptToken`. 74 | 75 | ### Deploy to different DB 76 | 77 | ### PostGresSQL 78 | 79 | Add `DATABASE_URL` setting with database connection parameters. 80 | Left empty if you want use memory as your database. 81 | 82 | ### Update LINE Bot 83 | 84 | Remember your Web Platform server ID. 85 | 86 | To set up the basic API in the LINE Bot Dashboard: 87 | 88 | Set up your basic account information, including the "Callback URL" at . 89 | That's it! You're done. 90 | 91 | License 92 | --------------- 93 | 94 | Licensed under the Apache License, Version 2.0 (the "License"); 95 | you may not use this file except in compliance with the License. 96 | You may obtain a copy of the License at 97 | 98 | 99 | 100 | Unless required by applicable law or agreed to in writing, software 101 | distributed under the License is distributed on an "AS IS" BASIS, 102 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 103 | See the License for the specific language governing permissions and 104 | limitations under the License. 105 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "LINE Bot Chat Summarizer by ChatGPT", 3 | "description": "Using ChatGPT API to auto summarize your Group Chat", 4 | "repository": "https://github.com/kkdai/LINE-Bot-ChatSummarizer", 5 | "keywords": [ 6 | "Line", 7 | "go", 8 | "static" 9 | ], 10 | "buildpacks": [ 11 | { 12 | "url": "https://github.com/kr/heroku-buildpack-go.git" 13 | }, 14 | { 15 | "url": "heroku/go" 16 | } 17 | ], 18 | "env": { 19 | "ChatGptToken": { 20 | "description": "ChatGPT Access Token", 21 | "required": true 22 | }, 23 | "ChannelAccessToken": { 24 | "description": "Channel Access Token", 25 | "required": true 26 | }, 27 | "ChannelSecret": { 28 | "description": "Channel Secret", 29 | "required": true 30 | }, 31 | "DATABASE_URL": { 32 | "description": "PostGresSQL DB url, leave empty if you want to use memory DB.", 33 | "required": false 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /bot.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | "strings" 8 | "time" 9 | 10 | "github.com/line/line-bot-sdk-go/v7/linebot" 11 | ) 12 | 13 | func callbackHandler(w http.ResponseWriter, r *http.Request) { 14 | events, err := bot.ParseRequest(r) 15 | 16 | if err != nil { 17 | if err == linebot.ErrInvalidSignature { 18 | w.WriteHeader(http.StatusBadRequest) 19 | } else { 20 | w.WriteHeader(http.StatusInternalServerError) 21 | } 22 | return 23 | } 24 | 25 | for _, event := range events { 26 | if event.Type == linebot.EventTypeMessage { 27 | switch message := event.Message.(type) { 28 | // Handle only on text message 29 | case *linebot.TextMessage: 30 | // Directly to ChatGPT 31 | if strings.Contains(message.Text, ":gpt") { 32 | // New feature. 33 | if IsRedemptionEnabled() { 34 | if stickerRedeemable { 35 | handleGPT(GPT_Complete, event, message.Text) 36 | stickerRedeemable = false 37 | } else { 38 | handleRedeemRequestMsg(event) 39 | } 40 | } else { 41 | // Original one 42 | handleGPT(GPT_Complete, event, message.Text) 43 | } 44 | } else if strings.Contains(message.Text, ":gpt4") { 45 | // New feature. 46 | if IsRedemptionEnabled() { 47 | if stickerRedeemable { 48 | handleGPT(GPT_GPT4_Complete, event, message.Text) 49 | stickerRedeemable = false 50 | } else { 51 | handleRedeemRequestMsg(event) 52 | } 53 | } else { 54 | // Original one 55 | handleGPT(GPT_GPT4_Complete, event, message.Text) 56 | } 57 | } else if strings.Contains(message.Text, ":draw") { 58 | // New feature. 59 | if IsRedemptionEnabled() { 60 | if stickerRedeemable { 61 | handleGPT(GPT_Draw, event, message.Text) 62 | stickerRedeemable = false 63 | } else { 64 | handleRedeemRequestMsg(event) 65 | } 66 | } else { 67 | // Original one 68 | handleGPT(GPT_Draw, event, message.Text) 69 | } 70 | } else if strings.EqualFold(message.Text, ":list_all") && isGroupEvent(event) { 71 | handleListAll(event) 72 | } else if strings.EqualFold(message.Text, ":sum_all") && isGroupEvent(event) { 73 | handleSumAll(event) 74 | } else if isGroupEvent(event) { 75 | // 如果聊天機器人在群組中,開始儲存訊息。 76 | handleStoreMsg(event, message.Text) 77 | } 78 | 79 | // Handle only on Sticker message 80 | case *linebot.StickerMessage: 81 | var kw string 82 | for _, k := range message.Keywords { 83 | kw = kw + "," + k 84 | } 85 | 86 | log.Println("Sticker: PID=", message.PackageID, " SID=", message.StickerID) 87 | if IsRedemptionEnabled() { 88 | if message.PackageID == RedeemStickerPID && message.StickerID == RedeemStickerSID { 89 | stickerRedeemable = true 90 | if _, err = bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage("你的賦能功能啟動了!")).Do(); err != nil { 91 | log.Print(err) 92 | } 93 | } 94 | } 95 | 96 | if isGroupEvent(event) { 97 | // 在群組中,一樣紀錄起來不回覆。 98 | outStickerResult := fmt.Sprintf("貼圖訊息: %s ", kw) 99 | handleStoreMsg(event, outStickerResult) 100 | } else { 101 | outStickerResult := fmt.Sprintf("貼圖訊息: %s, pkg: %s kw: %s text: %s", message.StickerID, message.PackageID, kw, message.Text) 102 | 103 | // 1 on 1 就回覆 104 | if _, err = bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage(outStickerResult)).Do(); err != nil { 105 | log.Print(err) 106 | } 107 | } 108 | } 109 | } 110 | } 111 | } 112 | 113 | func handleSumAll(event *linebot.Event) { 114 | // Scroll through all the messages in the chat group (in chronological order). 115 | oriContext := "" 116 | q := summaryQueue.ReadGroupInfo(getGroupID(event)) 117 | for _, m := range q { 118 | // [xxx]: 他講了什麼... 時間 119 | oriContext = oriContext + fmt.Sprintf("[%s]: %s . %s\n", m.UserName, m.MsgText, m.Time.Local().UTC().Format("2006-01-02 15:04:05")) 120 | } 121 | 122 | // 取得使用者暱稱 123 | userName := event.Source.UserID 124 | userProfile, err := bot.GetProfile(event.Source.UserID).Do() 125 | if err == nil { 126 | userName = userProfile.DisplayName 127 | } 128 | 129 | // 訊息內先回,再來總結。 130 | if _, err = bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage("好的,總結文字已經發給您了"+userName)).Do(); err != nil { 131 | log.Print(err) 132 | } 133 | 134 | // 就是請 ChatGPT 幫你總結 135 | oriContext = fmt.Sprintf("幫我總結 `%s`", oriContext) 136 | reply := gptGPT3CompleteContext(oriContext) 137 | 138 | // 因為 ChatGPT 可能會很慢,所以這邊後來用 SendMsg 來發送私訊給使用者。 139 | if _, err = bot.PushMessage(event.Source.UserID, linebot.NewTextMessage(reply)).Do(); err != nil { 140 | log.Print(err) 141 | } 142 | } 143 | 144 | func handleListAll(event *linebot.Event) { 145 | reply := "" 146 | q := summaryQueue.ReadGroupInfo(getGroupID(event)) 147 | for _, m := range q { 148 | reply = reply + fmt.Sprintf("[%s]: %s . %s\n", m.UserName, m.MsgText, m.Time.Local().UTC().Format("2006-01-02 15:04:05")) 149 | } 150 | 151 | if _, err := bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage(reply)).Do(); err != nil { 152 | log.Print(err) 153 | } 154 | } 155 | 156 | func handleGPT(action GPT_ACTIONS, event *linebot.Event, message string) { 157 | switch action { 158 | case GPT_Complete: 159 | reply := gptGPT3CompleteContext(message) 160 | if _, err := bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage(reply)).Do(); err != nil { 161 | log.Print(err) 162 | } 163 | case GPT_GPT4_Complete: 164 | reply := gptGPT4CompleteContext(message) 165 | if _, err := bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage(reply)).Do(); err != nil { 166 | log.Print(err) 167 | } 168 | case GPT_Draw: 169 | if reply, err := gptImageCreate(message); err != nil { 170 | if _, err := bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage("無法正確顯示圖形.")).Do(); err != nil { 171 | log.Print(err) 172 | } 173 | } else { 174 | if _, err := bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage("根據你的提示,畫出以下圖片:"), linebot.NewImageMessage(reply, reply)).Do(); err != nil { 175 | log.Print(err) 176 | } 177 | } 178 | } 179 | 180 | } 181 | 182 | func handleRedeemRequestMsg(event *linebot.Event) { 183 | // First, obtain the user's Display Name (i.e., the name displayed). 184 | userName := event.Source.UserID 185 | userProfile, err := bot.GetProfile(event.Source.UserID).Do() 186 | if err == nil { 187 | userName = userProfile.DisplayName 188 | } 189 | 190 | if _, err := bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage(userName+":你需要買貼圖,開啟這個功能"), linebot.NewStickerMessage(RedeemStickerPID, RedeemStickerSID)).Do(); err != nil { 191 | log.Print(err) 192 | } 193 | } 194 | 195 | func handleStoreMsg(event *linebot.Event, message string) { 196 | // Get user display name. (It is nick name of the user define.) 197 | userName := event.Source.UserID 198 | userProfile, err := bot.GetProfile(event.Source.UserID).Do() 199 | if err == nil { 200 | userName = userProfile.DisplayName 201 | } 202 | 203 | // event.Source.GroupID 就是聊天群組的 ID,並且透過聊天群組的 ID 來放入 Map 之中。 204 | m := MsgDetail{ 205 | MsgText: message, 206 | UserName: userName, 207 | Time: time.Now(), 208 | } 209 | summaryQueue.AppendGroupInfo(getGroupID(event), m) 210 | } 211 | 212 | func isGroupEvent(event *linebot.Event) bool { 213 | return event.Source.GroupID != "" || event.Source.RoomID != "" 214 | } 215 | 216 | func getGroupID(event *linebot.Event) string { 217 | if event.Source.GroupID != "" { 218 | return event.Source.GroupID 219 | } else if event.Source.RoomID != "" { 220 | return event.Source.RoomID 221 | } 222 | 223 | return "" 224 | } 225 | -------------------------------------------------------------------------------- /chatgpt.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | 8 | "github.com/sashabaranov/go-openai" 9 | ) 10 | 11 | // gptGPT3CompleteContext: Call GPT3.5 API 12 | func gptGPT3CompleteContext(ori string) (ret string) { 13 | fmt.Printf("Using GPT3 Complete") 14 | return gptCompleteContext(ori, openai.GPT3Dot5Turbo) 15 | } 16 | 17 | // gptGPT4CompleteContext: Call GPT4 API 18 | func gptGPT4CompleteContext(ori string) (ret string) { 19 | fmt.Printf("Using GPT4 Complete") 20 | return gptCompleteContext(ori, openai.GPT4) 21 | } 22 | 23 | func gptCompleteContext(ori string, model string) (ret string) { 24 | // Get the context. 25 | ctx := context.Background() 26 | 27 | // For more details about the API of Open AI Chat Completion: https://platform.openai.com/docs/guides/chat 28 | req := openai.ChatCompletionRequest{ 29 | Model: model, 30 | // The message to complete. 31 | Messages: []openai.ChatCompletionMessage{{ 32 | Role: openai.ChatMessageRoleUser, 33 | Content: ori, 34 | }}, 35 | } 36 | 37 | resp, err := client.CreateChatCompletion(ctx, req) 38 | if err != nil { 39 | ret = fmt.Sprintf("Err: %v", err) 40 | } else { 41 | // The response contains a list of choices, each with a score. 42 | // The score is a float between 0 and 1, with 1 being the most likely. 43 | // The choices are sorted by score, with the first choice being the most likely. 44 | // So we just take the first choice. 45 | ret = resp.Choices[0].Message.Content 46 | } 47 | 48 | return ret 49 | } 50 | 51 | // Create image by DALL-E 2 52 | func gptImageCreate(prompt string) (string, error) { 53 | ctx := context.Background() 54 | 55 | // Sample image by link 56 | reqUrl := openai.ImageRequest{ 57 | Prompt: prompt, 58 | Size: openai.CreateImageSize512x512, 59 | ResponseFormat: openai.CreateImageResponseFormatURL, 60 | N: 1, 61 | } 62 | 63 | respUrl, err := client.CreateImage(ctx, reqUrl) 64 | if err != nil { 65 | fmt.Printf("Image creation error: %v\n", err) 66 | return "", errors.New("Image creation error") 67 | } 68 | fmt.Println(respUrl.Data[0].URL) 69 | 70 | return respUrl.Data[0].URL, nil 71 | } 72 | -------------------------------------------------------------------------------- /data.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "time" 4 | 5 | type GroupDB interface { 6 | ReadGroupInfo(string) GroupData 7 | AppendGroupInfo(string, MsgDetail) 8 | } 9 | type MsgDetail struct { 10 | MsgText string 11 | UserName string 12 | Time time.Time 13 | } 14 | 15 | type GroupData []MsgDetail 16 | -------------------------------------------------------------------------------- /dataMem.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type MemStorage map[string]GroupData 4 | 5 | type MemDB struct { 6 | db MemStorage 7 | } 8 | 9 | func (mdb *MemDB) ReadGroupInfo(roomID string) GroupData { 10 | return mdb.db[roomID] 11 | } 12 | 13 | func (mdb *MemDB) AppendGroupInfo(roomID string, m MsgDetail) { 14 | mdb.db[roomID] = append(mdb.db[roomID], m) 15 | } 16 | 17 | func NewMemDB() *MemDB { 18 | return &MemDB{ 19 | db: make(MemStorage), 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /datapgsql.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/go-pg/pg/v10" 7 | "github.com/go-pg/pg/v10/orm" 8 | ) 9 | 10 | type PGSqlDB struct { 11 | Db *pg.DB 12 | } 13 | 14 | func (mdb *PGSqlDB) ReadGroupInfo(roomID string) GroupData { 15 | pgsql := &DBStorage{ 16 | RoomID: roomID, 17 | } 18 | if ret, err := pgsql.Get(mdb); err == nil { 19 | return ret.Dataset 20 | } else { 21 | log.Println("DB read err:", err) 22 | } 23 | 24 | return GroupData{} 25 | } 26 | 27 | func (mdb *PGSqlDB) AppendGroupInfo(roomID string, m MsgDetail) { 28 | u := mdb.ReadGroupInfo(roomID) 29 | u = append(u, m) 30 | pgsql := &DBStorage{ 31 | RoomID: roomID, 32 | } 33 | if err := pgsql.Update(mdb); err != nil { 34 | log.Println("DB update err:", err) 35 | } 36 | } 37 | 38 | func NewPGSql(url string) *PGSqlDB { 39 | options, _ := pg.ParseURL(url) 40 | db := pg.Connect(options) 41 | 42 | err := createSchema(db) 43 | if err != nil { 44 | panic(err) 45 | } 46 | 47 | return &PGSqlDB{ 48 | Db: db, 49 | } 50 | } 51 | 52 | func createSchema(db *pg.DB) error { 53 | models := []interface{}{ 54 | (*MemStorage)(nil), 55 | } 56 | 57 | for _, model := range models { 58 | err := db.Model(model).CreateTable(&orm.CreateTableOptions{ 59 | IfNotExists: true}) 60 | if err != nil { 61 | return err 62 | } 63 | } 64 | return nil 65 | } 66 | 67 | // DBStorage for orm db storage. 68 | type DBStorage struct { 69 | Id int64 `bson:"_id"` 70 | RoomID string `json:"roomid" bson:"roomid"` 71 | Dataset GroupData `json:"dataset" bson:"dataset"` 72 | } 73 | 74 | func (u *DBStorage) Add(conn *PGSqlDB) { 75 | _, err := conn.Db.Model(u).Insert() 76 | if err != nil { 77 | log.Println(err) 78 | } 79 | } 80 | 81 | func (u *DBStorage) Get(conn *PGSqlDB) (result *DBStorage, err error) { 82 | log.Println("***Get dataset roomID=", u.RoomID) 83 | data := DBStorage{} 84 | err = conn.Db.Model(&data). 85 | Where("Room ID = ?", u.RoomID). 86 | Select() 87 | if err != nil { 88 | log.Println(err) 89 | return nil, err 90 | } 91 | log.Println("DB result= ", data) 92 | return &data, nil 93 | } 94 | 95 | func (u *DBStorage) Update(conn *PGSqlDB) (err error) { 96 | log.Println("***Update DB group data=", u) 97 | 98 | _, err = conn.Db.Model(u). 99 | Set("dataset = ?", u.Dataset). 100 | Where("roomid = ?", u.RoomID). 101 | Update() 102 | if err != nil { 103 | log.Println(err) 104 | } 105 | return nil 106 | } 107 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/kkdai/LINE-Bot-ChatSummarizer 2 | 3 | // +heroku goVersion go1.20 4 | go 1.20 5 | 6 | require github.com/line/line-bot-sdk-go/v7 v7.20.0 7 | 8 | require ( 9 | github.com/go-pg/pg/v10 v10.11.0 10 | github.com/sashabaranov/go-openai v1.9.4 11 | ) 12 | 13 | require ( 14 | github.com/go-pg/zerochecker v0.2.0 // indirect 15 | github.com/jinzhu/inflection v1.0.0 // indirect 16 | github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect 17 | github.com/vmihailenco/bufpool v0.1.11 // indirect 18 | github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect 19 | github.com/vmihailenco/tagparser v0.1.2 // indirect 20 | github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect 21 | golang.org/x/crypto v0.9.0 // indirect 22 | golang.org/x/sys v0.8.0 // indirect 23 | mellium.im/sasl v0.3.1 // indirect 24 | ) 25 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 3 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 4 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 5 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 7 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 8 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 9 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 10 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 11 | github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= 12 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 13 | github.com/go-pg/pg/v10 v10.11.0 h1:CMKJqLgTrfpE/aOVeLdybezR2om071Vh38OLZjsyMI0= 14 | github.com/go-pg/pg/v10 v10.11.0/go.mod h1:4BpHRoxE61y4Onpof3x1a2SQvi9c+q1dJnrNdMjsroA= 15 | github.com/go-pg/zerochecker v0.2.0 h1:pp7f72c3DobMWOb2ErtZsnrPaSvHd2W4o9//8HtF4mU= 16 | github.com/go-pg/zerochecker v0.2.0/go.mod h1:NJZ4wKL0NmTtz0GKCoJ8kym6Xn/EQzXRl2OnAe7MmDo= 17 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 18 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 19 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 20 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 21 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 22 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 23 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 24 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 25 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 26 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 27 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 28 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 29 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 30 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 31 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 32 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 33 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 34 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 35 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 36 | github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= 37 | github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= 38 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 39 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 40 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 41 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 42 | github.com/line/line-bot-sdk-go/v7 v7.20.0 h1:thFPRAHggENIY3+Kn1Er1obn5phrjFV1H6K6WNqaXRM= 43 | github.com/line/line-bot-sdk-go/v7 v7.20.0/go.mod h1:idpoxOZgtSd8JyhctMMpwg5LNgRAIL/QIxa5S0DXcMg= 44 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= 45 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 46 | github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= 47 | github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= 48 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 49 | github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= 50 | github.com/onsi/ginkgo v1.14.2 h1:8mVmC9kjFFmA8H4pKMUhcblgifdkOIXPvbhN1T36q1M= 51 | github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= 52 | github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= 53 | github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= 54 | github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA= 55 | github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= 56 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 57 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 58 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 59 | github.com/sashabaranov/go-openai v1.9.4 h1:KanoCEoowAI45jVXlenMCckutSRr39qOmSi9MyPBfZM= 60 | github.com/sashabaranov/go-openai v1.9.4/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg= 61 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 62 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 63 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 64 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 65 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 66 | github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo= 67 | github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs= 68 | github.com/vmihailenco/bufpool v0.1.11 h1:gOq2WmBrq0i2yW5QJ16ykccQ4wH9UyEsgLm6czKAd94= 69 | github.com/vmihailenco/bufpool v0.1.11/go.mod h1:AFf/MOy3l2CFTKbxwt0mp2MwnqjNEs5H/UxrkA5jxTQ= 70 | github.com/vmihailenco/msgpack/v5 v5.3.4/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= 71 | github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU= 72 | github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= 73 | github.com/vmihailenco/tagparser v0.1.2 h1:gnjoVuB/kljJ5wICEEOpx98oXMWPLj22G67Vbd1qPqc= 74 | github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= 75 | github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= 76 | github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= 77 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 78 | golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 79 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 80 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 81 | golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= 82 | golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= 83 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 84 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 85 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 86 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 87 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 88 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 89 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 90 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 91 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 92 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 93 | golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 94 | golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 95 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 96 | golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= 97 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 98 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 99 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 100 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 101 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 102 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 103 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 104 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 105 | golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 106 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 107 | golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 108 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 109 | golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 110 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 111 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 112 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 113 | golang.org/x/sys v0.0.0-20210923061019-b8560ed6a9b7/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 114 | golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= 115 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 116 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 117 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 118 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 119 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 120 | golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= 121 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 122 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 123 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 124 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 125 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 126 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 127 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 128 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 129 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 130 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 131 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 132 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 133 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 134 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 135 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 136 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 137 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 138 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 139 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 140 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 141 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 142 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 143 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 144 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 145 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 146 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 147 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= 148 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 149 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 150 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= 151 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 152 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 153 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 154 | gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= 155 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 156 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 157 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 158 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 159 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 160 | mellium.im/sasl v0.3.1 h1:wE0LW6g7U83vhvxjC1IY8DnXM+EU095yeo8XClvCdfo= 161 | mellium.im/sasl v0.3.1/go.mod h1:xm59PUYpZHhgQ9ZqoJ5QaCqzWMi8IeS49dhp6plPCzw= 162 | -------------------------------------------------------------------------------- /img/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kkdai/LINE-Bot-ChatSummarizer/545edd6e1b6f3c22bef2f2a354f2e8a8ad18e25b/img/.DS_Store -------------------------------------------------------------------------------- /img/chat_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kkdai/LINE-Bot-ChatSummarizer/545edd6e1b6f3c22bef2f2a354f2e8a8ad18e25b/img/chat_1.png -------------------------------------------------------------------------------- /img/list_all.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kkdai/LINE-Bot-ChatSummarizer/545edd6e1b6f3c22bef2f2a354f2e8a8ad18e25b/img/list_all.png -------------------------------------------------------------------------------- /img/sum_all.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kkdai/LINE-Bot-ChatSummarizer/545edd6e1b6f3c22bef2f2a354f2e8a8ad18e25b/img/sum_all.png -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // Licensed under the Apache License, Version 2.0 (the "License"); 2 | // you may not use this file except in compliance with the License. 3 | // You may obtain a copy of the License at 4 | // 5 | // http://www.apache.org/licenses/LICENSE-2.0 6 | // 7 | // Unless required by applicable law or agreed to in writing, software 8 | // distributed under the License is distributed on an "AS IS" BASIS, 9 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | // See the License for the specific language governing permissions and 11 | // limitations under the License. 12 | 13 | package main 14 | 15 | import ( 16 | "fmt" 17 | "log" 18 | "net/http" 19 | "os" 20 | 21 | "github.com/line/line-bot-sdk-go/v7/linebot" 22 | "github.com/sashabaranov/go-openai" 23 | ) 24 | 25 | var bot *linebot.Client 26 | var client *openai.Client 27 | var summaryQueue GroupDB 28 | var stickerRedeemable bool 29 | var enableRedeem string 30 | 31 | const RedeemStickerPID = "789" 32 | const RedeemStickerSID = "10856" 33 | 34 | type GPT_ACTIONS int 35 | 36 | const ( 37 | GPT_Complete GPT_ACTIONS = 0 38 | GPT_Draw GPT_ACTIONS = 1 39 | GPT_Whister GPT_ACTIONS = 2 40 | GPT_GPT4_Complete GPT_ACTIONS = 3 41 | ) 42 | 43 | func main() { 44 | stickerRedeemable = false 45 | var err error 46 | 47 | // Enable new feature (YES, default no) 48 | enableRedeem = os.Getenv("REDEEM_ENABLE") 49 | 50 | // If DABTASE_URL is preset, create PostGresSQL; otherwise, create Mem DB. 51 | pSQL := os.Getenv("DATABASE_URL") 52 | if pSQL != "" { 53 | summaryQueue = NewPGSql(pSQL) 54 | } else { 55 | summaryQueue = NewMemDB() 56 | } 57 | 58 | bot, err = linebot.New(os.Getenv("ChannelSecret"), os.Getenv("ChannelAccessToken")) 59 | log.Println("Bot:", bot, " err:", err) 60 | 61 | port := os.Getenv("PORT") 62 | apiKey := os.Getenv("ChatGptToken") 63 | 64 | if apiKey != "" { 65 | client = openai.NewClient(apiKey) 66 | } 67 | 68 | http.HandleFunc("/callback", callbackHandler) 69 | addr := fmt.Sprintf(":%s", port) 70 | http.ListenAndServe(addr, nil) 71 | } 72 | 73 | func IsRedemptionEnabled() bool { 74 | return enableRedeem == "YES" 75 | } 76 | -------------------------------------------------------------------------------- /render.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | - type: web 3 | name: LINE-Bot-ChatSummarizer 4 | env: go 5 | buildCommand: go build -o app 6 | startCommand: ./app 7 | plan: free 8 | autoDeploy: false 9 | envVars: 10 | - key: ChannelAccessToken 11 | sync: false 12 | - key: ChannelSecret 13 | sync: false 14 | - key: ChatGptToken 15 | sync: false 16 | - key: DATABASE_URL 17 | sync: false --------------------------------------------------------------------------------