3 | 4 | ## Deploy 5 | 6 | ### Deploy To Heroku Now: 7 | [](https://devilsheavenmf.github.io/Redirector/) 8 | 9 | ### Deploy in vps 10 | - Clone this repo 11 | ``` 12 | git clone https://github.com/sjmxaditi/ForceSub_Bot 13 | ``` 14 | - Change directory 15 | ``` 16 | cd ForceSub_Bot 17 | ``` 18 | - Install requirements 19 | ``` 20 | pip3 install -r requirements.txt 21 | ``` 22 | - setup configuration 23 | ``` 24 | Open Config.py in text Editor and set values 25 | ``` 26 | - Start the bot 27 | ``` 28 | python3 bot.py 29 | ``` 30 | ## Configs: 31 | - Read `Config.py` to know all configs 32 | 33 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Force Sub Bot", 3 | "description": "Python based bot to Force Users to sub a channel.", 4 | "logo": "", 5 | "keywords": [ 6 | "telegram", 7 | "pyrogram", 8 | "pyrogrambot", 9 | "telegram-bot", 10 | "Forcesub", 11 | "ForceSubBot" 12 | ], 13 | "repository": "https://github.com/Jigarvarma2005/ForceSub_Bot", 14 | "website": "https://t.me/UniversalBotsUpdate", 15 | "success_url": "https://t.me/UniversalBotsUpdate", 16 | "env": { 17 | "API_HASH": { 18 | "description": "You API HASH from my.telegram.org", 19 | "value": "", 20 | "required": true 21 | }, 22 | "APP_ID": { 23 | "description": "You APP ID from my.telegram.org", 24 | "value": "", 25 | "required": true 26 | }, 27 | "BOT_TOKEN": { 28 | "description": "Bot token, get it from @BotFather.", 29 | "value": "", 30 | "required": true 31 | }, 32 | "UPDATES_CHANNEL": { 33 | "description": "Your bot updates channel username without @ or leave empty", 34 | "value": "Techno_Trickop", 35 | "required": false 36 | }, 37 | "SUDO_USERS": { 38 | "description": "Sudo users (Goto @JVToolsBot and send /id to get your id)", 39 | "value": "5124507794", 40 | "required": false 41 | } 42 | }, 43 | "addons": [ 44 | { 45 | "options": { 46 | "version": "12" 47 | }, 48 | "plan": "heroku-postgresql" 49 | } 50 | ], 51 | "buildpacks": [ 52 | { 53 | "url": "heroku/python" 54 | } 55 | ] 56 | } 57 | -------------------------------------------------------------------------------- /bot.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from pyrogram import Client, idle 3 | from Config import Config 4 | 5 | plugins = dict( 6 | root="plugins" 7 | ) 8 | 9 | app = Client( 10 | 'ForceSubscribeRobot', 11 | bot_token = Config.BOT_TOKEN, 12 | api_id = Config.APP_ID, 13 | api_hash = Config.API_HASH, 14 | plugins=plugins 15 | ) 16 | 17 | app.start() 18 | idle() 19 | app.stop() -------------------------------------------------------------------------------- /plugins/forceSubscribe.py: -------------------------------------------------------------------------------- 1 | import time 2 | import logging 3 | from Config import Config 4 | from pyrogram import Client, filters 5 | from sql_helpers import forceSubscribe_sql as sql 6 | from pyrogram.types import ChatPermissions, InlineKeyboardMarkup, InlineKeyboardButton 7 | from pyrogram.errors.exceptions.bad_request_400 import UserNotParticipant, UsernameNotOccupied, ChatAdminRequired, PeerIdInvalid 8 | 9 | logging.basicConfig(level=logging.INFO) 10 | 11 | static_data_filter = filters.create(lambda _, __, query: query.data == "onUnMuteRequest") 12 | @Client.on_callback_query(static_data_filter) 13 | async def _onUnMuteRequest(client, cb): 14 | user_id = cb.from_user.id 15 | chat_id = cb.message.chat.id 16 | chat_db = sql.fs_settings(chat_id) 17 | if chat_db: 18 | channel = chat_db.channel 19 | chat_member = await client.get_chat_member(chat_id, user_id) 20 | if chat_member.restricted_by: 21 | if chat_member.restricted_by.id == (await client.get_me()).id: 22 | try: 23 | await client.get_chat_member(channel, user_id) 24 | await client.unban_chat_member(chat_id, user_id) 25 | if cb.message.reply_to_message.from_user.id == user_id: 26 | await cb.message.delete() 27 | except UserNotParticipant: 28 | await client.answer_callback_query(cb.id, text="❗ Join the mentioned 'channel' and press the 'UnMute Me' button again.", show_alert=True) 29 | else: 30 | await client.answer_callback_query(cb.id, text="❗ You are muted by admins for other reasons.", show_alert=True) 31 | else: 32 | if not (await client.get_chat_member(chat_id, (await client.get_me()).id)).status == 'administrator': 33 | await client.send_message(chat_id, f"❗ **{cb.from_user.mention} is trying to UnMute himself but i can't unmute him because i am not an admin in this chat add me as admin again.**\n__#Leaving this chat...__") 34 | await client.leave_chat(chat_id) 35 | else: 36 | await client.answer_callback_query(cb.id, text="❗ Warning: Don't click the button if you can speak freely.", show_alert=True) 37 | 38 | 39 | 40 | @Client.on_message((filters.text | filters.media) & ~filters.private & ~filters.edited, group=1) 41 | async def _check_member(client, message): 42 | chat_id = message.chat.id 43 | chat_db = sql.fs_settings(chat_id) 44 | if chat_db: 45 | user_id = message.from_user.id 46 | if not (await client.get_chat_member(chat_id, user_id)).status in ("administrator", "creator") and not user_id in Config.SUDO_USERS: 47 | channel = chat_db.channel 48 | if channel.startswith("-"): 49 | channel_url = await client.export_chat_invite_link(int(channel)) 50 | else: 51 | channel_url = f"https://t.me/{channel}" 52 | try: 53 | await client.get_chat_member(channel, user_id) 54 | except UserNotParticipant: 55 | try: 56 | sent_message = await message.reply_text( 57 | " {} , you are not subscribed to my channel yet. Please join using below button and press the UnMute Me button to unmute yourself.".format(message.from_user.mention, channel, channel), 58 | disable_web_page_preview=True, 59 | reply_markup=InlineKeyboardMarkup( 60 | [ 61 | [ 62 | InlineKeyboardButton("Subscribe My Channel", url=channel_url) 63 | ], 64 | [ 65 | InlineKeyboardButton("UnMute Me", callback_data="onUnMuteRequest") 66 | ] 67 | ] 68 | ) 69 | ) 70 | await client.restrict_chat_member(chat_id, user_id, ChatPermissions(can_send_messages=False)) 71 | except ChatAdminRequired: 72 | await sent_message.edit("❗ **I am not an admin here.**\n__Make me admin with ban user permission and add me again.\n#Leaving this chat...__") 73 | await client.leave_chat(chat_id) 74 | except ChatAdminRequired: 75 | await client.send_message(chat_id, text=f"❗ **I am not an admin in [channel]({channel_url})**\n__Make me admin in the channel and add me again.\n#Leaving this chat...__") 76 | await client.leave_chat(chat_id) 77 | 78 | 79 | @Client.on_message(filters.command(["forcesubscribe", "fsub"]) & ~filters.private) 80 | async def config(client, message): 81 | user = await client.get_chat_member(message.chat.id, message.from_user.id) 82 | if user.status == "creator" or user.user.id in Config.SUDO_USERS: 83 | chat_id = message.chat.id 84 | if len(message.command) > 1: 85 | input_str = message.command[1] 86 | input_str = input_str.replace("@", "") 87 | if input_str.lower() in ("off", "no", "disable"): 88 | sql.disapprove(chat_id) 89 | await message.reply_text("❌ **Force Subscribe is Disabled Successfully.**") 90 | elif input_str.lower() in ('clear'): 91 | sent_message = await message.reply_text('**Unmuting all members who are muted by me...**') 92 | try: 93 | for chat_member in (await client.get_chat_members(message.chat.id, filter="restricted")): 94 | if chat_member.restricted_by.id == (await client.get_me()).id: 95 | await client.unban_chat_member(chat_id, chat_member.user.id) 96 | time.sleep(1) 97 | await sent_message.edit('✅ **UnMuted all members who are muted by me.**') 98 | except ChatAdminRequired: 99 | await sent_message.edit('❗ **I am not an admin in this chat.**\n__I can\'t unmute members because i am not an admin in this chat make me admin with ban user permission.__') 100 | else: 101 | try: 102 | await client.get_chat_member(input_str, "me") 103 | sql.add_channel(chat_id, input_str) 104 | if input_str.startswith("-"): 105 | channel_url = await client.export_chat_invite_link(int(input_str)) 106 | else: 107 | channel_url = f"https://t.me/{input_str}" 108 | await message.reply_text(f"✅ **Force Subscribe is Enabled**\n__Force Subscribe is enabled, all the group members have to subscribe this [channel]({channel_url}) in order to send messages in this group.__", disable_web_page_preview=True) 109 | except UserNotParticipant: 110 | await message.reply_text(f"❗ **Not an Admin in the Channel**\n__I am not an admin in the [channel]({channel_url}). Add me as a admin in order to enable ForceSubscribe.__", disable_web_page_preview=True) 111 | except (UsernameNotOccupied, PeerIdInvalid): 112 | await message.reply_text(f"❗ **Invalid Channel Username/ID.**") 113 | except Exception as err: 114 | await message.reply_text(f"❗ **ERROR:** ```{err}```") 115 | else: 116 | if sql.fs_settings(chat_id): 117 | my_channel = sql.fs_settings(chat_id).channel 118 | if my_channel.startswith("-"): 119 | channel_url = await client.export_chat_invite_link(int(input_str)) 120 | else: 121 | channel_url = f"https://t.me/{my_channel}" 122 | await message.reply_text(f"✅ **Force Subscribe is enabled in this chat.**\n__For this [Channel]({channel_url})__", disable_web_page_preview=True) 123 | else: 124 | await message.reply_text("❌ **Force Subscribe is disabled in this chat.**") 125 | else: 126 | await message.reply_text("❗ **Group Creator Required**\n__You have to be the group creator to do that.__") 127 | -------------------------------------------------------------------------------- /plugins/help.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | from Config import Messages as tr 4 | from Config import Config as C 5 | from pyrogram import Client, filters 6 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton 7 | from pyrogram.errors.exceptions.bad_request_400 import UserNotParticipant, UsernameNotOccupied, ChatAdminRequired, PeerIdInvalid 8 | UPDATES_CHANNEL = C.UPDATES_CHANNEL 9 | logging.basicConfig(level=logging.INFO) 10 | 11 | @Client.on_message(filters.incoming & filters.command(['start']) & filters.private) 12 | async def _start(client, message): 13 | update_channel = UPDATES_CHANNEL 14 | if update_channel: 15 | try: 16 | user = await client.get_chat_member(update_channel, message.chat.id) 17 | if user.status == "kicked": 18 | await client.send_message( 19 | chat_id=message.chat.id, 20 | text="Sorry Sir, You are Banned to use me. Contact my [Support Group](https://t.me/Techno_Trickop).", 21 | parse_mode="markdown", 22 | disable_web_page_preview=True 23 | ) 24 | return 25 | except UserNotParticipant: 26 | await client.send_message( 27 | chat_id=message.chat.id, 28 | text="**Please Join My Updates Channel to use this Bot!**", 29 | reply_markup=InlineKeyboardMarkup( 30 | [ 31 | [ 32 | InlineKeyboardButton("Join Updates Channel", url=f"https://t.me/{update_channel}") 33 | ] 34 | ] 35 | ), 36 | parse_mode="markdown" 37 | ) 38 | return 39 | except Exception: 40 | await client.send_message(message.chat.id, 41 | text=tr.START_MSG.format(message.from_user.first_name, message.from_user.id), 42 | reply_markup=InlineKeyboardMarkup( 43 | [ 44 | [ 45 | InlineKeyboardButton("Join Updates Channel", url="https://t.me/Techno_Trickop"), 46 | InlineKeyboardButton("Support Group", url="https://t.me/Techno_Trickop") 47 | ], 48 | [ 49 | InlineKeyboardButton("🧑💻Devloper🧑💻", url="https://t.me/herox_xd") 50 | ] 51 | ] 52 | ), 53 | parse_mode="markdown", 54 | reply_to_message_id=message.message_id 55 | ) 56 | return 57 | await client.send_message(message.chat.id, 58 | text=tr.START_MSG.format(message.from_user.first_name, message.from_user.id), 59 | reply_markup=InlineKeyboardMarkup( 60 | [ 61 | [ 62 | InlineKeyboardButton("Join Updates Channel", url="https://t.me/Techno_Trickop"), 63 | InlineKeyboardButton("Support Group", url="https://t.me/Techno_Trickop") 64 | ], 65 | [ 66 | InlineKeyboardButton("🧑💻Devloper🧑💻", url="https://t.me/herox_xd") 67 | ] 68 | ] 69 | ), 70 | parse_mode="markdown", 71 | reply_to_message_id=message.message_id 72 | ) 73 | 74 | 75 | @Client.on_message(filters.incoming & filters.command(['source_code']) & filters.private) 76 | async def _source_code(client, message): 77 | await client.send_message(message.chat.id, 78 | text=tr.SC_MSG.format(message.from_user.first_name, message.from_user.id), 79 | reply_markup=InlineKeyboardMarkup( 80 | [ 81 | [ 82 | InlineKeyboardButton("Souce Code", url="https://github.com/sjmxaditi/ForceSub_Bot") 83 | ], 84 | [ 85 | InlineKeyboardButton("Join Updates Channel", url="https://t.me/Techno_Trickop"), 86 | InlineKeyboardButton("Support Group", url="https://t.me/Techno_Trickop") 87 | ], 88 | [ 89 | InlineKeyboardButton("🧑💻Devloper🧑💻", url="https://t.me/herox_xd") 90 | ] 91 | ] 92 | ), 93 | parse_mode="markdown", 94 | reply_to_message_id=message.message_id 95 | ) 96 | 97 | @Client.on_message(filters.incoming & filters.command(['help']) & filters.private) 98 | async def _help(client, message): 99 | update_channel = UPDATES_CHANNEL 100 | if update_channel: 101 | try: 102 | user = await client.get_chat_member(update_channel, message.chat.id) 103 | if user.status == "kicked": 104 | await client.send_message( 105 | chat_id=message.chat.id, 106 | text="Sorry Sir, You are Banned to use me. Contact my [Support Group](https://t.me/Techno_Trickop).", 107 | parse_mode="markdown", 108 | disable_web_page_preview=True 109 | ) 110 | return 111 | except UserNotParticipant: 112 | await client.send_message( 113 | chat_id=message.chat.id, 114 | text="**Please Join My Updates Channel to use this Bot!**", 115 | reply_markup=InlineKeyboardMarkup( 116 | [ 117 | [ 118 | InlineKeyboardButton("Join Updates Channel", url=f"https://t.me/{update_channel}") 119 | ] 120 | ] 121 | ), 122 | parse_mode="markdown" 123 | ) 124 | return 125 | except Exception: 126 | await client.send_message( 127 | chat_id=message.chat.id, 128 | text="Hey use this command in my pm. \nFor more help ask in my [Support Group](https://t.me/Techno_Trickop).", 129 | parse_mode="markdown", 130 | disable_web_page_preview=True) 131 | return 132 | await client.send_message(chat_id = message.chat.id, 133 | text = tr.HELP_MSG[1], 134 | disable_notification = True, 135 | reply_markup = InlineKeyboardMarkup(map(1)), 136 | reply_to_message_id = message.message_id 137 | ) 138 | 139 | help_callback_filter = filters.create(lambda _, __, query: query.data.startswith('help+')) 140 | 141 | @Client.on_callback_query(help_callback_filter) 142 | async def help_answer(client, callback_query): 143 | chat_id = callback_query.from_user.id 144 | message_id = callback_query.message.message_id 145 | msg = int(callback_query.data.split('+')[1]) 146 | await client.edit_message_text(chat_id=chat_id, message_id=message_id, 147 | text=tr.HELP_MSG[msg], reply_markup=InlineKeyboardMarkup(map(msg)) 148 | ) 149 | 150 | 151 | def map(pos): 152 | if(pos==1): 153 | button = [ 154 | [InlineKeyboardButton(text = '-->', callback_data = "help+2")] 155 | ] 156 | elif(pos==len(tr.HELP_MSG)-1): 157 | button = [ 158 | [InlineKeyboardButton(text = '<--', callback_data = f"help+{pos-1}")] 159 | ] 160 | else: 161 | button = [ 162 | [ 163 | InlineKeyboardButton(text = '<--', callback_data = f"help+{pos-1}"), 164 | InlineKeyboardButton(text = '-->', callback_data = f"help+{pos+1}") 165 | ], 166 | ] 167 | return button 168 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | TgCrypto 2 | pyrogram==1.3.1 3 | sqlalchemy==1.3.20 4 | psycopg2 5 | -------------------------------------------------------------------------------- /sql_helpers/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | from sqlalchemy import create_engine 3 | from sqlalchemy.ext.declarative import declarative_base 4 | from sqlalchemy.orm import sessionmaker, scoped_session 5 | from Config import Config 6 | 7 | 8 | def start() -> scoped_session: 9 | engine = create_engine(Config.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 | try: 16 | BASE = declarative_base() 17 | SESSION = start() 18 | except AttributeError as e: 19 | # this is a dirty way for the work-around required for #23 20 | print("DATABASE_URL is not configured. Features depending on the database might have issues.") 21 | print(str(e)) -------------------------------------------------------------------------------- /sql_helpers/forceSubscribe_sql.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import Column, String, Numeric, Boolean 2 | from sql_helpers import SESSION, BASE 3 | 4 | class forceSubscribe(BASE): 5 | __tablename__ = "forceSubscribe" 6 | chat_id = Column(Numeric, primary_key=True) 7 | channel = Column(String) 8 | 9 | def __init__(self, chat_id, channel): 10 | self.chat_id = chat_id 11 | self.channel = channel 12 | 13 | 14 | forceSubscribe.__table__.create(checkfirst=True) 15 | 16 | 17 | def fs_settings(chat_id): 18 | try: 19 | return SESSION.query(forceSubscribe).filter(forceSubscribe.chat_id == chat_id).one() 20 | except: 21 | return None 22 | finally: 23 | SESSION.close() 24 | 25 | 26 | def add_channel(chat_id, channel): 27 | adder = SESSION.query(forceSubscribe).get(chat_id) 28 | if adder: 29 | adder.channel = channel 30 | else: 31 | adder = forceSubscribe( 32 | chat_id, 33 | channel 34 | ) 35 | SESSION.add(adder) 36 | SESSION.commit() 37 | 38 | def disapprove(chat_id): 39 | rem = SESSION.query(forceSubscribe).get(chat_id) 40 | if rem: 41 | SESSION.delete(rem) 42 | SESSION.commit() --------------------------------------------------------------------------------