├── .gitignore ├── LICENSE ├── Procfile ├── README.md ├── chatbot ├── __init__.py ├── __main__.py └── bot │ ├── chat_bot.py │ └── database │ ├── __init__.py │ └── chatbot_db.py ├── generate_session.py ├── requirements.txt ├── runtime.txt ├── sample_config.ini └── string_session.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | config.ini 3 | .vscode -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Real Phoenix 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 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | worker: python3 -m chatbot -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # IMPORTANT 2 | Since the API has been [discontinued](https://t.me/Intellivoid/559), this will no longer work. 3 | 4 | # About 5 | A fun telegram userbot written in python3 using [Intellivoid](https://github.com/intellivoid)'s Coffeehouse API. 6 | Written by [this person](https://t.me/TheRealPhoenix)! 7 | ## Installation 8 | Open up your terminal and run these commands. 9 | 10 | • ```git clone https://github.com/rsktg/Chatbot.git``` 11 | 12 | • ```cd Chatbot``` 13 | 14 | • ```pip install -r requirements.txt``` 15 | 16 | • Now make a copy of ```sample_config.ini``` and rename it to ```config.ini``` and enter your ```api_id``` and ```api_hash```. These can be obtained from [here](https://my.telegram.org). 17 | 18 | ### Non-Heroku users 19 | Edit ```NAME``` in ```generate_session.py``` 20 | 21 | • ```python3 generate_session.py``` 22 | 23 | ### Heroku users 24 | • ```python3 string_session.py``` 25 | 26 | You'll be asked to enter your phone number and 2FA password (if any). Copy and save the string session you get in the end. 27 | 28 | ### Two possible ways of going forward 29 | You can either use environment variables or edit the config.ini file further. 30 | 31 | If you're going to use environment variables, then add a variable named ```ENV``` and set the value to anything you want. 32 | Now you can use environment variables. 33 | 34 | Add the following ones as well. 35 | 36 | • ```SESSION_NAME```: If you ran ```generate_session.py```, the name of the session you created. 37 | 38 | • ```STRING_SESSION```: The string session (Only for Heroku users). 39 | 40 | • ```CF_API_KEY```: You can get this API key from [here](https://coffeehouse.intellivoid.net) 41 | 42 | • ```DATABASE_URL```: The URL of your SQL database. It should look something like this - ```sqldbtype://username:pw@hostname:port/db_name```. 43 | PostgreSQL is recommended. 44 | 45 | • ```NAME```: Your bot will reply to the AI-enabled users everytime this name is said. 46 | 47 | However, if you're gonna use ```config.ini```, go ahead and set the values given above with the exception of ```ENV```. 48 | 49 | ## Running the bot 50 | After setting the required environment variables/editing the ```config.ini``` file, run ```python3 -m chatbot```. 51 | 52 | Congrats, your bot should now be up! 53 | 54 | ## Credits 55 | • [Intellivoid](https://github.com/intellivoid) for providing the API used for this project. 56 | 57 | • [pyrogram](https://github.com/pyrogram) - the library used for this project. 58 | -------------------------------------------------------------------------------- /chatbot/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import logging 4 | 5 | from pyrogram import Client 6 | 7 | # Enable logging 8 | logging.basicConfig( 9 | format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", 10 | level=logging.INFO) 11 | 12 | LOGGER = logging.getLogger(__name__) 13 | 14 | # If python version < 3.6, quit 15 | if sys.version_info[0] < 3 or sys.version_info[1] < 6: 16 | LOGGER.error("You need at least python v3.6.x\nBot quitting.") 17 | quit(1) 18 | 19 | ENV = bool(os.environ.get("ENV", False)) 20 | 21 | if ENV: 22 | SESSION_NAME = os.environ.get("SESSION_NAME") 23 | STRING_SESSION = os.environ.get("STRING_SESSION") 24 | CF_API_KEY = os.environ.get("CF_API_KEY") 25 | DATABASE_URL = os.environ.get("DATABASE_URL") 26 | NAME = os.environ.get("NAME") 27 | 28 | else: 29 | from configparser import ConfigParser 30 | 31 | parser = ConfigParser() 32 | parser.read("config.ini") 33 | config = parser["config"] 34 | 35 | SESSION_NAME = config.get("SESSION_NAME") 36 | STRING_SESSION = config.get("STRING_SESSION") 37 | CF_API_KEY = config.get("CF_API_KEY") 38 | DATABASE_URL = config.get("DATABASE_URL") 39 | NAME = config.get("NAME") 40 | 41 | if not SESSION_NAME and not STRING_SESSION: 42 | print("There's no session set up!") 43 | quit(1) 44 | if SESSION_NAME: 45 | SESSION = SESSION_NAME 46 | elif STRING_SESSION: 47 | SESSION = STRING_SESSION 48 | app = Client(SESSION) 49 | -------------------------------------------------------------------------------- /chatbot/__main__.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import importlib 3 | 4 | from pyrogram import idle 5 | from chatbot import app, LOGGER 6 | 7 | 8 | importlib.import_module("chatbot.bot.chat_bot") 9 | 10 | 11 | async def start_bot() -> None: 12 | await app.start() 13 | LOGGER.info( 14 | "Simple chatbot written using the pyrogram library.\n " 15 | "Uses Intellivoid's Coffeehouse API.\n" 16 | "Written by @TheRealPhoenix on Telegram." 17 | ) 18 | LOGGER.info("Your bot is now online. Check .help for help!") 19 | await idle() 20 | 21 | asyncio.get_event_loop().run_until_complete(start_bot()) 22 | -------------------------------------------------------------------------------- /chatbot/bot/chat_bot.py: -------------------------------------------------------------------------------- 1 | from time import time 2 | from pyrogram import filters, Client 3 | from pyrogram.types import Message 4 | 5 | from coffeehouse.lydia import LydiaAI 6 | from coffeehouse.api import API 7 | from coffeehouse.exception import CoffeeHouseError as CFError 8 | 9 | from chatbot import app, LOGGER, CF_API_KEY, NAME 10 | import chatbot.bot.database.chatbot_db as db 11 | 12 | 13 | CoffeeHouseAPI = API(CF_API_KEY) 14 | api_client = LydiaAI(CoffeeHouseAPI) 15 | 16 | 17 | HELP_TEXT = """• Reply `.adduser` to someone to enable the chatbot for that person! 18 | • Reply `.rmuser` to someone to stop the chatbot for them! 19 | Have fun!""" 20 | 21 | 22 | @app.on_message(filters.me & filters.command("start", ".")) 23 | async def start(_, message: Message) -> None: 24 | """Check if bot is up.""" 25 | await message.edit_text("I'm alive! :3") 26 | 27 | 28 | @app.on_message(filters.me & filters.command("help", ".")) 29 | async def help(_, message: Message) -> None: 30 | """Gives help on how to use the bot.""" 31 | await message.edit_text(HELP_TEXT, parse_mode="md") 32 | 33 | 34 | @app.on_message(filters.me & filters.command("adduser", ".")) 35 | async def add_user(_, message: Message) -> None: 36 | """Enable AI for a user.""" 37 | if not message.reply_to_message: 38 | message.edit_text( 39 | "Reply to someone to enable chatbot for that person!") 40 | return 41 | user_id = message.reply_to_message.from_user.id 42 | is_user = db.is_user(user_id) 43 | if not is_user: 44 | ses = api_client.create_session() 45 | ses_id = str(ses.id) 46 | expires = str(ses.expires) 47 | db.set_ses(user_id, ses_id, expires) 48 | await message.edit_text("AI enabled for user successfully!") 49 | LOGGER.info(f"AI enabled for user - {user_id}") 50 | else: 51 | await message.edit_text("AI is already enabled for this user!") 52 | 53 | 54 | @app.on_message(filters.me & filters.command("rmuser", ".")) 55 | async def rem_user(_, message: Message) -> None: 56 | """Remove AI for a user.""" 57 | if not message.reply_to_message: 58 | message.edit_text("You've gotta reply to someone!") 59 | return 60 | user_id = message.reply_to_message.from_user.id 61 | is_user = db.is_user(user_id) 62 | if not is_user: 63 | await message.edit_text( 64 | "AI isn't enabled for this user in the first place!") 65 | else: 66 | db.rem_user(user_id) 67 | await message.edit_text("AI disabled for this user successfully!") 68 | LOGGER.info(f"AI disabled for user - {user_id}") 69 | 70 | 71 | def check_message(msg: Message) -> bool: 72 | """Check if a message needs to be replied to.""" 73 | reply_msg = msg.reply_to_message 74 | if NAME.lower() in msg.text.lower(): 75 | return True 76 | if reply_msg and reply_msg.from_user is not None: 77 | if reply_msg.from_user.is_self: 78 | return True 79 | return False 80 | 81 | 82 | @app.on_message(filters.text) 83 | async def chatbot(app: Client, message: Message) -> None: 84 | msg = message 85 | if not check_message(msg): 86 | return 87 | user_id = msg.from_user.id 88 | if user_id not in db.USERS: 89 | return 90 | sesh, exp = db.get_ses(user_id) 91 | query = msg.text 92 | if int(exp) < time(): 93 | ses = api_client.create_session() 94 | ses_id = str(ses.id) 95 | expires = str(ses.expires) 96 | db.set_ses(user_id, ses_id, expires) 97 | sesh, exp = ses_id, expires 98 | 99 | try: 100 | await msg.reply_chat_action("typing") 101 | response = api_client.think_thought(sesh, query) 102 | await msg.reply_text(response) 103 | except CFError as e: 104 | await app.send_message( 105 | chat_id=msg.chat.id, 106 | text=f"An error occurred:\n`{e}`", 107 | parse_mode="md" 108 | ) 109 | -------------------------------------------------------------------------------- /chatbot/bot/database/__init__.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import create_engine 2 | from sqlalchemy.ext.declarative import declarative_base 3 | from sqlalchemy.orm import sessionmaker, scoped_session 4 | 5 | from chatbot import DATABASE_URL 6 | 7 | 8 | def start() -> scoped_session: 9 | engine = create_engine(DATABASE_URL) 10 | BASE.metadata.bind = engine 11 | BASE.metadata.create_all(engine) 12 | return scoped_session(sessionmaker(bind=engine, autoflush=False)) 13 | 14 | 15 | BASE = declarative_base() 16 | SESSION = start() 17 | -------------------------------------------------------------------------------- /chatbot/bot/database/chatbot_db.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import Column, String, Integer 4 | 5 | from chatbot.bot.database import BASE, SESSION 6 | 7 | 8 | class Chatbot(BASE): 9 | __tablename__ = "chatbot" 10 | user_id = Column(Integer, primary_key=True) 11 | ses_id = Column(String(64)) 12 | expires = Column(String(10)) 13 | 14 | def __init__(self, user_id, ses_id, expires): 15 | self.user_id = user_id 16 | self.ses_id = ses_id 17 | self.expires = expires 18 | 19 | 20 | Chatbot.__table__.create(checkfirst=True) 21 | 22 | INSERTION_LOCK = threading.RLock() 23 | USERS = set() 24 | 25 | 26 | def is_user(user_id): 27 | try: 28 | user = SESSION.query(Chatbot).get(int(user_id)) 29 | if user: 30 | return True 31 | else: 32 | return False 33 | finally: 34 | SESSION.close() 35 | 36 | 37 | def set_ses(user_id, ses_id, expires): 38 | with INSERTION_LOCK: 39 | autochat = SESSION.query(Chatbot).get(int(user_id)) 40 | if not autochat: 41 | autochat = Chatbot(int(user_id), str(ses_id), str(expires)) 42 | else: 43 | autochat.ses_id = str(ses_id) 44 | autochat.expires = str(expires) 45 | 46 | SESSION.add(autochat) 47 | SESSION.commit() 48 | __load_userid_list() 49 | 50 | 51 | def get_ses(user_id): 52 | autochat = SESSION.query(Chatbot).get(int(user_id)) 53 | sesh = "" 54 | exp = "" 55 | if autochat: 56 | sesh = str(autochat.ses_id) 57 | exp = str(autochat.expires) 58 | 59 | SESSION.close() 60 | return sesh, exp 61 | 62 | 63 | def rem_user(user_id): 64 | with INSERTION_LOCK: 65 | autochat = SESSION.query(Chatbot).get(int(user_id)) 66 | if autochat: 67 | SESSION.delete(autochat) 68 | 69 | SESSION.commit() 70 | __load_userid_list() 71 | 72 | 73 | def __load_userid_list(): 74 | global USERS 75 | try: 76 | USERS = {int(x.user_id) for x in SESSION.query(Chatbot).all()} 77 | finally: 78 | SESSION.close() 79 | 80 | 81 | __load_userid_list() 82 | -------------------------------------------------------------------------------- /generate_session.py: -------------------------------------------------------------------------------- 1 | # Generate session files for permanent session. 2 | # Not suitable for ephemeral file systems like Heroku. 3 | import asyncio 4 | from pyrogram import Client 5 | 6 | 7 | # Choose name for session 8 | NAME = "Phoenix" 9 | 10 | 11 | async def create_session() -> None: 12 | async with Client(NAME): 13 | pass 14 | 15 | asyncio.run(create_session()) 16 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | coffeehouse 2 | configparser 3 | psycopg2 4 | Pyrogram==1.0.7 5 | sqlalchemy 6 | TgCrypto==1.2.0 7 | -------------------------------------------------------------------------------- /runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.8.1 -------------------------------------------------------------------------------- /sample_config.ini: -------------------------------------------------------------------------------- 1 | [pyrogram] 2 | # Get api_id and api_hash from my.telegram.org 3 | # If the website doesn't open, use a VPN and set region to USA. 4 | api_id = 12345 5 | api_hash = 0123456789abcdef0123456789abcdef 6 | [config] 7 | # Name of session if you're using session files 8 | SESSION_NAME = 9 | # Else run python3 string_session.py to generate session. 10 | STRING_SESSION = 11 | # Get your API key from t.me/IntellivoidDev 12 | CF_API_KEY = 13 | # URL of your SQL database. Postgres is recommended. 14 | DATABASE_URL = 15 | # If someone sends this name, your bot will reply. 16 | NAME = 17 | -------------------------------------------------------------------------------- /string_session.py: -------------------------------------------------------------------------------- 1 | # This file is for generating a string session only 2 | # Please copy and save the string session 3 | # Note that you need to have api_id and api_hash set in config.ini before 4 | # executing this 5 | import asyncio 6 | 7 | from pyrogram import Client 8 | 9 | 10 | async def get_session() -> None: 11 | async with Client(":memory:") as bot: 12 | print(bot.export_session_string()) 13 | 14 | asyncio.run(get_session()) 15 | --------------------------------------------------------------------------------