├── .env.example ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.en.md ├── README.md ├── demo ├── Get-Notion-SpaceId.png ├── Get-Notion-Token.png ├── NotionAI-Discord-Bot-Demo1.png ├── NotionAI-Discord-Bot-Demo2.png ├── NotionAI-Discord-Bot-En-Demo1.png └── NotionAI-Discord-Bot-En-Demo2.png ├── docker-compose.yaml ├── main.py ├── notionai ├── __init__.py ├── enums.py └── notionai.py ├── requirements.txt └── src ├── __init__.py ├── discordBot.py ├── logger.py └── server.py /.env.example: -------------------------------------------------------------------------------- 1 | DISCORD_TOKEN = 2 | NOTION_TOKEN = 3 | NOTION_SPACE_ID = -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | .DS_Store 3 | */.DS_Store 4 | logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | lerna-debug.log* 10 | 11 | # Diagnostic reports (https://nodejs.org/api/report.html) 12 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 13 | 14 | # Runtime data 15 | pids 16 | *.pid 17 | *.seed 18 | *.pid.lock 19 | 20 | # Directory for instrumented libs generated by jscoverage/JSCover 21 | lib-cov 22 | 23 | # Coverage directory used by tools like istanbul 24 | coverage 25 | *.lcov 26 | 27 | # nyc test coverage 28 | .nyc_output 29 | 30 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 31 | .grunt 32 | 33 | # Bower dependency directory (https://bower.io/) 34 | bower_components 35 | 36 | # node-waf configuration 37 | .lock-wscript 38 | 39 | # Compiled binary addons (https://nodejs.org/api/addons.html) 40 | build/Release 41 | 42 | # Dependency directories 43 | node_modules/ 44 | jspm_packages/ 45 | 46 | # Snowpack dependency directory (https://snowpack.dev/) 47 | web_modules/ 48 | 49 | # TypeScript cache 50 | *.tsbuildinfo 51 | 52 | # Optional npm cache directory 53 | .npm 54 | 55 | # Optional eslint cache 56 | .eslintcache 57 | 58 | # Microbundle cache 59 | .rpt2_cache/ 60 | .rts2_cache_cjs/ 61 | .rts2_cache_es/ 62 | .rts2_cache_umd/ 63 | 64 | # Optional REPL history 65 | .node_repl_history 66 | 67 | # Output of 'npm pack' 68 | *.tgz 69 | 70 | # Yarn Integrity file 71 | .yarn-integrity 72 | 73 | # dotenv environment variables file 74 | .env 75 | .env.test 76 | 77 | # parcel-bundler cache (https://parceljs.org/) 78 | .cache 79 | .parcel-cache 80 | 81 | # Next.js build output 82 | .next 83 | out 84 | 85 | # Nuxt.js build / generate output 86 | .nuxt 87 | dist 88 | 89 | # Gatsby files 90 | .cache/ 91 | # Comment in the public line in if your project uses Gatsby and not Next.js 92 | # https://nextjs.org/blog/next-9-1#public-directory-support 93 | # public 94 | 95 | # vuepress build output 96 | .vuepress/dist 97 | 98 | # Serverless directories 99 | .serverless/ 100 | 101 | # FuseBox cache 102 | .fusebox/ 103 | 104 | # DynamoDB Local files 105 | .dynamodb/ 106 | 107 | # TernJS port file 108 | .tern-port 109 | 110 | # Stores VSCode versions used for testing VSCode extensions 111 | .vscode-test 112 | 113 | # yarn v2 114 | .yarn/cache 115 | .yarn/unplugged 116 | .yarn/build-state.yml 117 | .yarn/install-state.gz 118 | .pnp.* 119 | 120 | 121 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.9-alpine 2 | 3 | 4 | COPY ./ /DiscordBot 5 | WORKDIR /DiscordBot 6 | 7 | RUN pip3 install -r requirements.txt 8 | 9 | CMD ["python3", "main.py"] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 ExplainThis 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.en.md: -------------------------------------------------------------------------------- 1 | # NotionAI Discord Bot 2 | 3 | [中文](README.md) | English 4 | 5 | [![license](https://img.shields.io/pypi/l/ansicolortags.svg)](LICENSE) [![Release](https://img.shields.io/github/v/release/TheExplainthis/NotionAI-Discord-Bot)](https://github.com/TheExplainthis/NotionAI-Discord-Bot/releases/) 6 | 7 | 8 | ## Introduction 9 | Notion, a note-taking software, has launched its own NotionAI service in recent months. How powerful is it? You can check out the video below: 10 | 11 | [![NotionAI Introduction](https://i.ytimg.com/vi/RDZ3mY10zY8/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLAzPk5CktB6wPz8lgHguLi2UjfrOw)](https://youtu.be/RDZ3mY10zY8) 12 | 13 | NotionAI is similar to ChatGPT, but it provides various functions, such as translation, itinerary planning, email writing, copywriting, brainstorming, etc. This article will teach you how to use NotionAI on Discord to enhance team collaboration. 14 | 15 | ![NotionAI-Discord-Bot-Demo1](https://github.com/TheExplainthis/NotionAI-Discord-Bot/blob/main/demo/NotionAI-Discord-Bot-En-Demo1.png) 16 | ![NotionAI-Discord-Bot-Demo2](https://github.com/TheExplainthis/NotionAI-Discord-Bot/blob/main/demo/NotionAI-Discord-Bot-En-Demo2.png) 17 | 18 | ## Installation Steps 19 | ### Token Retrieval 20 | 1. Obtain NotionAI Token: 21 | 1. Login to the web version of [Notion](https://www.notion.so/) 22 | 2. After logging in, right-click on the web page -> "Inspect" -> "Application" -> Token and then in Cookies, and SpaceId in LocalStorage, as shown below 23 | * ![Get-Notion-Token](https://github.com/TheExplainthis/NotionAI-Discord-Bot/blob/main/demo/Get-Notion-Token.png) 24 | * ![Get-Notion-SpaceId](https://github.com/TheExplainthis/NotionAI-Discord-Bot/blob/main/demo/Get-Notion-SpaceId.png) 25 | 2. Obtain Discord Token:: 26 | 1. Login to [Discord Developer](https://discord.com/developers/applications) 27 | 2. Create a bot: 28 | 1. Enter `Applications` on the left 29 | 2. Click `New Application` in the upper right corner and enter the name of the bot > after confirmation, enter the new page. 30 | 3. Click `Bot` on the left 31 | 4. Click `Add Bot` on the right 32 | 5. The `MESSAGE CONTENT INTENT` below needs to be turned on 33 | 6. Click `Save Change` 34 | 7. The Token can be selected at the top by clicking `View Token` or if you have already applied, it will be the `Reset Token` button. 35 | 3. Set up OAuth2 36 | 1. Click `OAuth2` on the left 37 | 2. Click `URL Generator` on the left 38 | 3. On the right, select `bot` under `SCOPES` and `Administrator` under `BOT PERMISSIONS` at the bottom right 39 | 4. Copy the bottom URL to the browser 40 | 5. Select the server to join 41 | 6. Click `Continue` > `Authorize` 42 | 43 | ### Project Setup 44 | 1. Fork Github project: 45 | 1. Register/Login to [GitHub](https://github.com/) 46 | 2. Access [NotionAI-Discord-Bot](https://github.com/TheExplainthis/NotionAI-Discord-Bot) 47 | 3. Click `Star` to support the developer 48 | 4. Click `Fork` to copy all the code to your own repository 49 | 2. Deployment (free space): 50 | 1. Go to [replit](https://replit.com/) 51 | 2. Click `Sign Up` to log in directly with your Github account and authorize it -> click `Skip` to skip the initialization settings 52 | 3. After entering, click `Create` in the center of the main page -> a pop-up window will appear, click `Import from Github` in the upper right corner 53 | 4. If the Github repository has not been added, click the link `Connect GitHub to import your private repos.` -> check `Only select repositories` -> select `NotionAI-Discord-Bot` 54 | 5. Go back to step four, at this point the `Github URL` can choose the `NotionAI-Discord-Bot` project -> click `Import from Github`. 55 | 56 | ### Project Execution 57 | 1. Environment variable setting 58 | 1. After completing the previous step of `Import` in the Replit project management page, click `Secrets` in the lower left corner of `Tools`. 59 | 2. After clicking `Got it` on the right, you can add environment variables, and you need to add: 60 | 1. Discord Token: 61 | - key: `DISCORD_TOKEN` 62 | - value: `[obtained from step one above]` 63 | 2. Notion Token: 64 | - key: `NOTION_TOKEN` 65 | - value: `[obtained from step one above]` 66 | 3. Notion Space Id: 67 | - key: `NOTION_SPACE_ID` 68 | - value: `[obtained from step one above]` 69 | 2. Start running 70 | 1. Click `Run` above 71 | 2. After success, the right side of the screen will display `Hello World`, and copy the URL in the upper left of the screen, which will be used in the next step 72 | - Note: If there are no requests within an hour, the program will be interrupted, so the next step is required. 73 | 3. CronJob timing request 74 | 1. Register/Login to [cron-job.org](https://cron-job.org/en/) 75 | 2. Select `CREATE CRONJOB` in the upper right of the panel 76 | 3. Enter `NotionAI-Discord-Bot` in the `Title` and enter the URL from the previous step 77 | 4. Click every `5 minutes` below 78 | 5. Click `CREATE` 79 | 80 | ## Explanation 81 | | Command | Parameters + Explanation | 82 | | --- | ---------- | 83 | | `help_me_write` | prompt: The command for AI, context: The text to be edited, page_title(Optional): Title, rest_content(Optional): Other parts of the text | 84 | | `continue_write` | context: Text, page_title(Optional): Title, rest_content(Optional): Other parts of the text | 85 | | `help_me_edit` | prompt: The command for AI, context: The text to be edited, page_title(Optional): Title | 86 | | `translate` | language: The language to be translated, context: The text to be translated | 87 | | `change_tone` | context: The text to be converted, tone: The style of the text | 88 | | `summarize` | context: The text to be summarized, page_title(Optional): Title | 89 | | `improve_writing` | context: The text to be improved, page_title(Optional): Title | 90 | | `fix_spelling_grammar` | context: The text to be corrected, page_title(Optional): Title | 91 | | `explain_this` | context: The text to be explained, page_title(Optional): Title | 92 | | `make_longer` | context: The text to be lengthened, page_title(Optional): Title | 93 | | `make_shorter` | context: The text to be shortened, page_title(Optional): Title | 94 | | `find_action_items` | context: The text to be edited, page_title(Optional): Title | 95 | | `simplify_language` | context: The text to be edited, page_title(Optional): Title | 96 | | `blog_post` | prompt: The command for AI | 97 | | `brainstorm_ideas` | prompt: The command for AI | 98 | | `outline` | prompt: The command for AI | 99 | | `social_media_post` | prompt: The command for AI | 100 | | `creative_story` | prompt: The command for AI | 101 | | `poem` | prompt: The command for AI | 102 | | `essay` | prompt: The command for AI | 103 | | `meeting_agenda` | prompt: The command for AI | 104 | | `press_release` | prompt: The command for AI | 105 | | `job_description` | prompt: The command for AI | 106 | | `sales_email` | prompt: The command for AI | 107 | | `recruiting_email` | prompt: The command for AI | 108 | | `pros_cons_list` | prompt: The command for AI | 109 | 110 | ## Support Us 111 | Like this free project? Please consider [supporting us](https://www.buymeacoffee.com/explainthis) to keep it running. 112 | 113 | [Buy Me A Coffee](https://www.buymeacoffee.com/explainthis) 114 | 115 | ## Related Projects 116 | [NotionAI](https://github.com/Vaayne/NotionAI) 117 | 118 | ## License 119 | [MIT](LICENSE) 120 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NotionAI Discord Bot 2 | 3 | 中文 | [English](README.en.md) 4 | 5 | [![license](https://img.shields.io/pypi/l/ansicolortags.svg)](LICENSE) [![Release](https://img.shields.io/github/v/release/TheExplainthis/NotionAI-Discord-Bot)](https://github.com/TheExplainthis/NotionAI-Discord-Bot/releases/) 6 | 7 | 8 | ## 介紹 9 | 近幾個月筆記軟體 Notion 也開始推出了自己的 [NotionAI 服務](https://www.notion.so/product/ai),功能有多強大呢?可以先看看下面的影片: 10 | 11 | [![NotionAI Introduction](https://i.ytimg.com/vi/RDZ3mY10zY8/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLAzPk5CktB6wPz8lgHguLi2UjfrOw)](https://youtu.be/RDZ3mY10zY8) 12 | 13 | 14 | NotionAI 和 ChatGPT 相似,但提供多種不同的功能,例如翻譯、行程規劃、Email 撰寫、文案發想、頭腦風暴等。本文將教你如何在 Discord 上使用 NotionAI,增強團隊協作。 15 | 16 | ![NotionAI-Discord-Bot-Demo1](https://github.com/TheExplainthis/NotionAI-Discord-Bot/blob/main/demo/NotionAI-Discord-Bot-Demo1.png) 17 | ![NotionAI-Discord-Bot-Demo2](https://github.com/TheExplainthis/NotionAI-Discord-Bot/blob/main/demo/NotionAI-Discord-Bot-Demo2.png) 18 | 19 | 20 | 21 | ## 安裝步驟 22 | ### Token 取得 23 | 1. 取得 NotionAI Token: 24 | 1. 登入網頁版 [Notion](https://www.notion.so/) 25 | 2. 登入後按網頁 `右鍵` -> `檢查` -> `應用程式` -> Token 再 Cookies 裡,而 SpaceId 在 LocalStorage 裡,如下圖所示 26 | * ![Get-Notion-Token](https://github.com/TheExplainthis/NotionAI-Discord-Bot/blob/main/demo/Get-Notion-Token.png) 27 | * ![Get-Notion-SpaceId](https://github.com/TheExplainthis/NotionAI-Discord-Bot/blob/main/demo/Get-Notion-SpaceId.png) 28 | 2. 取得 Discord Token: 29 | 1. 登入 [Discord Developer](https://discord.com/developers/applications) 30 | 2. 創建機器人: 31 | 1. 進入左方 `Applications` 32 | 2. 點擊右上方 `New Application` 並輸入 Bot 的名稱 > 確認後進入新頁面。 33 | 3. 點擊左方 `Bot` 34 | 4. 點擊右方 `Add Bot` 35 | 5. 下方 `MESSAGE CONTENT INTENT` 需打開 36 | 6. 按下 `Save Change` 37 | 7. Token 在上方選擇 `View Token` 或已申請過則會是 `Reset Token` 的按鈕。 38 | 3. 設定 OAuth2 39 | 1. 點擊左欄 `OAuth2` 40 | 2. 點擊左欄 `URL Generator` 41 | 3. 右欄 `SCOPES` 選擇 `bot`、右欄下方 `BOT PERMISSIONS` 選擇 `Administrator` 42 | 4. 複製最下方網址到瀏覽器中 43 | 5. 選擇欲加入的伺服器 44 | 6. 按下 `繼續` > `授權` 45 | 46 | ### 專案設置 47 | 1. Fork Github 專案: 48 | 1. 註冊/登入 [GitHub](https://github.com/) 49 | 2. 進入 [NotionAI-Discord-Bot](https://github.com/TheExplainthis/NotionAI-Discord-Bot) 50 | 3. 點選 `Star` 支持開發者 51 | 4. 點選 `Fork` 複製全部的程式碼到自己的倉庫 52 | 2. 部署(免費空間): 53 | 1. 進入 [replit](https://replit.com/) 54 | 2. 點選 `Sign Up` 直接用 `Github` 帳號登入並授權 -> 按下 `Skip` 跳過初始化設定 55 | 3. 進入後中間主頁的部分點選 `Create` -> 跳出框,點選右上角 `Import from Github` 56 | 4. 若尚未加入 Github 倉庫,則點選連結 `Connect GitHub to import your private repos.` -> 勾選 `Only select repositories` -> 選擇 `NotionAI-Discord-Bot` 57 | 5. 回到第四步,此時 `Github URL` 可以選擇 `NotionAI-Discord-Bot` 專案 -> 點擊 `Import from Github`。 58 | 59 | ### 專案執行 60 | 1. 環境變數設定 61 | 1. 接續上一步 `Import` 完成後在 `Replit` 的專案管理頁面左下方 `Tools` 點擊 `Secrets`。 62 | 2. 右方按下 `Got it` 後,即可新增環境變數,需新增: 63 | 1. Discord Token: 64 | - key: `DISCORD_TOKEN` 65 | - value: `[由上方步驟一取得]` 66 | 2. Notion Token: 67 | - key: `NOTION_TOKEN` 68 | - value: `[由上方步驟一取得]` 69 | 3. Notion Space Id: 70 | - key: `NOTION_SPACE_ID` 71 | - value: `[由上方步驟一取得]` 72 | 2. 開始執行 73 | 1. 點擊上方的 `Run` 74 | 2. 成功後右邊畫面會顯示 `Hello World`,並將畫面中上方的**網址複製**下來,下一步驟會用到 75 | - 注意:若一小時內沒有任何請求,則程式會中斷,因此需要下步驟 76 | 3. CronJob 定時發送請求 77 | 1. 註冊/登入 [cron-job.org](https://cron-job.org/en/) 78 | 2. 進入後面板右上方選擇 `CREATE CRONJOB` 79 | 3. `Title` 輸入 `NotionAI-Discord-Bot`,網址輸入上一步驟的網址 80 | 4. 下方則每 `5 分鐘` 打一次 81 | 5. 按下 `CREATE` 82 | 83 | 84 | ## 說明 85 | | 指令 | 參數 + 說明 | 86 | | --- | ---------- | 87 | | `help_me_write` | prompt: 給 AI 的指令, context: 欲編輯的內文, page_title(Optional): 標題, rest_content(Optional): 其他部分的內文 | 88 | | `continue_write` | context: 內文, page_title(Optional): 標題, rest_content(Optional): 其他部分的內文 | 89 | | `help_me_edit` | prompt: 給 AI 的指令, context: 欲編輯的內文, page_title(Optional): 標題 | 90 | | `translate` | language: 欲翻譯的語言, context: 欲翻譯的內文 | 91 | | `change_tone` | context: 欲轉換風格的內文, tone: 內文風格 | 92 | | `summarize` | context: 欲總結的內文, page_title(Optional): 標題 | 93 | | `improve_writing` | context: 欲改善的內文, page_title(Optional): 標題 | 94 | | `fix_spelling_grammar` | context: 欲修正的內文, page_title(Optional): 標題 | 95 | | `explain_this` | context: 欲解釋的內文, page_title(Optional): 標題 | 96 | | `make_longer` | context: 欲變長的內文, page_title(Optional): 標題 | 97 | | `make_shorter` | context: 欲變短的內文, page_title(Optional): 標題 | 98 | | `find_action_items` | context: 欲編輯的內文, page_title(Optional): 標題 | 99 | | `simplify_language` | context: 欲編輯的內文, page_title(Optional): 標題 | 100 | | `blog_post` | prompt: 給 AI 的指令 | 101 | | `brainstorm_ideas` | prompt: 給 AI 的指令 | 102 | | `outline` | prompt: 給 AI 的指令 | 103 | | `social_media_post` | prompt: 給 AI 的指令 | 104 | | `creative_story` | prompt: 給 AI 的指令 | 105 | | `poem` | prompt: 給 AI 的指令 | 106 | | `essay` | prompt: 給 AI 的指令 | 107 | | `meeting_agenda` | prompt: 給 AI 的指令 | 108 | | `press_release` | prompt: 給 AI 的指令 | 109 | | `job_description` | prompt: 給 AI 的指令 | 110 | | `sales_email` | prompt: 給 AI 的指令 | 111 | | `recruiting_email` | prompt: 給 AI 的指令 | 112 | | `pros_cons_list` | prompt: 給 AI 的指令 | 113 | 114 | 115 | ## 支持我們 116 | 如果你喜歡這個專案,願意[支持我們](https://www.buymeacoffee.com/explainthis),可以請我們喝一杯咖啡,這會成為我們繼續前進的動力! 117 | 118 | [Buy Me A Coffee](https://www.buymeacoffee.com/explainthis) 119 | 120 | ## 相關專案 121 | [NotionAI](https://github.com/Vaayne/NotionAI) 122 | 123 | ## 授權 124 | [MIT](LICENSE) 125 | -------------------------------------------------------------------------------- /demo/Get-Notion-SpaceId.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheExplainthis/NotionAI-Discord-Bot/1bf02ec7cb4c5513c17deeed1c5aa38e3a11fe6c/demo/Get-Notion-SpaceId.png -------------------------------------------------------------------------------- /demo/Get-Notion-Token.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheExplainthis/NotionAI-Discord-Bot/1bf02ec7cb4c5513c17deeed1c5aa38e3a11fe6c/demo/Get-Notion-Token.png -------------------------------------------------------------------------------- /demo/NotionAI-Discord-Bot-Demo1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheExplainthis/NotionAI-Discord-Bot/1bf02ec7cb4c5513c17deeed1c5aa38e3a11fe6c/demo/NotionAI-Discord-Bot-Demo1.png -------------------------------------------------------------------------------- /demo/NotionAI-Discord-Bot-Demo2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheExplainthis/NotionAI-Discord-Bot/1bf02ec7cb4c5513c17deeed1c5aa38e3a11fe6c/demo/NotionAI-Discord-Bot-Demo2.png -------------------------------------------------------------------------------- /demo/NotionAI-Discord-Bot-En-Demo1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheExplainthis/NotionAI-Discord-Bot/1bf02ec7cb4c5513c17deeed1c5aa38e3a11fe6c/demo/NotionAI-Discord-Bot-En-Demo1.png -------------------------------------------------------------------------------- /demo/NotionAI-Discord-Bot-En-Demo2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheExplainthis/NotionAI-Discord-Bot/1bf02ec7cb4c5513c17deeed1c5aa38e3a11fe6c/demo/NotionAI-Discord-Bot-En-Demo2.png -------------------------------------------------------------------------------- /docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | app: 5 | container_name: discord-chatgpt-ai-assistant 6 | build: . 7 | restart: always 8 | ports: 9 | - "${APP_PORT}:${APP_PORT}" -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | 4 | from dotenv import load_dotenv 5 | import discord 6 | from notionai import NotionAI, ToneEnum, TranslateLanguageEnum 7 | from src.discordBot import DiscordClient, Sender 8 | from src.logger import logger 9 | from src.server import keep_alive 10 | load_dotenv() 11 | 12 | 13 | def run(): 14 | client = DiscordClient() 15 | sender = Sender() 16 | notion_ai = NotionAI(os.getenv('NOTION_TOKEN'), os.getenv('NOTION_SPACE_ID')) 17 | 18 | @client.tree.command(name="help_me_write", description="Help me write, ask AI to write for you") 19 | async def help_me_write(interaction: discord.Interaction, *, prompt: str, context: str, page_title: str = "", rest_content: str = ""): 20 | if interaction.user == client.user: 21 | return 22 | await interaction.response.defer() 23 | ai_response = notion_ai.help_me_write(prompt, context, page_title, rest_content) 24 | send_message = f"[help_me_write] prompt: {prompt} \n context: {context} \n page_title: {page_title} \n rest_content: {rest_content}" 25 | await sender.send_message(interaction, send_message, ai_response) 26 | 27 | @client.tree.command(name="continue_write", description="Continue writing, generating more") 28 | async def continue_write(interaction: discord.Interaction, *, context: str, page_title: str = "", rest_content: str = ""): 29 | if interaction.user == client.user: 30 | return 31 | await interaction.response.defer() 32 | ai_response = notion_ai.continue_write(context, page_title, rest_content) 33 | send_message = f"[continue_write] context: {context} \n page_title: {page_title} \n rest_content: {rest_content}" 34 | await sender.send_message(interaction, send_message, ai_response) 35 | 36 | @client.tree.command(name="help_me_edit", description="Help me edit somethings, it will change the current context") 37 | async def help_me_edit(interaction: discord.Interaction, *, prompt: str, context: str, page_title: str = ""): 38 | if interaction.user == client.user: 39 | return 40 | await interaction.response.defer() 41 | ai_response = notion_ai.help_me_edit(prompt, context, page_title) 42 | send_message = f"[help_me_edit] prompt: {prompt} \n context: {context} \n page_title: {page_title} " 43 | await sender.send_message(interaction, send_message, ai_response) 44 | 45 | @client.tree.command(name="translate", description="Use NotionAI to translate your context") 46 | async def translate(interaction: discord.Interaction, *, language: str, context: str): 47 | if interaction.user == client.user: 48 | return 49 | await interaction.response.defer() 50 | send_message = f"[translate] language: {language} \n context: {context}" 51 | 52 | language = language.lower() 53 | try: 54 | language = TranslateLanguageEnum(language) 55 | except Exception: 56 | error_message = "❌ Error: The language can only be: english, korean, chinese, japanese, spanish, russiab, french, german, italian, portuguese, dutch, indonesia, tagalog or vietnamese." 57 | await sender.send_message(interaction, send_message, error_message) 58 | ai_response = notion_ai.translate(language, context) 59 | await sender.send_message(interaction, send_message, ai_response) 60 | 61 | @client.tree.command(name="change_tone", description="Change the tone of your context") 62 | async def change_tone(interaction: discord.Interaction, *, context: str, tone: str): 63 | if interaction.user == client.user: 64 | return 65 | await interaction.response.defer() 66 | 67 | send_message = f"[change_tone] context: {context} \n tone: {tone}" 68 | try: 69 | tone = ToneEnum(tone) 70 | except Exception: 71 | error_message = "❌ Error: The tone can only be: professional, casual, straightforward, confident, or friendly." 72 | await sender.send_message(interaction, send_message, error_message) 73 | ai_response = notion_ai.change_tone(context, ToneEnum(tone)) 74 | await sender.send_message(interaction, send_message, ai_response) 75 | 76 | @client.tree.command(name="summarize", description="Summarize context") 77 | async def summarize(interaction: discord.Interaction, *, context: str, page_title: str = ""): 78 | if interaction.user == client.user: 79 | return 80 | await interaction.response.defer() 81 | ai_response = notion_ai.summarize(context, page_title) 82 | send_message = f"[summarize] context: {context} \n page_title: {page_title}" 83 | await sender.send_message(interaction, send_message, ai_response) 84 | 85 | @client.tree.command(name="improve_writing", description="Improve context") 86 | async def improve_writing(interaction: discord.Interaction, *, context: str, page_title: str = ""): 87 | if interaction.user == client.user: 88 | return 89 | await interaction.response.defer() 90 | ai_response = notion_ai.improve_writing(context, page_title) 91 | send_message = f"[improve_writing] context: {context} \n page_title: {page_title}" 92 | await sender.send_message(interaction, send_message, ai_response) 93 | 94 | @client.tree.command(name="fix_spelling_grammar", description="Correcting grammar errors in context") 95 | async def fix_spelling_grammar(interaction: discord.Interaction, *, context: str, page_title: str = ""): 96 | if interaction.user == client.user: 97 | return 98 | await interaction.response.defer() 99 | ai_response = notion_ai.fix_spelling_grammar(context, page_title) 100 | send_message = f"[fix_spelling_grammar] context: {context} \n page_title: {page_title}" 101 | await sender.send_message(interaction, send_message, ai_response) 102 | 103 | @client.tree.command(name="explain_this", description="Explanation of Context") 104 | async def explain_this(interaction: discord.Interaction, *, context: str, page_title: str = ""): 105 | if interaction.user == client.user: 106 | return 107 | await interaction.response.defer() 108 | ai_response = notion_ai.explain_this(context, page_title) 109 | send_message = f"[explain_this] context: {context} \n page_title: {page_title}" 110 | await sender.send_message(interaction, send_message, ai_response) 111 | 112 | @client.tree.command(name="make_longer", description="Editing text to make the Context longer") 113 | async def make_longer(interaction: discord.Interaction, *, context: str, page_title: str = ""): 114 | if interaction.user == client.user: 115 | return 116 | await interaction.response.defer() 117 | ai_response = notion_ai.make_longer(context, page_title) 118 | send_message = f"[make_longer] context: {context} \n page_title: {page_title}" 119 | await sender.send_message(interaction, send_message, ai_response) 120 | 121 | @client.tree.command(name="make_shorter", description="Editing text to make the Context shorter") 122 | async def make_shorter(interaction: discord.Interaction, *, context: str, page_title: str = ""): 123 | if interaction.user == client.user: 124 | return 125 | await interaction.response.defer() 126 | ai_response = notion_ai.make_shorter(context, page_title) 127 | send_message = f"[make_shorter] context: {context} \n page_title: {page_title}" 128 | await sender.send_message(interaction, send_message, ai_response) 129 | 130 | @client.tree.command(name="find_action_items", description="Generate action items") 131 | async def find_action_items(interaction: discord.Interaction, *, context: str, page_title: str = ""): 132 | if interaction.user == client.user: 133 | return 134 | await interaction.response.defer() 135 | ai_response = notion_ai.find_action_items(context, page_title) 136 | send_message = f"[find_action_items] context: {context} \n page_title: {page_title}" 137 | await sender.send_message(interaction, send_message, ai_response) 138 | 139 | @client.tree.command(name="simplify_language", description="Simplify Context") 140 | async def simplify_language(interaction: discord.Interaction, *, context: str, page_title: str = ""): 141 | if interaction.user == client.user: 142 | return 143 | await interaction.response.defer() 144 | ai_response = notion_ai.simplify_language(context, page_title) 145 | send_message = f"[simplify_language] context: {context} \n page_title: {page_title}" 146 | await sender.send_message(interaction, send_message, ai_response) 147 | 148 | @client.tree.command(name="blog_post", description="Generate post") 149 | async def blog_post(interaction: discord.Interaction, *, prompt: str): 150 | if interaction.user == client.user: 151 | return 152 | await interaction.response.defer() 153 | ai_response = notion_ai.blog_post(prompt) 154 | send_message = f"[blog_post] prompt: {prompt}" 155 | await sender.send_message(interaction, send_message, ai_response) 156 | 157 | @client.tree.command(name="brainstorm_ideas", description="Generate ideas") 158 | async def brainstorm_ideas(interaction: discord.Interaction, *, prompt: str): 159 | if interaction.user == client.user: 160 | return 161 | await interaction.response.defer() 162 | ai_response = notion_ai.brainstorm_ideas(prompt) 163 | send_message = f"[brainstorm_ideas] prompt: {prompt}" 164 | await sender.send_message(interaction, send_message, ai_response) 165 | 166 | @client.tree.command(name="outline", description="Generate outline") 167 | async def outline(interaction: discord.Interaction, *, prompt: str): 168 | if interaction.user == client.user: 169 | return 170 | await interaction.response.defer() 171 | ai_response = notion_ai.outline(prompt) 172 | send_message = f"[outline] prompt: {prompt}" 173 | await sender.send_message(interaction, send_message, ai_response) 174 | 175 | @client.tree.command(name="social_media_post", description="Generate social media post") 176 | async def social_media_post(interaction: discord.Interaction, *, prompt: str): 177 | if interaction.user == client.user: 178 | return 179 | await interaction.response.defer() 180 | ai_response = notion_ai.social_media_post(prompt) 181 | send_message = f"[social_media_post] prompt: {prompt}" 182 | await sender.send_message(interaction, send_message, ai_response) 183 | 184 | @client.tree.command(name="creative_story", description="Generate creative story") 185 | async def creative_story(interaction: discord.Interaction, *, prompt: str): 186 | if interaction.user == client.user: 187 | return 188 | await interaction.response.defer() 189 | ai_response = notion_ai.creative_story(prompt) 190 | send_message = f"[creative_story] prompt: {prompt}" 191 | await sender.send_message(interaction, send_message, ai_response) 192 | 193 | @client.tree.command(name="poem", description="Generate poem") 194 | async def poem(interaction: discord.Interaction, *, prompt: str): 195 | if interaction.user == client.user: 196 | return 197 | await interaction.response.defer() 198 | ai_response = notion_ai.poem(prompt) 199 | send_message = f"[poem] prompt: {prompt}" 200 | await sender.send_message(interaction, send_message, ai_response) 201 | 202 | @client.tree.command(name="essay", description="Generate essay") 203 | async def essay(interaction: discord.Interaction, *, prompt: str): 204 | if interaction.user == client.user: 205 | return 206 | await interaction.response.defer() 207 | ai_response = notion_ai.essay(prompt) 208 | send_message = f"[essay] prompt: {prompt}" 209 | await sender.send_message(interaction, send_message, ai_response) 210 | 211 | @client.tree.command(name="meeting_agenda", description="Generate meeting agenda") 212 | async def meeting_agenda(interaction: discord.Interaction, *, prompt: str): 213 | if interaction.user == client.user: 214 | return 215 | await interaction.response.defer() 216 | ai_response = notion_ai.meeting_agenda(prompt) 217 | send_message = f"[meeting_agenda] prompt: {prompt}" 218 | await sender.send_message(interaction, send_message, ai_response) 219 | 220 | @client.tree.command(name="press_release", description="Generate press release") 221 | async def press_release(interaction: discord.Interaction, *, prompt: str): 222 | if interaction.user == client.user: 223 | return 224 | await interaction.response.defer() 225 | ai_response = notion_ai.press_release(prompt) 226 | send_message = f"[press_release] prompt: {prompt}" 227 | await sender.send_message(interaction, send_message, ai_response) 228 | 229 | @client.tree.command(name="job_description", description="Generate jon description") 230 | async def job_description(interaction: discord.Interaction, *, prompt: str): 231 | if interaction.user == client.user: 232 | return 233 | await interaction.response.defer() 234 | ai_response = notion_ai.job_description(prompt) 235 | send_message = f"[job_description] prompt: {prompt}" 236 | await sender.send_message(interaction, send_message, ai_response) 237 | 238 | @client.tree.command(name="sales_email", description="Generate sales email") 239 | async def sales_email(interaction: discord.Interaction, *, prompt: str): 240 | if interaction.user == client.user: 241 | return 242 | await interaction.response.defer() 243 | ai_response = notion_ai.sales_email(prompt) 244 | send_message = f"[sales_email] prompt: {prompt}" 245 | await sender.send_message(interaction, send_message, ai_response) 246 | 247 | @client.tree.command(name="recruiting_email", description="Generate recuiting email") 248 | async def recruiting_email(interaction: discord.Interaction, *, prompt: str): 249 | if interaction.user == client.user: 250 | return 251 | await interaction.response.defer() 252 | ai_response = notion_ai.recruiting_email(prompt) 253 | send_message = f"[recruiting_email] prompt: {prompt}" 254 | await sender.send_message(interaction, send_message, ai_response) 255 | 256 | @client.tree.command(name="pros_cons_list", description="Generate prons and cons list") 257 | async def pros_cons_list(interaction: discord.Interaction, *, prompt: str): 258 | if interaction.user == client.user: 259 | return 260 | await interaction.response.defer() 261 | ai_response = notion_ai.pros_cons_list(prompt) 262 | send_message = f"[pros_cons_list] prompt: {prompt}" 263 | await sender.send_message(interaction, send_message, ai_response) 264 | 265 | client.run(os.getenv('DISCORD_TOKEN')) 266 | 267 | 268 | if __name__ == '__main__': 269 | logger.info('Server Run') 270 | keep_alive() 271 | run() 272 | -------------------------------------------------------------------------------- /notionai/__init__.py: -------------------------------------------------------------------------------- 1 | # ruff: noqa 2 | from .notionai import NotionAI, NotionAIStream 3 | from .enums import TopicEnum, TranslateLanguageEnum, PromptTypeEnum, ToneEnum 4 | -------------------------------------------------------------------------------- /notionai/enums.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class ExtendedEnum(Enum): 5 | @classmethod 6 | def list(cls): 7 | return list(map(lambda c: c.value, cls)) 8 | 9 | @classmethod 10 | def list_name(cls): 11 | return list(map(lambda c: c.name, cls)) 12 | 13 | 14 | class TopicEnum(ExtendedEnum): 15 | brainstorm_ideas = "brainstormIdeas" 16 | blog_post = "blogPost" 17 | outline = "outline" 18 | social_media_post = "socialMediaPost" 19 | press_release = "pressRelease" 20 | creative_story = "creativeStory" 21 | essay = "essay" 22 | poem = "poem" 23 | meeting_agenda = "meetingAgenda" 24 | pros_cons_list = "prosConsList" 25 | job_description = "jobDescription" 26 | sales_email = "salesEmail" 27 | recruiting_email = "recruitingEmail" 28 | 29 | 30 | class TranslateLanguageEnum(ExtendedEnum): 31 | english = "english" 32 | korean = "korean" 33 | chinese = "chinese" 34 | japanese = "japanese" 35 | spanish = "spanish" 36 | russiab = "russiab" 37 | french = "french" 38 | german = "german" 39 | italian = "italian" 40 | portuguese = "portuguese" 41 | dutch = "dutch" 42 | indonesia = "indonesia" 43 | tagalog = "tagalog" 44 | vietnamese = "vietnamese" 45 | 46 | 47 | class PromptTypeEnum(ExtendedEnum): 48 | help_me_write = "helpMeWrite" 49 | continue_writing = "continueWriting" 50 | change_tone = "changeTone" 51 | summarize = "summarize" 52 | improve_writing = "improveWriting" 53 | fix_spelling_grammar = "fixSpellingGrammar" 54 | translate = "translate" 55 | explain_this = "explainThis" 56 | make_longer = "makeLonger" 57 | make_shorter = "makeShorter" 58 | find_action_items = "findActionItems" 59 | simplify_language = "simplifyLanguage" 60 | help_me_edit = "helpMeEdit" 61 | 62 | 63 | class ToneEnum(ExtendedEnum): 64 | professional = "professional" 65 | casual = "casual" 66 | straight_forward = "straightforward" 67 | confident = "confident" 68 | friendly = "friendly" -------------------------------------------------------------------------------- /notionai/notionai.py: -------------------------------------------------------------------------------- 1 | import json 2 | import uuid 3 | 4 | import requests 5 | 6 | from notionai.enums import PromptTypeEnum, ToneEnum, TopicEnum, TranslateLanguageEnum 7 | 8 | MODEL = "openai-3" 9 | URL = "https://www.notion.so/api/v3/getCompletion" 10 | 11 | 12 | class NotionAIBase(object): 13 | stream = False 14 | 15 | def __init__( 16 | self, 17 | token: str, 18 | space_id: str, 19 | model: str = MODEL, 20 | ) -> None: 21 | """Init NotionAI 22 | Args: 23 | token (str): Notion token_v2 24 | space_id (str): Notion workspace id 25 | model (str, optional): AI model. Default to openai-1.1 26 | stream (bool, optional): use stream result. Defaults to False. 27 | pageTitle (str, optional): Title for your content. Defaults to PAGE_TITLE. 28 | """ 29 | self.token = token 30 | self.space_id = space_id 31 | self.model = model 32 | self.is_space_permission = False 33 | self.url = URL 34 | 35 | def _request(self, content: dict) -> str: 36 | payload = { 37 | "id": self._get_id(), 38 | "model": self.model, 39 | "spaceId": self.space_id, 40 | "isSpacePermission": self.is_space_permission, 41 | "context": content, 42 | } 43 | 44 | cookies = [ 45 | "token_v2=" + self.token, 46 | ] 47 | 48 | headers = { 49 | "accept": "application/json", 50 | "Cookie": "; ".join(cookies), 51 | "Content-Type": "application/json", 52 | } 53 | 54 | return requests.post( 55 | self.url, json=payload, headers=headers, stream=self.stream 56 | ) 57 | 58 | def _post(self, content: dict) -> str: 59 | r = self._request(content) 60 | data = r.text.split("\n") 61 | res = [self._parse_resp_line(d) for d in data] 62 | return "".join(res).strip("\n") 63 | 64 | def _parse_resp_line(self, line): 65 | if line: 66 | try: 67 | data = json.loads(line) 68 | if data["type"] == "success": 69 | return data["completion"] 70 | except Exception as e: 71 | print(f"data: {line}, error: {e}") 72 | return "" 73 | 74 | def _get_id(self) -> str: 75 | return str(uuid.uuid4()) 76 | 77 | 78 | class NotionAI(NotionAIBase): 79 | def writing_with_topic(self, topic: TopicEnum, prompt: str) -> str: 80 | """Writing for special topic 81 | Args: 82 | topic (TopicEnum): the special topic 83 | prompt (str): prompt for writing 84 | Returns: 85 | str: Response from NotionAI 86 | """ 87 | content = {"type": topic.value, "topic": prompt} 88 | return self._post(content) 89 | 90 | def writing_with_prompt( 91 | self, 92 | prompt_type: PromptTypeEnum, 93 | context: str, 94 | page_title: str = "", 95 | ) -> str: 96 | """Writing with special prompt, like summarize, explain_this, improve_writing 97 | Args: 98 | prompt_type (PromptTypeEnum): special prompt 99 | context (str): the context for your writing 100 | page_title (str, optional): I am not sure about this. Defaults to "" 101 | Returns: 102 | str: Response from NotionAI 103 | """ 104 | if prompt_type == PromptTypeEnum.continue_writing: 105 | return self.continue_write(context, page_title) 106 | 107 | if prompt_type in { 108 | PromptTypeEnum.help_me_write, 109 | PromptTypeEnum.help_me_edit, 110 | PromptTypeEnum.translate, 111 | PromptTypeEnum.change_tone, 112 | }: 113 | raise ValueError("Please use the specific method for this prompt type") 114 | content = { 115 | "type": prompt_type.value, 116 | "pageTitle": page_title, 117 | "selectedText": context, 118 | } 119 | return self._post(content) 120 | 121 | def help_me_write( 122 | self, prompt: str, context: str, page_title: str = "", rest_content: str = "" 123 | ) -> str: 124 | """Help me write, ask AI to write for you 125 | Args: 126 | prompt (str): your prompt, could be anything 127 | context (str): context for your writing 128 | page_title (str, optional): not sure. Defaults to "". 129 | rest_content (str, optional): more context. Defaults to "". 130 | Returns: 131 | str: Response from NotionAI 132 | """ 133 | content = { 134 | "type": PromptTypeEnum.help_me_write.value, 135 | "prompt": prompt, 136 | "pageTitle": page_title, 137 | "previousContent": context, 138 | "restContent": rest_content, 139 | } 140 | return self._post(content) 141 | 142 | def continue_write( 143 | self, context: str, page_title: str = "", rest_content: str = "" 144 | ) -> str: 145 | """Continue writing, generating more 146 | Args: 147 | context (str): context for continue 148 | page_title (str, optional): not sure. Defaults to "". 149 | rest_content (str, optional): more context. Defaults to "". 150 | Returns: 151 | str: Response from NotionAI 152 | """ 153 | content = { 154 | "type": PromptTypeEnum.continue_writing.value, 155 | "pageTitle": page_title, 156 | "previousContent": context, 157 | "restContent": rest_content, 158 | } 159 | return self._post(content) 160 | 161 | def help_me_edit(self, prompt: str, context: str, page_title: str = "") -> str: 162 | """Help me edit somethings, it will change the current context 163 | Args: 164 | prompt (str): your prompt, could be anything 165 | context (str): context to edit 166 | page_title (str, optional): not sure. Defaults to "". 167 | Returns: 168 | str: Response from NotionAI 169 | """ 170 | 171 | content = { 172 | "type": PromptTypeEnum.help_me_edit.value, 173 | "pageTitle": page_title, 174 | "prompt": prompt, 175 | "selectedText": context, 176 | } 177 | return self._post(content) 178 | 179 | def translate(self, language: TranslateLanguageEnum, context: str) -> str: 180 | """Use NotionAI to translate your context 181 | Args: 182 | language (TranslateLanguageEnum): target language 183 | context (str): context to translate 184 | Returns: 185 | str: translate result 186 | """ 187 | content = { 188 | "type": PromptTypeEnum.translate.value, 189 | "text": context, 190 | "language": language.value, 191 | } 192 | return self._post(content) 193 | 194 | def change_tone(self, context: str, tone: ToneEnum) -> str: 195 | """Change the tone of your context 196 | Args: 197 | context (str): context to change 198 | tone (ToneEnum): target tone 199 | Returns: 200 | str: Response from NotionAI 201 | """ 202 | content = { 203 | "type": "changeTone", 204 | "tone": tone.value, 205 | "text": context, 206 | } 207 | return self._post(content) 208 | 209 | def summarize(self, context: str, page_title: str = "") -> str: 210 | return self.writing_with_prompt(PromptTypeEnum.summarize, context) 211 | 212 | def improve_writing(self, context: str, page_title: str = "") -> str: 213 | return self.writing_with_prompt(PromptTypeEnum.improve_writing, context) 214 | 215 | def fix_spelling_grammar(self, context: str, page_title: str = "") -> str: 216 | return self.writing_with_prompt( 217 | PromptTypeEnum.fix_spelling_grammar, 218 | context, 219 | page_title=page_title, 220 | ) 221 | 222 | def explain_this(self, context: str, page_title: str = "") -> str: 223 | return self.writing_with_prompt( 224 | PromptTypeEnum.explain_this, context, page_title=page_title 225 | ) 226 | 227 | def make_longer(self, context: str, page_title: str = "") -> str: 228 | return self.writing_with_prompt( 229 | PromptTypeEnum.make_longer, context, page_title=page_title 230 | ) 231 | 232 | def make_shorter(self, context: str, page_title: str = "") -> str: 233 | return self.writing_with_prompt( 234 | PromptTypeEnum.make_shorter, context, page_title=page_title 235 | ) 236 | 237 | def find_action_items(self, context: str, page_title: str = "") -> str: 238 | return self.writing_with_prompt( 239 | PromptTypeEnum.find_action_items, context, page_title=page_title 240 | ) 241 | 242 | def simplify_language(self, context: str, page_title: str = "") -> str: 243 | return self.writing_with_prompt( 244 | PromptTypeEnum.simplify_language, context, page_title=page_title 245 | ) 246 | 247 | def blog_post(self, prompt: str) -> str: 248 | return self.writing_with_topic(TopicEnum.blog_post, prompt) 249 | 250 | def brainstorm_ideas(self, prompt: str) -> str: 251 | return self.writing_with_topic(TopicEnum.brainstorm_ideas, prompt) 252 | 253 | def outline(self, prompt: str) -> str: 254 | return self.writing_with_topic(TopicEnum.outline, prompt) 255 | 256 | def social_media_post(self, prompt: str) -> str: 257 | return self.writing_with_topic(TopicEnum.social_media_post, prompt) 258 | 259 | def creative_story(self, prompt: str) -> str: 260 | return self.writing_with_topic(TopicEnum.creative_story, prompt) 261 | 262 | def poem(self, prompt: str) -> str: 263 | return self.writing_with_topic(TopicEnum.poem, prompt) 264 | 265 | def essay(self, prompt: str) -> str: 266 | return self.writing_with_topic(TopicEnum.essay, prompt) 267 | 268 | def meeting_agenda(self, prompt: str) -> str: 269 | return self.writing_with_topic(TopicEnum.meeting_agenda, prompt) 270 | 271 | def press_release(self, prompt: str) -> str: 272 | return self.writing_with_topic(TopicEnum.press_release, prompt) 273 | 274 | def job_description(self, prompt: str) -> str: 275 | return self.writing_with_topic(TopicEnum.job_description, prompt) 276 | 277 | def sales_email(self, prompt: str) -> str: 278 | return self.writing_with_topic(TopicEnum.sales_email, prompt) 279 | 280 | def recruiting_email(self, prompt: str) -> str: 281 | return self.writing_with_topic(TopicEnum.recruiting_email, prompt) 282 | 283 | def pros_cons_list(self, prompt: str) -> str: 284 | self.writing_with_topic(TopicEnum.pros_cons_list, prompt) 285 | 286 | 287 | class NotionAIStream(NotionAI): 288 | stream = True 289 | 290 | def _post(self, content: dict) -> str: 291 | r = self._request(content) 292 | for line in r.text.splitlines(): 293 | yield self._parse_resp_line(line) -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | discord.py==2.1.1 2 | python-dotenv==0.21.1 3 | Flask==2.2.3 -------------------------------------------------------------------------------- /src/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheExplainthis/NotionAI-Discord-Bot/1bf02ec7cb4c5513c17deeed1c5aa38e3a11fe6c/src/__init__.py -------------------------------------------------------------------------------- /src/discordBot.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from src.logger import logger 3 | 4 | intents = discord.Intents.default() 5 | intents.message_content = True 6 | 7 | 8 | class DiscordClient(discord.Client): 9 | def __init__(self) -> None: 10 | super().__init__(intents=intents) 11 | self.synced = False 12 | self.added = False 13 | self.tree = discord.app_commands.CommandTree(self) 14 | self.activity = discord.Activity(type=discord.ActivityType.watching, name="/chat | /reset | /imagine") 15 | 16 | async def on_ready(self): 17 | await self.wait_until_ready() 18 | logger.info("Syncing") 19 | if not self.synced: 20 | await self.tree.sync() 21 | self.synced = True 22 | if not self.added: 23 | self.added = True 24 | logger.info(f"Synced, {self.user} is running!") 25 | 26 | 27 | class Sender(): 28 | async def send_message(self, interaction, send, receive): 29 | try: 30 | user_id = interaction.user.id 31 | response = f'> **{send}** - <@{str(user_id)}> \n\n {receive}' 32 | await interaction.followup.send(response) 33 | logger.info(f"{user_id} sent: {send}, response: {receive}") 34 | except Exception as e: 35 | await interaction.followup.send('> **Error: Something went wrong, please try again later!**') 36 | logger.exception(f"Error while sending:{send} in chatgpt model, error: {e}") 37 | 38 | async def send_image(self, interaction, send, receive): 39 | try: 40 | user_id = interaction.user.id 41 | response = f'> **{send}** - <@{str(user_id)}> \n\n' 42 | await interaction.followup.send(response) 43 | await interaction.followup.send(receive) 44 | logger.info(f"{user_id} sent: {send}, response: {receive}") 45 | except Exception as e: 46 | await interaction.followup.send('> **Error: Something went wrong, please try again later!**') 47 | logger.exception(f"Error while sending:{send} in dalle model, error: {e}") 48 | -------------------------------------------------------------------------------- /src/logger.py: -------------------------------------------------------------------------------- 1 | import os 2 | import logging 3 | import logging.handlers 4 | 5 | 6 | class CustomFormatter(logging.Formatter): 7 | __LEVEL_COLORS = [ 8 | (logging.DEBUG, '\x1b[40;1m'), 9 | (logging.INFO, '\x1b[34;1m'), 10 | (logging.WARNING, '\x1b[33;1m'), 11 | (logging.ERROR, '\x1b[31m'), 12 | (logging.CRITICAL, '\x1b[41m'), 13 | ] 14 | __FORMATS = None 15 | 16 | @classmethod 17 | def get_formats(cls): 18 | if cls.__FORMATS is None: 19 | cls.__FORMATS = { 20 | level: logging.Formatter( 21 | f'\x1b[30;1m%(asctime)s\x1b[0m {color}%(levelname)-8s\x1b[0m \x1b[35m%(name)s\x1b[0m -> %(message)s', 22 | '%Y-%m-%d %H:%M:%S' 23 | ) 24 | for level, color in cls.__LEVEL_COLORS 25 | } 26 | return cls.__FORMATS 27 | 28 | def format(self, record): 29 | formatter = self.get_formats().get(record.levelno) 30 | if formatter is None: 31 | formatter = self.get_formats()[logging.DEBUG] 32 | if record.exc_info: 33 | text = formatter.formatException(record.exc_info) 34 | record.exc_text = f'\x1b[31m{text}\x1b[0m' 35 | 36 | output = formatter.format(record) 37 | record.exc_text = None 38 | return output 39 | 40 | 41 | class LoggerFactory: 42 | @staticmethod 43 | def create_logger(formatter, handlers): 44 | logger = logging.getLogger('chatgpt_logger') 45 | logger.setLevel(logging.INFO) 46 | for handler in handlers: 47 | handler.setLevel(logging.DEBUG) 48 | handler.setFormatter(formatter) 49 | logger.addHandler(handler) 50 | return logger 51 | 52 | 53 | class FileHandler(logging.FileHandler): 54 | def __init__(self, log_file): 55 | os.makedirs(os.path.dirname(log_file), exist_ok=True) 56 | super().__init__(log_file) 57 | 58 | 59 | class ConsoleHandler(logging.StreamHandler): 60 | pass 61 | 62 | 63 | formatter = CustomFormatter() 64 | file_handler = FileHandler('./logs') 65 | console_handler = ConsoleHandler() 66 | logger = LoggerFactory.create_logger(formatter, [file_handler, console_handler]) 67 | -------------------------------------------------------------------------------- /src/server.py: -------------------------------------------------------------------------------- 1 | from threading import Thread 2 | from flask import Flask 3 | 4 | app = Flask('NotionAI-Discord-Bot') 5 | 6 | 7 | @app.route('/') 8 | def home(): 9 | return "Hello World" 10 | 11 | 12 | def server_run(): 13 | app.run(host='0.0.0.0', port=8080) 14 | 15 | 16 | def keep_alive(): 17 | t = Thread(target=server_run) 18 | t.start() 19 | --------------------------------------------------------------------------------