├── .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 | [](https://gitter.im/kkdai/LINE-Bot-ChatSummarizer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [](https://godoc.org/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 | 
16 |
17 | - 能夠記住群組內的對話。
18 |
19 | 
20 |
21 | - 直接送群組內容摘要私訊給你。
22 | 
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 | [](https://heroku.com/deploy)
68 |
69 | - 輸入 `Channel Secret` and `Channel Access Token` 還有 `ChatGptToken` .
70 |
71 | #### 部署在 Rener
72 |
73 | [](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 | [](https://gitter.im/kkdai/LINE-Bot-ChatSummarizer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [](https://godoc.org/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 | 
16 |
17 | - Be able to remember the conversation in the group.
18 |
19 | 
20 |
21 | - Send a summary of the group's content privately to you.
22 |
23 | 
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 | [](https://heroku.com/deploy)
66 |
67 | - Input `Channel Secret` and `Channel Access Token` and `ChatGptToken`.
68 |
69 | #### Deploy this on Rener
70 |
71 | [](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
--------------------------------------------------------------------------------