├── Dockerfile ├── requirements.txt ├── README.md └── main.py /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3 2 | 3 | WORKDIR /usr/src/app 4 | 5 | COPY requirements.txt ./ 6 | RUN pip install --no-cache-dir -r requirements.txt 7 | 8 | COPY . . 9 | 10 | ENTRYPOINT ["python", "./main.py"] 11 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | asn1crypto==0.24.0 2 | certifi==2019.3.9 3 | cffi==1.12.3 4 | chardet==3.0.4 5 | cryptography==2.6.1 6 | future==0.17.1 7 | idna==2.8 8 | pycparser==2.19 9 | python-telegram-bot==12.0.0b1 10 | requests==2.22.0 11 | six==1.12.0 12 | tornado==6.0.2 13 | urllib3==1.25.2 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gatekeeper bot 2 | This Telegram bot automatically prevents new users from posting anything. It also uses an inline keyboard which the newly joined user can use to prove that he/she is not a robot. 3 | 4 | ## Setting up 5 | Set your bot token and chat id as env variables. Add the bot to your group as an admin, so it can read the messages. 6 | 7 | ## Operation 8 | Every time a new user joins your group, the bot prevents the user from saying anything until he/she answers a question using the inline keyboard. Upon answering correctly, all restrictions will be lifted. If the answer is incorrect, the bot points this out and the user's restrictions have to be removed manually by an admin. 9 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # This program is dedicated to the public domain under the CC0 license. 4 | # 5 | # THIS EXAMPLE HAS BEEN UPDATED TO WORK WITH THE BETA VERSION 12 OF PYTHON-TELEGRAM-BOT. 6 | # If you're still using version 11.1.0, please see the examples at 7 | # https://github.com/python-telegram-bot/python-telegram-bot/tree/v11.1.0/examples 8 | 9 | """ 10 | First, a few handler functions are defined. Then, those functions are passed to 11 | the Dispatcher and registered at their respective places. 12 | Then, the bot is started and runs until we press Ctrl-C on the command line. 13 | Usage: 14 | Basic inline bot example. Applies different text transformations. 15 | Press Ctrl-C on the command line or send a signal to the process to stop the 16 | bot. 17 | """ 18 | import os 19 | import logging 20 | from telegram import InlineKeyboardButton, InlineKeyboardMarkup, ChatPermissions 21 | from telegram.ext import Filters, Updater, MessageHandler, CommandHandler, CallbackQueryHandler 22 | from random import shuffle 23 | 24 | # Enable logging 25 | logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', 26 | level=logging.INFO) 27 | 28 | logger = logging.getLogger(__name__) 29 | 30 | 31 | # Define a few command handlers. These usually take the two arguments bot and 32 | # update. Error handlers also receive the raised TelegramError object in error. 33 | def start(update, context): 34 | """Send a message when the command /start is issued.""" 35 | update.message.reply_text("Hello, I am the GateKeeper.") 36 | 37 | 38 | def help(update, context): 39 | """Send a message when the command /help is issued.""" 40 | update.message.reply_text('Available commands:\n\n/id') 41 | 42 | 43 | def id(update, context): 44 | """Send a message when the command /help is issued.""" 45 | update.message.reply_text(update.effective_chat.id) 46 | 47 | 48 | def hodor(update, context): 49 | try: 50 | for new_member in update.message.new_chat_members: 51 | callback_id = str(new_member.id) 52 | permissions = ChatPermissions( 53 | can_send_messages=False, 54 | can_send_media_messages=False, 55 | can_send_other_messages=False, 56 | can_add_web_page_previews=False 57 | ) 58 | context.bot.restrict_chat_member( 59 | int(CHAT_ID), 60 | new_member.id, 61 | permissions, 62 | ) 63 | 64 | keyboard_items = [ 65 | InlineKeyboardButton("🥩", callback_data=callback_id + ',steak'), 66 | InlineKeyboardButton("🥝", callback_data=callback_id + ',kiwi'), 67 | InlineKeyboardButton("🥛", callback_data=callback_id + ',milk'), 68 | InlineKeyboardButton("🥓", callback_data=callback_id + ',bacon'), 69 | InlineKeyboardButton("🥥", callback_data=callback_id + ',coconut'), 70 | InlineKeyboardButton("🍩", callback_data=callback_id + ',donut'), 71 | InlineKeyboardButton("🌮", callback_data=callback_id + ',taco'), 72 | InlineKeyboardButton("🍕", callback_data=callback_id + ',pizza'), 73 | InlineKeyboardButton("🥗", callback_data=callback_id + ',salad'), 74 | InlineKeyboardButton("🍌", callback_data=callback_id + ',banana'), 75 | InlineKeyboardButton("🌰", callback_data=callback_id + ',chestnut'), 76 | InlineKeyboardButton("🍭", callback_data=callback_id + ',lollipop'), 77 | InlineKeyboardButton("🥑", callback_data=callback_id + ',avocado'), 78 | InlineKeyboardButton("🍗", callback_data=callback_id + ',chicken'), 79 | InlineKeyboardButton("🥪", callback_data=callback_id + ',sandwich'), 80 | InlineKeyboardButton("🥒", callback_data=callback_id + ',cucumber') 81 | ] 82 | 83 | shuffle(keyboard_items) 84 | keyboard = [] 85 | 86 | counter = 0 87 | for i in range(4): # create a list with nested lists 88 | keyboard.append([]) 89 | for n in range(4): 90 | keyboard_item = keyboard_items[counter] 91 | keyboard[i].append(keyboard_item) # fills nested lists with data 92 | counter += 1 93 | 94 | reply_markup = InlineKeyboardMarkup(keyboard) 95 | 96 | update.message.reply_text( 97 | 'Hello, ' + 98 | new_member.first_name + 99 | ' and welcome to the group. Just a small formality before we allow you to post: please prove that you are not a robot by grabbing a drink below.', 100 | reply_markup=reply_markup 101 | ) 102 | except AttributeError: 103 | pass 104 | 105 | 106 | def button(update, context): 107 | query = update.callback_query 108 | person_who_pushed_the_button = int(query.data.split(",")[0]) 109 | print("Query user: " + str(query.from_user)) 110 | print("Query data: " + str(query.data)) 111 | 112 | if query.from_user.id == person_who_pushed_the_button: 113 | if 'milk' in query.data: 114 | context.bot.delete_message( 115 | chat_id=update.callback_query.message.chat_id, 116 | message_id=update.callback_query.message.message_id 117 | ) 118 | permissions = ChatPermissions( 119 | can_send_messages=True, 120 | can_send_media_messages=True, 121 | can_send_other_messages=True, 122 | can_add_web_page_previews=True, 123 | ) 124 | context.bot.restrict_chat_member( 125 | int(CHAT_ID), 126 | person_who_pushed_the_button, 127 | permissions, 128 | ) 129 | else: 130 | query.edit_message_text(text="🚨 A robot suspect was just put on hold! 🚨") 131 | 132 | 133 | def error(update, context): 134 | """Log Errors caused by Updates.""" 135 | logger.warning('Update "%s" caused error "%s"', update, context.error) 136 | 137 | 138 | def main(): 139 | # Create the Updater and pass it your bot's token. 140 | # Make sure to set use_context=True to use the new context based callbacks 141 | # Post version 12 this will no longer be necessary 142 | updater = Updater(str(os.environ['TOKEN']), use_context=True) 143 | 144 | # Get the dispatcher to register handlers 145 | dp = updater.dispatcher 146 | 147 | # on different commands - answer in Telegram 148 | dp.add_handler(CommandHandler("start", start)) 149 | dp.add_handler(CommandHandler("help", help)) 150 | dp.add_handler(CommandHandler("id", id)) 151 | 152 | dp.add_handler(MessageHandler(Filters.chat(int(os.environ['CHAT_ID'])), hodor)) 153 | 154 | updater.dispatcher.add_handler(CallbackQueryHandler(button)) 155 | 156 | # on noncommand i.e message - echo the message on Telegram 157 | 158 | # log all errors 159 | dp.add_error_handler(error) 160 | 161 | # Start the Bot 162 | updater.start_polling() 163 | 164 | # Block until the user presses Ctrl-C or the process receives SIGINT, 165 | # SIGTERM or SIGABRT. This should be used most of the time, since 166 | # start_polling() is non-blocking and will stop the bot gracefully. 167 | updater.idle() 168 | 169 | 170 | if __name__ == '__main__': 171 | main() 172 | --------------------------------------------------------------------------------