├── .gitignore ├── preview.gif ├── .env.example ├── commands ├── newchat.js └── start.js ├── app └── utils.js ├── package.json ├── LICENSE.MD ├── README.md └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | ./node_modules 3 | -------------------------------------------------------------------------------- /preview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gavin-chua/chatgpt-telegram/HEAD/preview.gif -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | OPENAI_EMAIL="YOUR_EMAIL" 2 | OPENAI_PASSWORD="YOUR_PASSWORD" 3 | TELEGRAM_TOKEN="YOUR_TELEGRAM_TOKEN" -------------------------------------------------------------------------------- /commands/newchat.js: -------------------------------------------------------------------------------- 1 | export async function run(bot, msg, args, {db}) { 2 | db.delete(msg.chat.id) 3 | await bot.sendMessage(msg.chat.id, 'Your old chat has been deleted.', { 4 | reply_to_message_id: msg.message_id 5 | }) 6 | } -------------------------------------------------------------------------------- /commands/start.js: -------------------------------------------------------------------------------- 1 | export async function run(bot, msg) { 2 | const text = "Hello, This bot gives you AI-powered answers using ChatGPT. \nAvailable commands are:\n\n/newchat - Start a new chat" 3 | await bot.sendMessage(msg.chat.id, text, { 4 | reply_to_message_id: msg.message_id 5 | }) 6 | } -------------------------------------------------------------------------------- /app/utils.js: -------------------------------------------------------------------------------- 1 | import str from 'superscript-number'; 2 | 3 | export function sleep(ms) { 4 | return new Promise(resolve => setTimeout(resolve, ms)); 5 | } 6 | 7 | export function onStart(user) { 8 | return new Promise(resolve => setTimeout(resolve, user)); 9 | } 10 | 11 | export function convertExponentialToSymbol(text) { 12 | return text.replace(/\^\d+/g, (match) => str(match)); 13 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chatgpt-telegram", 3 | "version": "1.0.0", 4 | "description": "ChatGPT bot for Telegram", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "ArdaGnsrn", 10 | "license": "ISC", 11 | "type": "module", 12 | "dependencies": { 13 | "bee-queue": "^1.5.0", 14 | "chatgpt": "^3.3.9", 15 | "dotenv": "^16.0.3", 16 | "fs": "^0.0.1-security", 17 | "node-telegram-bot-api": "^0.60.0", 18 | "ora": "^6.1.2", 19 | "simple-json-db": "^2.0.0", 20 | "superscript-number": "^1.0.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /LICENSE.MD: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Arda Günsüren 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ChatGPT Telegram Bot 2 | 3 | ChatGPT Telegram Bot Preview 4 | 5 | > **Note:** You need Redis Server to run the bot. 6 | 7 | > **Note:** This bot uses a [browser-based ChatGPT API](https://github.com/transitive-bullshit/chatgpt-api), please make sure you have Node.js >= 18 and a Chromium-based browser installed. 8 | 9 | > **Note:** The bot uses Google Account because of Recaptcha. The app asks you to pass a captcha verification if you don't log in with google. 10 | 11 | ## Usage 12 | 13 | 1. Clone the repository 14 | 2. Copy `.env.example` to `.env` and fill in the values 15 | 1. `OPENAI_EMAIL` and `OPENAI_PASSWORD` is your Google account credentials. 16 | 2. `TELEGRAM_TOKEN` is your Telegram bot token. If you don't have one, you can create one using [@BotFather](https://t.me/BotFather). 17 | 3. Run `npm install` to install dependencies 18 | 4. Run `npm start` to start the bot 19 | 5. Send `/start` to the bot to start chatting 20 | 21 | ## Features 22 | 23 | - [X] Support multiple conversations, each chat has its own conversation. 24 | - [X] You can clear your conversation by sending `/newchat`. 25 | - [X] Queue system, if the bot is busy, it will queue your message and send it when it's free. 26 | - [X] Thinking effect. 27 | - [X] Rate limit to prevent spamming. 28 | - [X] Authentication using Google Account. 29 | - [X] Cloudflare captcha bypass. 30 | - [X] Convert exponential numbers to symbols. 31 | 32 | ## License -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import {ChatGPTAPIBrowser} from 'chatgpt' 2 | import * as dotenv from 'dotenv' 3 | import TelegramBot from 'node-telegram-bot-api' 4 | import {sleep, convertExponentialToSymbol} from './app/utils.js' 5 | import {oraPromise} from "ora"; 6 | import Queue from 'bee-queue'; 7 | 8 | const queue = new Queue('chatgpt'); 9 | import JSONdb from 'simple-json-db'; 10 | const db = new JSONdb('./app/storage.json'); 11 | 12 | import fs from "fs"; 13 | dotenv.config() 14 | const bot = new TelegramBot(process.env.TELEGRAM_TOKEN, {polling: true}); 15 | 16 | const api = new ChatGPTAPIBrowser({ 17 | email: process.env.OPENAI_EMAIL, 18 | password: process.env.OPENAI_PASSWORD, 19 | isGoogleLogin: true, 20 | }) 21 | await api.initSession() 22 | 23 | let commands = (await fs.promises.readdir("./commands")) 24 | .map((f) => { 25 | return { 26 | name: f.replace(".js", ""), 27 | module: import(`./commands/${f}`) 28 | } 29 | }) 30 | 31 | bot.on('message', async (msg) => { 32 | const chatId = msg.chat.id; 33 | const chatText = msg.text; 34 | 35 | if (chatText.startsWith("/")) { 36 | let splitText = chatText.split(" ", 1) 37 | let cmd = commands.find((c) => c.name === splitText[0].replace("/", "")) 38 | if (cmd) return (await cmd.module).run(bot, msg, splitText[1] ?? null, { 39 | db 40 | }) 41 | } 42 | 43 | const thingMsgId = (await bot.sendMessage(chatId, '🤔', { 44 | reply_to_message_id: msg.message_id 45 | })).message_id; 46 | 47 | const job = await queue.createJob({ 48 | chatId, 49 | thingMsgId, 50 | text: chatText 51 | }).save() 52 | 53 | job.on('failed', async () => { 54 | await job.retries(1).save(); 55 | }) 56 | }); 57 | 58 | 59 | queue.process(async function (job, done) { 60 | const {text, chatId, thingMsgId} = job.data 61 | try { 62 | const chatInfo = db.has(chatId) ? db.get(chatId) : {} 63 | const chatSettings = { 64 | conversationId: chatInfo.conversationId ?? undefined, 65 | parentMessageId: chatInfo.lastMessage ?? undefined 66 | } 67 | 68 | let result = await oraPromise(api.sendMessage(text, chatSettings), { 69 | text: "Processing Message: " + text 70 | }) 71 | 72 | let resp = result.response 73 | 74 | resp = convertExponentialToSymbol(resp) 75 | 76 | db.set(chatId, {conversationId: result.conversationId, lastMessage: result.messageId}) 77 | await bot.editMessageText(resp, { 78 | chat_id: chatId, 79 | message_id: thingMsgId, 80 | parse_mode: 'Markdown' 81 | }) 82 | return done() 83 | } catch (e) { 84 | console.log("Sleeping for 5 secs..") 85 | await sleep(5000) 86 | throw new Error("Error") 87 | } 88 | }); 89 | 90 | 91 | 92 | 93 | --------------------------------------------------------------------------------