TLG_JoinCaptchaBot/sources/join_captcha_bot.py

3219 wiersze
134 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
'''
Script:
join_captcha_bot.py
Description:
Telegram Bot that send a captcha for each new user who join a group, and
remove them if they can not solve the captcha in a specified time.
Author:
Jose Miguel Rios Rubio
Creation date:
09/09/2018
Last modified date:
21/10/2022
Version:
1.27.1
'''
###############################################################################
### Imported modules
from platform import system as os_system
from signal import signal, SIGTERM, SIGINT
if os_system() != "Windows":
from signal import SIGUSR1
import logging
import re
from sys import exit
from os import kill, getpid, path, remove, makedirs, listdir
from shutil import rmtree
from time import time, sleep
from threading import Thread
from collections import OrderedDict
from random import choice, randint
from json import dumps as json_dumps
from tsjson import TSjson
from multicolorcaptcha import CaptchaGenerator
from telegram import (
Update, Chat, InputMediaPhoto, InlineKeyboardButton,
InlineKeyboardMarkup, Poll
)
from telegram.ext import (
CallbackContext, Updater, CommandHandler,
ChatMemberHandler, MessageHandler, Filters,
CallbackQueryHandler, PollAnswerHandler, Defaults
)
from telegram.utils.helpers import (
escape_markdown
)
from telegram.error import (
TelegramError, Unauthorized, BadRequest,
TimedOut, NetworkError
)
from commons import (
printts, is_int, add_lrm, file_exists, file_write, file_read,
list_remove_element, get_unix_epoch, pickle_save, pickle_restore
)
from tlgbotutils import (
tlg_send_msg, tlg_send_image, tlg_send_poll, tlg_stop_poll,
tlg_answer_callback_query, tlg_delete_msg, tlg_edit_msg_media,
tlg_ban_user, tlg_kick_user, tlg_user_is_admin, tlg_leave_chat,
tlg_restrict_user, tlg_unrestrict_user, tlg_is_valid_user_id_or_alias,
tlg_is_valid_group, tlg_alias_in_string, tlg_extract_members_status_change,
tlg_get_msg, tlg_is_a_channel_msg_on_discussion_group, tlg_get_user_name,
tlg_has_new_member_join_group
)
from constants import (
SCRIPT_PATH, CONST, TEXT
)
###############################################################################
### Globals
updater = None
files_config_list = []
to_delete_in_time_messages_list = []
new_users = {}
connections = {}
th_0 = None
th_1 = None
force_exit = False
# Create Captcha Generator object of specified size (2 -> 640x360)
CaptchaGen = CaptchaGenerator(2)
###############################################################################
### Setup Bot Logger
log_level=logging.INFO
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=log_level)
###############################################################################
### Termination Signals Handler For Program Process
def signal_handler(signal, frame):
'''Termination signals (SIGINT, SIGTERM) handler for program process'''
global force_exit
global updater
global th_0
global th_1
force_exit = True
printts("Termination signal received. Releasing resources...")
# Close the Bot instance (it wait for updater, dispatcher and other internals threads to end)
if updater is not None:
printts("Closing Bot...")
updater.stop()
# Launch threads to acquire all messages and users files mutex to ensure that them are closed
# (make sure to close the script when no read/write operation on files)
if files_config_list:
printts("Closing resource files...")
th_list = []
for chat_config_file in files_config_list:
t = Thread(target=th_close_resource_file, args=(chat_config_file["File"],))
th_list.append(t)
t.start()
# Wait for all threads to end
for th in th_list:
if th.is_alive():
th.join()
# Wait to end threads
printts("Waiting th_0 end...")
if th_0 is not None:
if th_0.is_alive():
th_0.join()
printts("Waiting th_1 end...")
if th_1 is not None:
if th_1.is_alive():
th_1.join()
# Save current session data
save_session()
# Close the program
printts("All resources released.")
printts("Exit 0")
exit(0)
def th_close_resource_file(file_to_close):
'''Threaded function to close resource files in parallel when closing Bot Script.'''
file_to_close.lock.acquire()
### Signals attachment
signal(SIGTERM, signal_handler) # SIGTERM (kill pid) to signal_handler
signal(SIGINT, signal_handler) # SIGINT (Ctrl+C) to signal_handler
if os_system() != "Windows":
signal(SIGUSR1, signal_handler) # SIGUSR1 (self-send) to signal_handler
###############################################################################
### JSON Chat Config File Functions
def get_default_config_data():
'''Get default config data structure'''
config_data = OrderedDict(
[
("Title", CONST["INIT_TITLE"]),
("Link", CONST["INIT_LINK"]),
("Language", CONST["INIT_LANG"]),
("Enabled", CONST["INIT_ENABLE"]),
("URL_Enabled", CONST["INIT_URL_ENABLE"]),
("RM_All_Msg", CONST["INIT_RM_ALL_MSG"]),
("Captcha_Chars_Mode", CONST["INIT_CAPTCHA_CHARS_MODE"]),
("Captcha_Time", CONST["INIT_CAPTCHA_TIME"]),
("Captcha_Difficulty_Level", CONST["INIT_CAPTCHA_DIFFICULTY_LEVEL"]),
("Restrict_Non_Text", CONST["INIT_RESTRICT_NON_TEXT_MSG"]),
("Rm_Result_Msg", CONST["INIT_RM_RESULT_MSG"]),
("Rm_Welcome_Msg", CONST["INIT_RM_WELCOME_MSG"]),
("Poll_Q", ""),
("Poll_A", []),
("Poll_C_A", 0),
("Welcome_Msg", "-"),
("Welcome_Time", CONST["T_DEL_WELCOME_MSG"]),
("Ignore_List", [])
])
# Feed Captcha Poll Options with empty answers for expected max num
for _ in range(0, CONST["MAX_POLL_OPTIONS"]):
config_data["Poll_A"].append("")
return config_data
def save_config_property(chat_id, param, value):
'''Store actual chat configuration in file'''
fjson_config = get_chat_config_file(chat_id)
config_data = fjson_config.read()
if not config_data:
config_data = get_default_config_data()
if (param in config_data) and (value == config_data[param]):
return
config_data[param] = value
fjson_config.write(config_data)
def get_chat_config(chat_id, param):
'''Get specific stored chat configuration property'''
file = get_chat_config_file(chat_id)
if file:
config_data = file.read()
if (not config_data) or (param not in config_data):
config_data = get_default_config_data()
save_config_property(chat_id, param, config_data[param])
else:
config_data = get_default_config_data()
save_config_property(chat_id, param, config_data[param])
return config_data[param]
def get_all_chat_config(chat_id):
'''Get specific stored chat configuration property'''
file = get_chat_config_file(chat_id)
if file:
config_data = file.read()
if (not config_data):
config_data = get_default_config_data()
else:
config_data = get_default_config_data()
return config_data
def get_chat_config_file(chat_id):
'''Determine chat config file from the list by ID. Get the file if exists or create it if not'''
global files_config_list
file = OrderedDict([("ID", chat_id), ("File", None)])
found = False
if files_config_list:
for chat_file in files_config_list:
if chat_file["ID"] == chat_id:
file = chat_file
found = True
break
if not found:
chat_config_file_name = "{}/{}/{}".format(CONST["CHATS_DIR"], chat_id, CONST["F_CONF"])
file["ID"] = chat_id
file["File"] = TSjson(chat_config_file_name)
files_config_list.append(file)
else:
chat_config_file_name = "{}/{}/{}".format(CONST["CHATS_DIR"], chat_id, CONST["F_CONF"])
file["ID"] = chat_id
file["File"] = TSjson(chat_config_file_name)
files_config_list.append(file)
return file["File"]
###############################################################################
### Telegram Related Functions
def tlg_send_msg_type_chat(bot, chat_type, chat_id, text,
**kwargs_for_send_message):
'''Send a telegram message normal or schedule to self-destruct depending
of chat type (private chat - normal; group - selfdestruct).'''
if chat_type == "private":
tlg_send_msg(bot, chat_id, text, **kwargs_for_send_message)
else:
tlg_send_selfdestruct_msg(bot, chat_id, text,
**kwargs_for_send_message)
def tlg_send_selfdestruct_msg(bot, chat_id, message,
**kwargs_for_send_message):
'''tlg_send_selfdestruct_msg_in() with default delete time'''
return tlg_send_selfdestruct_msg_in(bot, chat_id, message,
CONST["T_DEL_MSG"], **kwargs_for_send_message)
def tlg_send_selfdestruct_msg_in(bot, chat_id, message, time_delete_sec,
**kwargs_for_send_message):
'''Send a telegram message that will be auto-delete in specified time'''
sent_result = tlg_send_msg(bot, chat_id, message,
**kwargs_for_send_message)
if sent_result["msg"] is None:
return None
tlg_msg_to_selfdestruct_in(sent_result["msg"], time_delete_sec)
return sent_result["msg"].message_id
def tlg_msg_to_selfdestruct(message):
'''tlg_msg_to_selfdestruct_in() with default delete time'''
tlg_msg_to_selfdestruct_in(message, CONST["T_DEL_MSG"])
def tlg_msg_to_selfdestruct_in(message, time_delete_sec):
'''Add a telegram message to be auto-delete in specified time'''
global to_delete_in_time_messages_list
# Check if provided message has all necessary attributes
if message is None:
return False
if not hasattr(message, "chat_id"):
return False
if not hasattr(message, "message_id"):
return False
if not hasattr(message, "from_user"):
return False
else:
if not hasattr(message.from_user, "id"):
return False
# Get sent message ID and calculate delete time
chat_id = message.chat_id
user_id = message.from_user.id
msg_id = message.message_id
t0 = time()
# Add sent message data to to-delete messages list
sent_msg_data = OrderedDict([("Chat_id", None), ("User_id", None),
("Msg_id", None), ("time", None), ("delete_time", None)])
sent_msg_data["Chat_id"] = chat_id
sent_msg_data["User_id"] = user_id
sent_msg_data["Msg_id"] = msg_id
sent_msg_data["time"] = t0
sent_msg_data["delete_time"] = time_delete_sec
to_delete_in_time_messages_list.append(sent_msg_data)
return True
###############################################################################
### General Functions
def save_session():
'''Backup current execution data'''
# Let's backup to file
data = {
"to_delete_in_time_messages_list": to_delete_in_time_messages_list,
"new_users": new_users,
"connections": connections
}
if not pickle_save(CONST["F_SESSION"], data):
printts("Fail to save current session data")
return False
printts("Current session data saved")
return True
def restore_session():
'''Load last execution data'''
global to_delete_in_time_messages_list
global new_users
global connections
# Check if session file exists
if not file_exists(CONST["F_SESSION"]):
return False
# Get data from session file
last_session_data = pickle_restore(CONST["F_SESSION"])
if last_session_data is None:
printts("Fail to restore last session data")
return False
# Load last session data to current RAM
connections = last_session_data["connections"]
new_users = last_session_data["new_users"]
to_delete_in_time_messages_list = \
last_session_data["to_delete_in_time_messages_list"]
# Renew time to kick users
for chat_id in new_users:
for user_id in new_users[chat_id]:
# Some rand to avoid all requests sent at same time
t0 = time() + randint(0, 10)
new_users[chat_id][user_id]["join_data"]["join_time"] = t0
# Renew time to remove messages
i = 0
while i < len(to_delete_in_time_messages_list):
# Some rand to avoid all requests sent at same time
t0 = time() + randint(0, 10)
to_delete_in_time_messages_list[i]["time"] = t0
i = i + 1
printts("Last session data restored")
return True
def initialize_resources():
'''Initialize resources by populating files list with chats found files'''
global files_config_list
# Remove old captcha directory and create it again
if path.exists(CONST["CAPTCHAS_DIR"]):
rmtree(CONST["CAPTCHAS_DIR"])
makedirs(CONST["CAPTCHAS_DIR"])
# Create allowed users file if it does not exists
if not path.exists(CONST["F_ALLOWED_USERS"]):
file_write(CONST["F_ALLOWED_USERS"], "")
# Create banned groups file if it does not exists
if not path.exists(CONST["F_BAN_GROUPS"]):
file_write(CONST["F_BAN_GROUPS"], "")
# Create allowed groups file if it does not exists
if CONST["BOT_PRIVATE"]:
if not path.exists(CONST["F_ALLOWED_GROUPS"]):
file_write(CONST["F_ALLOWED_GROUPS"], "")
# Create data directory if it does not exists
if not path.exists(CONST["CHATS_DIR"]):
makedirs(CONST["CHATS_DIR"])
else:
# If chats directory exists, check all subdirectories names (chats ID)
files = listdir(CONST["CHATS_DIR"])
for f_chat_id in files:
# Populate config files list
file_path = "{}/{}/{}".format(CONST["CHATS_DIR"], f_chat_id, CONST["F_CONF"])
files_config_list.append(OrderedDict([("ID", f_chat_id),
("File", TSjson(file_path))]))
# Create default configuration file if it does not exists
if not path.exists(file_path):
default_conf = get_default_config_data()
for key, value in default_conf.items():
save_config_property(f_chat_id, key, value)
# Load and generate URL detector regex from TLD list file
load_urls_regex("{}/{}".format(SCRIPT_PATH, CONST["F_TLDS"]))
# Load all languages texts
load_texts_languages()
def load_urls_regex(file_path):
'''Load URL detection Regex from IANA TLD list text file.'''
tlds_str = ""
list_file_lines = []
try:
with open(file_path, "r") as f:
for line in f:
if line is None:
continue
if (line == "") or (line == "\r\n") or (line == "\r") or (line == "\n"):
continue
# Ignore lines that start with # (first header line of IANA TLD list file)
if line[0] == "#":
continue
line = line.lower()
line = line.replace("\r", "")
line = line.replace("\n", "|")
list_file_lines.append(line)
except Exception as e:
printts("Error opening file \"{}\". {}".format(file_path, str(e)))
if len(list_file_lines) > 0:
tlds_str = "".join(list_file_lines)
CONST["REGEX_URLS"] = CONST["REGEX_URLS"].format(tlds_str)
def load_texts_languages():
'''Load all texts from each language file.'''
# Initialize all languages to english texts by default, so if
# some language file miss some field, the english text is used
lang_file = "{}/{}.json".format(CONST["LANG_DIR"], CONST["INIT_LANG"].lower())
json_init_lang_texts = TSjson(lang_file).read()
if (json_init_lang_texts is None) or (json_init_lang_texts == {}):
printts("Error loading language \"{}\" from {}. Language file not found or bad JSON "
"syntax.".format(CONST["INIT_LANG"].lower(), lang_file))
printts("Exit.\n")
exit(0)
for lang_iso_code in TEXT:
TEXT[lang_iso_code] = json_init_lang_texts.copy()
# Load supported languages texts
for lang_iso_code in TEXT:
lang_file = "{}/{}.json".format(CONST["LANG_DIR"], lang_iso_code.lower())
json_lang_file = TSjson(lang_file)
json_lang_texts = json_lang_file.read()
if (json_lang_texts is None) or (json_lang_texts == {}):
printts("Error loading language \"{}\" from {}. Language file not found or bad JSON "
"syntax.".format(lang_iso_code, lang_file))
printts("Exit.\n")
exit(0)
for text in json_lang_texts:
TEXT[lang_iso_code][text] = json_lang_texts[text]
# Check if there is some missing text in any language
for lang_iso_code in TEXT:
lang_iso_code = lang_iso_code.lower()
lang_file = "{}/{}.json".format(CONST["LANG_DIR"], lang_iso_code)
json_lang_file = TSjson(lang_file)
json_lang_texts = json_lang_file.read()
for text in json_init_lang_texts:
if text not in json_lang_texts:
printts("Warning: text \"{}\" missing from language file \"{}\".json".format(
text, lang_iso_code))
def create_image_captcha(chat_id, file_name, difficult_level, captcha_mode):
'''Generate an image captcha from pseudo numbers'''
# If it doesn't exists, create captchas folder to store generated captchas
img_dir_path = "{}/{}".format(CONST["CAPTCHAS_DIR"], chat_id)
img_file_path = "{}/{}.png".format(img_dir_path, file_name)
if not path.exists(CONST["CAPTCHAS_DIR"]):
makedirs(CONST["CAPTCHAS_DIR"])
else:
if not path.exists(img_dir_path):
makedirs(img_dir_path)
else:
# If the captcha file exists remove it
if path.exists(img_file_path):
remove(img_file_path)
# Generate and save the captcha with a random background
# mono-color or multi-color
captcha_result = {
"image": img_file_path,
"characters": "",
"equation_str": "",
"equation_result": ""
}
if captcha_mode == "math":
captcha = CaptchaGen.gen_math_captcha_image(2, bool(randint(0, 1)))
captcha_result["equation_str"] = captcha["equation_str"]
captcha_result["equation_result"] = captcha["equation_result"]
else:
captcha = CaptchaGen.gen_captcha_image(difficult_level, captcha_mode,
bool(randint(0, 1)))
captcha_result["characters"] = captcha["characters"]
captcha["image"].save(img_file_path, "png")
return captcha_result
def num_config_poll_options(poll_options):
'''Check how many poll options are configured.'''
configured_options = 0
for i in range(0, CONST["MAX_POLL_OPTIONS"]):
if poll_options[i] != "":
configured_options = configured_options + 1
return configured_options
def is_user_in_ignored_list(chat_id, user):
'''Check if user is in ignored users list.'''
ignored_users = get_chat_config(chat_id, "Ignore_List")
if user.id in ignored_users:
return True
if user.username is not None:
user_alias = "@{}".format(user.username)
if user_alias in ignored_users:
return True
return False
def is_user_in_allowed_list(user):
'''Check if user is in global allowed list.'''
l_white_users = file_read(CONST["F_ALLOWED_USERS"])
if user.id in l_white_users:
return True
if user.username is not None:
user_alias = "@{}".format(user.username)
if user_alias in l_white_users:
return True
return False
def is_group_in_allowed_list(chat_id):
'''Check if group is in allowed list.'''
# True if Bot is Public
if not CONST["BOT_PRIVATE"]:
return True
l_allowed_groups = file_read(CONST["F_ALLOWED_GROUPS"])
if str(chat_id) in l_allowed_groups:
return True
return False
def is_group_in_banned_list(chat_id):
'''Check if group is in banned list.'''
l_banned_groups = file_read(CONST["F_BAN_GROUPS"])
if str(chat_id) in l_banned_groups:
return True
return False
def allowed_in_this_group(bot, chat, member_added_by):
'''Check if Bot is allowed to be used in a Chat.'''
if not is_group_in_allowed_list(chat.id):
printts("Warning: Bot added to not allowed group.")
from_user_name = ""
if member_added_by.name is not None:
from_user_name = member_added_by.name
else:
from_user_name = member_added_by.full_name
chat_link = ""
if chat.username:
chat_link = "@{}".format(chat.username)
printts("{}, {}, {}, {}".format(chat.id, from_user_name, chat.title,
chat_link))
msg_text = CONST["NOT_ALLOW_GROUP"].format(CONST["BOT_OWNER"], chat.id,
CONST["REPOSITORY"])
tlg_send_msg(bot, chat.id, msg_text)
return False
if is_group_in_banned_list(chat.id):
printts("[{}] Warning: Bot added to banned group".format(chat.id))
return False
return True
def get_update_user_lang(update_user_data):
'''Get user language-code from Telegram Update user data and return
Bot supported language (english if not supported).'''
lang = getattr(update_user_data, "language_code", "EN")
if lang is None:
lang = "EN"
lang = lang.upper()
if lang not in TEXT:
lang = "EN"
return lang
def is_captcha_num_solve(captcha_mode, msg_text, solve_num):
'''Check if number send by user solves a num/hex/ascii/math captcha.
- For "math", the message must be the exact math equation result number.
- For other mode, the message must contains the numbers.'''
if captcha_mode == "math":
if msg_text == solve_num:
return True
else:
if solve_num.lower() in msg_text.lower():
return True
return False
def should_manage_captcha(update, bot):
'''Check if the Bot should manage a Captcha process to this Group and
Member. It checks if the group is allowed to use the Bot, checks if the
member is not an Administrator neither a member added by an Admin, or an
added Bot, and checks if the Member is not in any of the allowed users
lists.'''
chat = update.chat_member.chat
join_user = update.chat_member.new_chat_member.user
member_added_by = update.chat_member.from_user
# Check if Group is not allowed to be used by the Bot
if not allowed_in_this_group(bot, chat, member_added_by):
tlg_leave_chat(bot, chat.id)
return False
# Ignore Admins
if tlg_user_is_admin(bot, join_user.id, chat.id):
printts("[{}] User is an admin.".format(chat.id))
printts("Skipping the captcha process.")
return False
# Ignore Members added by an Admin
if tlg_user_is_admin(bot, member_added_by.id, chat.id):
printts("[{}] User has been added by an admin.".format(chat.id))
printts("Skipping the captcha process.")
return False
# Ignore if the member that has been join the group is a Bot
if join_user.is_bot:
printts("[{}] User is a Bot.".format(chat.id))
printts("Skipping the captcha process.")
return False
# Ignore if the member that has joined is in chat ignore list
if is_user_in_ignored_list(chat.id, join_user):
printts("[{}] User is in ignore list.".format(chat.id))
printts("Skipping the captcha process.")
return False
if is_user_in_allowed_list(join_user):
printts("[{}] User is in global allowed list.".format(chat.id))
printts("Skipping the captcha process.")
return False
return True
def captcha_fail_kick_ban_member(bot, chat_id, user_id, max_join_retries):
'''Kick/Ban a new member that has fail to solve the captcha.'''
global new_users
kicked = False
banned = False
# Get parameters
lang = get_chat_config(chat_id, "Language")
rm_result_msg = get_chat_config(chat_id, "Rm_Result_Msg")
user_name = new_users[chat_id][user_id]["join_data"]["user_name"]
join_retries = new_users[chat_id][user_id]["join_data"]["join_retries"]
printts("[{}] {} join_retries: {}".format(chat_id, user_id, join_retries))
# Kick if user has fail to solve the captcha less than "max_join_retries"
if join_retries < max_join_retries:
printts("[{}] Captcha not solved, kicking {} ({})...".format(chat_id,
user_name, user_id))
# Try to kick the user
kick_result = tlg_kick_user(bot, chat_id, user_id)
if kick_result["error"] == "":
# Kick success
kicked = True
join_retries = join_retries + 1
# Send kicked message
msg_text = TEXT[lang]["NEW_USER_KICK"].format(user_name)
if rm_result_msg:
tlg_send_selfdestruct_msg_in(bot, chat_id, msg_text, CONST["T_FAST_DEL_MSG"])
else:
tlg_send_msg(bot, chat_id, msg_text)
else:
# Kick fail
printts("[{}] Unable to kick".format(chat_id))
if (kick_result["error"] == "The user has left the group") or \
(kick_result["error"] == "The user was already kicked"):
# The user is not in the chat
msg_text = TEXT[lang]["NEW_USER_KICK_NOT_IN_CHAT"].format(user_name)
if rm_result_msg:
tlg_send_selfdestruct_msg_in(bot, chat_id, msg_text, CONST["T_FAST_DEL_MSG"])
else:
tlg_send_msg(bot, chat_id, msg_text)
elif kick_result["error"] == "Not enough rights to restrict/unrestrict chat member":
# Bot has no privileges to kick
msg_text = TEXT[lang]["NEW_USER_KICK_NOT_RIGHTS"].format(user_name)
# Send no rights for kick message without auto-remove
tlg_send_msg(bot, chat_id, msg_text)
else:
# For other reason, the Bot can't ban
msg_text = TEXT[lang]["BOT_CANT_KICK"].format(user_name)
if rm_result_msg:
tlg_send_selfdestruct_msg_in(bot, chat_id, msg_text, CONST["T_FAST_DEL_MSG"])
else:
tlg_send_msg(bot, chat_id, msg_text)
# Ban if user has join "max_join_retries" times without solving the captcha
else:
printts("[{}] Captcha not solved, banning {} ({})...".format(chat_id,
user_name, user_id))
# Try to ban the user and notify Admins
ban_result = tlg_ban_user(bot, chat_id, user_id)
if ban_result["error"] == "":
# Ban success
banned = True
msg_text = TEXT[lang]["NEW_USER_BAN"].format(
user_name, max_join_retries)
else:
# Ban fail
if ban_result["error"] == "User not found":
# The user is not in the chat
msg_text = TEXT[lang]["NEW_USER_BAN_NOT_IN_CHAT"].format(
user_name, max_join_retries)
elif ban_result["error"] == "Not enough rights to restrict/unrestrict chat member":
# Bot has no privileges to ban
msg_text = TEXT[lang]["NEW_USER_BAN_NOT_RIGHTS"].format(
user_name, max_join_retries)
else:
# For other reason, the Bot can't ban
msg_text = TEXT[lang]["BOT_CANT_BAN"].format(
user_name, max_join_retries)
# Send ban notify message
printts("[{}] {}".format(chat_id, msg_text))
if rm_result_msg:
tlg_send_selfdestruct_msg(bot, chat_id, msg_text)
else:
tlg_send_msg(bot, chat_id, msg_text)
# Update user info (join_retries & kick_ban)
new_users[chat_id][user_id]["join_data"]["kicked_ban"] = True
new_users[chat_id][user_id]["join_data"]["join_retries"] = join_retries
# Remove join messages
printts("[{}] Removing messages from user {}...".format(chat_id, user_name))
join_msg = new_users[chat_id][user_id]["join_msg"]
if join_msg is not None:
tlg_delete_msg(bot, chat_id, join_msg)
for msg in new_users[chat_id][user_id]["msg_to_rm"]:
tlg_delete_msg(bot, chat_id, msg)
new_users[chat_id][user_id]["msg_to_rm"].clear()
for msg in new_users[chat_id][user_id]["msg_to_rm_on_kick"]:
tlg_delete_msg(bot, chat_id, msg)
new_users[chat_id][user_id]["msg_to_rm_on_kick"].clear()
# Delete user join info if ban was success
if banned:
del new_users[chat_id][user_id]
printts("[{}] Kick/Ban process completed".format(chat_id))
printts(" ")
###############################################################################
### Received Telegram not-command messages handlers
def chat_bot_status_change(update: Update, context: CallbackContext):
'''Get Bot chats status changes (Bot added to group/channel,
started/stopped conversation in private chat, etc.) event handler.'''
# Check Bot changes
result = tlg_extract_members_status_change(update.my_chat_member)
if result is None:
return
was_member, is_member = result
# Get chat data
bot = context.bot
chat = update.effective_chat
caused_by_user = update.effective_user
# Private Chat
if chat.type == Chat.PRIVATE:
return
# Bot private conversation started
#if not was_member and is_member:
# # ...
# Bot private conversation blocked
#elif was_member and not is_member:
# # ...
#else:
# return
# Groups
elif chat.type in [Chat.GROUP, Chat.SUPERGROUP]:
# Bot added to group
if not was_member and is_member:
# Get the language of the Telegram client software the Admin
# that has added the Bot has, to assume this is the chat language
# and configure Bot language of this chat
admin_language = ""
language_code = getattr(caused_by_user, "language_code", None)
if language_code:
admin_language = language_code[0:2].upper()
if admin_language not in TEXT:
admin_language = CONST["INIT_LANG"]
save_config_property(chat.id, "Language", admin_language)
# Get and save chat data
if chat.title:
save_config_property(chat.id, "Title", chat.title)
if chat.username:
chat_link = "@{}".format(chat.username)
save_config_property(chat.id, "Link", chat_link)
# Check if Group is not allowed to be used by the Bot
if not allowed_in_this_group(bot, chat, caused_by_user):
tlg_leave_chat(bot, chat.id)
return
# Send bot join message
tlg_send_msg(bot, chat.id, TEXT[admin_language]["START"])
return
# Bot leave/removed from group
elif was_member and not is_member:
# Bot leave the group
if caused_by_user.id == bot.id:
# Bot left the group by itself
print("[{}] Bot leave the group".format(chat.id))
# Bot removed from group
else:
print("[{}] Bot removed from group by {}".format(
chat.id, caused_by_user.username))
return
else:
return
# Channels
else:
# Bot added to channel
if not was_member and is_member:
# Leave it (Bot don't allowed to be used in Channels)
printts("Bot try to be added to a channel")
tlg_send_msg(bot, chat.id, CONST["BOT_LEAVE_CHANNEL"])
tlg_leave_chat(bot, chat.id)
return
# Bot leave/removed channel
else:
return
def chat_member_status_change(update: Update, context: CallbackContext):
'''Get Members chats status changes (user join/leave/added/removed to/from
group/channel) event handler. Note: if Bot is not an Admin, "chat_member"
update won't be received.'''
global new_users
bot = context.bot
# Ignore if it is not a new member join
if not tlg_has_new_member_join_group(update.chat_member):
return
# Get Chat data
chat = update.chat_member.chat
join_user = update.chat_member.new_chat_member.user
chat_id = chat.id
# Get User ID and Name
join_user_id = join_user.id
join_user_name = tlg_get_user_name(join_user, 35)
printts("[{}] New join detected: {} ({})".format(chat_id,
join_user_name, join_user_id))
# Get and update chat data
chat_title = chat.title
if chat_title:
save_config_property(chat_id, "Title", chat_title)
# Add an unicode Left to Right Mark (LRM) to chat title (fix for
# arabic, hebrew, etc.)
chat_title = add_lrm(chat_title)
chat_link = chat.username
if chat_link:
chat_link = "@{}".format(chat_link)
save_config_property(chat_id, "Link", chat_link)
# Check if the Bot should manage a Captcha process to this Group and Member
if not should_manage_captcha(update, bot):
return
# Check and remove previous join messages of that user (if any)
if chat_id in new_users:
if join_user_id in new_users[chat_id]:
if "msg_to_rm" in new_users[chat_id][join_user_id]:
for msg in new_users[chat_id][join_user_id]["msg_to_rm"]:
tlg_delete_msg(bot, chat_id, msg)
new_users[chat_id][join_user_id]["msg_to_rm"].clear()
# Ignore if the captcha protection is not enable in this chat
captcha_enable = get_chat_config(chat_id, "Enabled")
if not captcha_enable:
printts("[{}] Captcha is not enabled in this chat".format(chat_id))
return
# Determine configured language and captcha settings
lang = get_chat_config(chat_id, "Language")
captcha_level = get_chat_config(chat_id, "Captcha_Difficulty_Level")
captcha_mode = get_chat_config(chat_id, "Captcha_Chars_Mode")
captcha_timeout = get_chat_config(chat_id, "Captcha_Time")
if captcha_timeout < CONST["T_SECONDS_IN_MIN"]:
timeout_str = "{} sec".format(captcha_timeout)
else:
timeout_str = "{} min".format(int(captcha_timeout / CONST["T_SECONDS_IN_MIN"]))
send_problem = False
captcha_num = ""
if captcha_mode == "random":
captcha_mode = choice(["nums", "math", "poll"])
# If Captcha Mode Poll is not configured use another mode
if captcha_mode == "poll":
poll_question = get_chat_config(chat_id, "Poll_Q")
poll_options = get_chat_config(chat_id, "Poll_A")
poll_correct_option = get_chat_config(chat_id, "Poll_C_A")
if (poll_question == "") or \
(num_config_poll_options(poll_options) < 2) or \
(poll_correct_option == 0):
captcha_mode = choice(["nums", "math"])
if captcha_mode == "button":
# Send a button-only challenge
challenge_text = TEXT[lang]["NEW_USER_BUTTON_MODE"].format(join_user_name,
chat_title, timeout_str)
# Prepare inline keyboard button to let user pass
keyboard = [[InlineKeyboardButton(TEXT[lang]["PASS_BTN_TEXT"],
callback_data="button_captcha {}".format(join_user_id))]]
reply_markup = InlineKeyboardMarkup(keyboard)
printts("[{}] Sending captcha message to {}: [button]".format(chat_id, join_user_name))
sent_result = tlg_send_msg(bot, chat_id, challenge_text,
reply_markup=reply_markup, timeout=40)
if sent_result["msg"] is None:
send_problem = True
elif captcha_mode == "poll":
poll_question = get_chat_config(chat_id, "Poll_Q")
poll_options = get_chat_config(chat_id, "Poll_A")
poll_correct_option = get_chat_config(chat_id, "Poll_C_A")
if (poll_question == "") or (num_config_poll_options(poll_options) < 2) \
or (poll_correct_option == 0):
tlg_send_selfdestruct_msg_in(bot, chat_id, TEXT[lang]["POLL_NEW_USER_NOT_CONFIG"], \
CONST["T_FAST_DEL_MSG"])
return
# Remove empty strings from options list
poll_options = list(filter(None, poll_options))
# Send request to solve the poll text message
poll_request_msg_text = TEXT[lang]["POLL_NEW_USER"].format(join_user_name,
chat_title, timeout_str)
sent_result = tlg_send_selfdestruct_msg(bot, chat_id, poll_request_msg_text)
solve_poll_request_msg_id = None
if sent_result is not None:
solve_poll_request_msg_id = sent_result
# Send the Poll
sent_result = tlg_send_poll(bot, chat_id, poll_question, poll_options,
poll_correct_option-1, captcha_timeout, False, Poll.QUIZ)
if sent_result["msg"] is None:
send_problem = True
else:
# Save some info about the poll the bot_data for
# later use in receive_quiz_answer
poll_id = sent_result["msg"].poll.id
poll_msg_id = sent_result["msg"].message_id
poll_data = {
poll_id:
{
"chat_id": chat_id,
"poll_msg_id": poll_msg_id,
"user_id": join_user_id,
"correct_option": poll_correct_option
}
}
context.bot_data.update(poll_data)
else: # Image captcha
# Generate a pseudorandom captcha send it to telegram group and
# program message
captcha = create_image_captcha(chat_id, join_user_id, captcha_level, \
captcha_mode)
if captcha_mode == "math":
captcha_num = captcha["equation_result"]
printts("[{}] Sending captcha message to {}: {}={}...".format( \
chat_id, join_user_name, captcha["equation_str"], \
captcha["equation_result"]))
# Note: Img caption must be <= 1024 chars
img_caption = TEXT[lang]["NEW_USER_MATH_CAPTION"].format( \
join_user_name, chat_title, timeout_str)
else:
captcha_num = captcha["characters"]
printts("[{}] Sending captcha message to {}: {}...".format( \
chat_id, join_user_name, captcha_num))
# Note: Img caption must be <= 1024 chars
img_caption = TEXT[lang]["NEW_USER_IMG_CAPTION"].format( \
join_user_name, chat_title, timeout_str)
# Prepare inline keyboard button to let user request another captcha
keyboard = [[InlineKeyboardButton(TEXT[lang]["OTHER_CAPTCHA_BTN_TEXT"],
callback_data="image_captcha {}".format(join_user_id))]]
reply_markup = InlineKeyboardMarkup(keyboard)
# Send the image
sent_result = tlg_send_image(bot, chat_id, \
open(captcha["image"],"rb"), img_caption, \
reply_markup=reply_markup)
if sent_result["msg"] is None:
send_problem = True
# Remove sent captcha image file from file system
if path.exists(captcha["image"]):
remove(captcha["image"])
if not send_problem:
# Add sent captcha message to self-destruct list
if sent_result["msg"] is not None:
tlg_msg_to_selfdestruct_in(sent_result["msg"], captcha_timeout+10)
# Default user join data
join_data = \
{
"user_name": join_user_name,
"captcha_num": captcha_num,
"captcha_mode": captcha_mode,
"join_time": time(),
"captcha_timeout": captcha_timeout,
"join_retries": 1,
"kicked_ban": False
}
# Create dict keys for new user
if chat_id not in new_users:
new_users[chat_id] = {}
if join_user_id not in new_users[chat_id]:
new_users[chat_id][join_user_id] = {}
if "join_data" not in new_users[chat_id][join_user_id]:
new_users[chat_id][join_user_id]["join_data"] = {}
if "join_msg" not in new_users[chat_id][join_user_id]:
new_users[chat_id][join_user_id]["join_msg"] = None
if "msg_to_rm" not in new_users[chat_id][join_user_id]:
new_users[chat_id][join_user_id]["msg_to_rm"] = []
if "msg_to_rm_on_kick" not in new_users[chat_id][join_user_id]:
new_users[chat_id][join_user_id]["msg_to_rm_on_kick"] = []
# Check if this user was before in the chat without solve the captcha
# and restore previous join_retries
if len(new_users[chat_id][join_user_id]["join_data"]) != 0:
join_data["join_retries"] = new_users[chat_id][join_user_id]["join_data"]["join_retries"]
# Add new user join data and messages to be removed
new_users[chat_id][join_user_id]["join_data"] = join_data
if update.message:
new_users[chat_id][join_user_id]["join_msg"] = update.message.message_id
if sent_result["msg"]:
new_users[chat_id][join_user_id]["msg_to_rm"].append(sent_result["msg"].message_id)
if (captcha_mode == "poll") and (solve_poll_request_msg_id is not None):
new_users[chat_id][join_user_id]["msg_to_rm"].append(solve_poll_request_msg_id)
# Restrict user to deny send any kind of message until captcha is solve
# Allow send text messages for image based captchas that requires it
if (captcha_mode == "poll") or (captcha_mode == "button"):
tlg_restrict_user(bot, chat_id, join_user_id, send_msg=False,
send_media=False, send_stickers_gifs=False, insert_links=False,
send_polls=False, invite_members=False, pin_messages=False,
change_group_info=False)
else:
# Restrict user to only allow send text messages
tlg_restrict_user(bot, chat_id, join_user_id, send_msg=True,
send_media=False, send_stickers_gifs=False, insert_links=False,
send_polls=False, invite_members=False, pin_messages=False,
change_group_info=False)
printts("[{}] Captcha send process completed.".format(chat_id))
printts(" ")
def msg_user_joined_group(update: Update, context: CallbackContext):
'''New member join the group event handler'''
global new_users
# Get message data
chat_id = None
update_msg = tlg_get_msg(update)
if update_msg is not None:
chat_id = getattr(update_msg, "chat_id", None)
if (update_msg is None) or (chat_id is None):
print("Warning: Received an unexpected update.")
print(update)
return
msg_id = getattr(update_msg, "message_id", None)
if msg_id is None:
return
new_chat_members = getattr(update_msg, "new_chat_members", None)
if new_chat_members is None:
return
# For each new user that join or has been added
for join_user in new_chat_members:
# Ignore if the chat is not expected
if chat_id not in new_users:
continue
# Ignore if user is not expected
if join_user.id not in new_users[chat_id]:
continue
# If user has join the group, add the "USER joined the group"
# message ID to new user data to be removed
new_users[chat_id][join_user.id]["join_msg"] = msg_id
def msg_notext(update: Update, context: CallbackContext):
'''All non-text messages handler.'''
bot = context.bot
# Get message data
chat = None
chat_id = None
update_msg = None
update_msg = tlg_get_msg(update)
if update_msg is not None:
chat = getattr(update_msg, "chat", None)
chat_id = getattr(update_msg, "chat_id", None)
if (update_msg is None) or (chat is None) or (chat_id is None):
print("Warning: Received an unexpected update.")
print(update)
return
# Ignore if message comes from a private chat
if chat.type == "private":
return
# Ignore if message comes from a channel
if chat.type == "channel":
return
# Ignore if message is a channel post automatically forwarded to the
# connected discussion group
if tlg_is_a_channel_msg_on_discussion_group(update_msg):
return
# Ignore if captcha protection is not enable in this chat
captcha_enable = get_chat_config(chat_id, "Enabled")
if not captcha_enable:
return
# Ignore if msg not from a new user that needs to solve the captcha
user_id = update_msg.from_user.id
if chat_id not in new_users:
return
if user_id not in new_users[chat_id]:
return
# Get username, if has an alias, just use the alias
user_name = update_msg.from_user.full_name
if update_msg.from_user.username is not None:
user_name = "@{}".format(update_msg.from_user.username)
# Remove send message and notify that not text messages are not allowed until solve captcha
msg_id = update_msg.message_id
printts("[{}] Removing non-text message sent by {}".format(chat_id, user_name))
tlg_delete_msg(bot, chat_id, msg_id)
lang = get_chat_config(chat_id, "Language")
bot_msg = TEXT[lang]["NOT_TEXT_MSG_ALLOWED"].format(user_name)
tlg_send_selfdestruct_msg_in(bot, chat_id, bot_msg, CONST["T_FAST_DEL_MSG"])
def msg_nocmd(update: Update, context: CallbackContext):
'''Non-command text messages handler'''
global new_users
bot = context.bot
# Get message data
chat = None
chat_id = None
update_msg = None
update_msg = tlg_get_msg(update)
if update_msg is not None:
chat = getattr(update_msg, "chat", None)
chat_id = getattr(update_msg, "chat_id", None)
if (update_msg is None) or (chat is None) or (chat_id is None):
print("Warning: Received an unexpected update.")
print(update)
return
# Ignore if message comes from a private chat
if chat.type == "private":
return
# Ignore if message comes from a channel
if chat.type == "channel":
return
# Ignore if message is a channel post automatically forwarded to the
# connected discussion group
if tlg_is_a_channel_msg_on_discussion_group(update_msg):
return
# Ignore if captcha protection is not enable in this chat
captcha_enable = get_chat_config(chat_id, "Enabled")
if not captcha_enable:
return
# If message doesn't has text, check for caption fields (for no text msgs
# and forward ones)
msg_text = getattr(update_msg, "text", None)
if msg_text is None:
msg_text = getattr(update_msg, "caption_html", None)
if msg_text is None:
msg_text = getattr(update_msg, "caption", None)
# Check if message has a text link (embedded url in text) and get it
msg_entities = getattr(update_msg, "entities", None)
if msg_entities is None:
msg_entities = []
for entity in msg_entities:
url = getattr(entity, "url", None)
if url is not None:
if url != "":
if msg_text is None:
msg_text = url
else:
msg_text = "{} [{}]".format(msg_text, url)
break
# Get others message data
user_id = update_msg.from_user.id
msg_id = update_msg.message_id
# Get and update chat data
chat_title = chat.title
if chat_title:
save_config_property(chat_id, "Title", chat_title)
chat_link = chat.username
if chat_link:
chat_link = "@{}".format(chat_link)
save_config_property(chat_id, "Link", chat_link)
user_name = update_msg.from_user.full_name
# If has an alias, just use the alias
if update_msg.from_user.username is not None:
user_name = "@{}".format(update_msg.from_user.username)
# Set default text message if not received
if msg_text is None:
msg_text = "[Not a text message]"
# Check if group is configured to deny users send URLs, and remove URLs msg
url_enable = get_chat_config(chat_id, "URL_Enabled")
if not url_enable:
# Ignore if message comes from an Admin
is_admin = tlg_user_is_admin(bot, user_id, chat_id)
if is_admin:
return
# Get Chat configured language
lang = get_chat_config(chat_id, "Language")
# Check for Spam (check if the message contains any URL)
has_url = re.findall(CONST["REGEX_URLS"], msg_text)
if has_url:
# Try to remove the message and notify detection
delete_result = tlg_delete_msg(bot, chat_id, msg_id)
if delete_result["error"] == "":
bot_msg = TEXT[lang]["URL_MSG_NOT_ALLOWED_DETECTED"].format(user_name)
tlg_send_selfdestruct_msg_in(bot, chat_id, bot_msg, CONST["T_FAST_DEL_MSG"])
# Ignore if message is not from a new user that has not completed the captcha yet
if chat_id not in new_users:
return
if user_id not in new_users[chat_id]:
return
# Get Chat settings
lang = get_chat_config(chat_id, "Language")
rm_result_msg = get_chat_config(chat_id, "Rm_Result_Msg")
captcha_mode = new_users[chat_id][user_id]["join_data"]["captcha_mode"]
# Check for forwarded messages and delete it
forward_from = getattr(update_msg, "forward_from", None)
forward_from_chat = getattr(update_msg, "forward_from_chat", None)
if (forward_from is not None) or (forward_from_chat is not None):
printts("[{}] Spammer detected: {}.".format(chat_id, user_name))
printts("[{}] Removing forwarded msg: {}.".format(chat_id, msg_text))
delete_result = tlg_delete_msg(bot, chat_id, msg_id)
if delete_result["error"] == "":
printts("Message removed.")
elif delete_result["error"] == "Message can't be deleted":
printts("No rights to remove msg.")
else:
printts("Message can't be deleted.")
return
# Check for Spam (check if the message contains any URL or alias)
has_url = re.findall(CONST["REGEX_URLS"], msg_text)
has_alias = tlg_alias_in_string(msg_text)
if has_url or has_alias:
printts("[{}] Spammer detected: {}.".format(chat_id, user_name))
printts("[{}] Removing spam message: {}.".format(chat_id, msg_text))
# Try to remove the message and notify detection
delete_result = tlg_delete_msg(bot, chat_id, msg_id)
if delete_result["error"] == "":
bot_msg = TEXT[lang]["SPAM_DETECTED_RM"].format(user_name)
tlg_send_selfdestruct_msg_in(bot, chat_id, bot_msg, CONST["T_FAST_DEL_MSG"])
# Check if message cant be removed due to not delete msg privileges
elif delete_result["error"] == "Message can't be deleted":
bot_msg = TEXT[lang]["SPAM_DETECTED_NOT_RM"].format(user_name)
tlg_send_selfdestruct_msg_in(bot, chat_id, bot_msg, CONST["T_FAST_DEL_MSG"])
else:
printts("Message can't be deleted.")
return
# Check group config regarding if all messages of user must be removed when kick
rm_all_msg = get_chat_config(chat_id, "RM_All_Msg")
if rm_all_msg:
new_users[chat_id][user_id]["msg_to_rm_on_kick"].append(msg_id)
# End here if no image captcha mode
if captcha_mode not in { "nums", "hex", "ascii", "math" }:
return
printts("[{}] Received captcha reply from {}: {}".format(chat_id, user_name, msg_text))
# Check if the expected captcha solve number is in the message
solve_num = new_users[chat_id][user_id]["join_data"]["captcha_num"]
if is_captcha_num_solve(captcha_mode, msg_text, solve_num):
printts("[{}] Captcha solved by {}".format(chat_id, user_name))
# Remove all restrictions on the user
tlg_unrestrict_user(bot, chat_id, user_id)
# Remove join messages
for msg in new_users[chat_id][user_id]["msg_to_rm"]:
tlg_delete_msg(bot, chat_id, msg)
new_users[chat_id][user_id]["msg_to_rm"].clear()
new_users[chat_id][user_id]["msg_to_rm_on_kick"].clear()
del new_users[chat_id][user_id]
# Remove user captcha numbers message
tlg_delete_msg(bot, chat_id, msg_id)
# Send message solve message
bot_msg = TEXT[lang]["CAPTCHA_SOLVED"].format(user_name)
if rm_result_msg:
tlg_send_selfdestruct_msg_in(bot, chat_id, bot_msg, CONST["T_FAST_DEL_MSG"])
else:
tlg_send_msg(bot, chat_id, bot_msg)
# Check for custom welcome message and send it
welcome_msg = get_chat_config(chat_id, "Welcome_Msg").format(escape_markdown(user_name, 2))
if welcome_msg != "-":
# Send the message as Markdown
rm_welcome_msg = get_chat_config(chat_id, "Rm_Welcome_Msg")
if rm_welcome_msg:
welcome_msg_time = get_chat_config(chat_id, "Welcome_Time")
sent_result = tlg_send_selfdestruct_msg_in(bot, chat_id, welcome_msg,
welcome_msg_time, parse_mode="MARKDOWN")
else:
sent_result = tlg_send_msg(bot, chat_id, welcome_msg, "MARKDOWN")
if sent_result is None:
printts("[{}] Error: Can't send the welcome message.".format(chat_id))
# Check for send just text message option and apply user restrictions
restrict_non_text_msgs = get_chat_config(chat_id, "Restrict_Non_Text")
# Restrict for 1 day
if restrict_non_text_msgs == 1:
tomorrow_epoch = get_unix_epoch() + CONST["T_RESTRICT_NO_TEXT_MSG"]
tlg_restrict_user(bot, chat_id, user_id, send_msg=True, send_media=False,
send_stickers_gifs=False, insert_links=False, send_polls=False,
invite_members=False, pin_messages=False, change_group_info=False,
until_date=tomorrow_epoch)
# Restrict forever
elif restrict_non_text_msgs == 2:
tlg_restrict_user(bot, chat_id, user_id, send_msg=True, send_media=False,
send_stickers_gifs=False, insert_links=False, send_polls=False,
invite_members=False, pin_messages=False, change_group_info=False)
# The provided message doesn't has the valid captcha number
else:
# Check if the message is for a math equation captcha
if (captcha_mode == "math"):
clueless_user = False
# Check if message is just 4 numbers
if is_int(msg_text) and (len(msg_text) == 4):
clueless_user = True
# Check if message is "NN+NN" or "NN-NN"
elif (len(msg_text) == 5) and (is_int(msg_text[:2])) and \
(is_int(msg_text[3:])) and (msg_text[2] in ["+", "-"]):
clueless_user = True
# Tell the user that is wrong
if clueless_user:
sent_msg_id = tlg_send_selfdestruct_msg_in(bot, chat_id, \
TEXT[lang]["CAPTCHA_INCORRECT_MATH"], CONST["T_FAST_DEL_MSG"])
new_users[chat_id][user_id]["msg_to_rm"].append(sent_msg_id)
new_users[chat_id][user_id]["msg_to_rm"].append(msg_id)
# If "nums", "hex" or "ascii" captcha
else:
# Check if the message has 4 chars
if len(msg_text) == 4:
sent_msg_id = tlg_send_selfdestruct_msg_in(bot, chat_id, \
TEXT[lang]["CAPTCHA_INCORRECT_0"], CONST["T_FAST_DEL_MSG"])
new_users[chat_id][user_id]["msg_to_rm"].append(sent_msg_id)
new_users[chat_id][user_id]["msg_to_rm"].append(msg_id)
# Check if the message was just a 4 numbers msg
elif is_int(msg_text):
sent_msg_id = tlg_send_selfdestruct_msg_in(bot, chat_id, \
TEXT[lang]["CAPTCHA_INCORRECT_1"], CONST["T_FAST_DEL_MSG"])
new_users[chat_id][user_id]["msg_to_rm"].append(sent_msg_id)
new_users[chat_id][user_id]["msg_to_rm"].append(msg_id)
printts("[{}] Captcha reply process completed.".format(chat_id))
printts(" ")
def receive_poll_answer(update: Update, context: CallbackContext):
'''User poll vote received'''
global new_users
bot = context.bot
active_polls = context.bot_data
poll_id = update.poll_answer.poll_id
from_user = update.poll_answer.user
option_answer = update.poll_answer.option_ids[0] + 1
msg_text = "User {} select poll option {}".format(from_user.username, option_answer)
print(msg_text)
# Ignore any Poll vote that comes from unexpected poll
if poll_id not in active_polls:
return
poll_data = active_polls[poll_id]
# Ignore Poll votes that doesn't come from expected user in captcha process
if from_user.id != poll_data["user_id"]:
return
# Handle poll vote
chat_id = poll_data["chat_id"]
user_id = poll_data["user_id"]
poll_msg_id = poll_data["poll_msg_id"]
poll_correct_option = poll_data["correct_option"]
# The vote come from expected user, let's stop the Poll
tlg_stop_poll(bot, chat_id, poll_msg_id)
# Get user name (if has an alias, just use the alias)
user_name = from_user.full_name
if from_user.username is not None:
user_name = "@{}".format(from_user.username)
# Get chat settings
lang = get_chat_config(chat_id, "Language")
rm_result_msg = get_chat_config(chat_id, "Rm_Result_Msg")
rm_welcome_msg = get_chat_config(chat_id, "Rm_Welcome_Msg")
welcome_msg = get_chat_config(chat_id, "Welcome_Msg").format(escape_markdown(user_name, 2))
restrict_non_text_msgs = get_chat_config(chat_id, "Restrict_Non_Text")
# Wait 3s to let poll animation be shown
sleep(3)
# Remove previous join messages
for msg in new_users[chat_id][user_id]["msg_to_rm"]:
tlg_delete_msg(bot, chat_id, msg)
new_users[chat_id][user_id]["msg_to_rm"].clear()
# Check if user vote the correct option
if option_answer == poll_correct_option:
printts("[{}] User {} solved a poll challenge.".format(chat_id, user_name))
# Remove all restrictions on the user
tlg_unrestrict_user(bot, chat_id, user_id)
# Send captcha solved message
bot_msg = TEXT[lang]["CAPTCHA_SOLVED"].format(user_name)
if rm_result_msg:
tlg_send_selfdestruct_msg_in(bot, chat_id, bot_msg, CONST["T_FAST_DEL_MSG"])
else:
tlg_send_msg(bot, chat_id, bot_msg)
del new_users[chat_id][user_id]
# Check for custom welcome message and send it
if welcome_msg != "-":
if rm_welcome_msg:
welcome_msg_time = get_chat_config(chat_id, "Welcome_Time")
sent_result = tlg_send_selfdestruct_msg_in(bot, chat_id, welcome_msg,
welcome_msg_time, parse_mode="MARKDOWN")
else:
sent_result = tlg_send_msg(bot, chat_id, welcome_msg, "MARKDOWN")
if sent_result is None:
printts("[{}] Error: Can't send the welcome message.".format(chat_id))
# Check for send just text message option and apply user restrictions
if restrict_non_text_msgs == 1: # Restrict for 1 day
tomorrow_epoch = get_unix_epoch() + CONST["T_RESTRICT_NO_TEXT_MSG"]
tlg_restrict_user(bot, chat_id, user_id, send_msg=True, send_media=False,
send_stickers_gifs=False, insert_links=False, send_polls=False,
invite_members=False, pin_messages=False, change_group_info=False,
until_date=tomorrow_epoch)
elif restrict_non_text_msgs == 2: # Restrict forever
tlg_restrict_user(bot, chat_id, user_id, send_msg=True, send_media=False,
send_stickers_gifs=False, insert_links=False, send_polls=False,
invite_members=False, pin_messages=False, change_group_info=False)
else:
# Notify captcha fail
printts("[{}] User {} fail a poll challenge.".format(chat_id, user_name))
bot_msg = TEXT[lang]["CAPTCHA_POLL_FAIL"].format(user_name)
sent_msg_id = None
if rm_result_msg:
sent_result = tlg_send_msg(bot, chat_id, bot_msg)
if sent_result["msg"] is not None:
sent_msg_id = sent_result["msg"].message_id
else:
tlg_send_msg(bot, chat_id, bot_msg)
# Wait 10s
sleep(10)
# Try to kick the user
captcha_fail_kick_ban_member(bot, chat_id, user_id,
CONST["MAX_FAIL_BAN_POLL"])
printts("[{}] Poll captcha process completed.".format(chat_id))
printts(" ")
def key_inline_keyboard(update: Update, context: CallbackContext):
'''Inline Keyboard button pressed handler'''
bot = context.bot
query = update.callback_query
# Confirm query received
query_ans_result = tlg_answer_callback_query(bot, query)
if query_ans_result["error"] != "":
return
# Convert query provided data into list
button_data = query.data.split(" ")
# Ignore if the query data unexpected or comes from an unexpected user
if (len(button_data) < 2) or (button_data[1] != str(query.from_user.id)):
return
# Get type of inline keyboard button pressed and user ID associated to that button
key_pressed = button_data[0]
# Check and handle "request new img captcha" or "button captcha challenge" buttons
if "image_captcha" in key_pressed:
button_request_captcha(bot, query)
elif "button_captcha" in key_pressed:
button_request_pass(bot, query)
def button_request_captcha(bot, query):
'''Button "Another captcha" pressed handler'''
global new_users
# Get query data
chat_id = query.message.chat_id
user_id = query.from_user.id
msg_id = query.message.message_id
user_name = query.from_user.full_name
# If has an alias, just use the alias
if query.from_user.username is not None:
user_name = "@{}".format(query.from_user.username)
chat_title = query.message.chat.title
# Add an unicode Left to Right Mark (LRM) to chat title
# (fix for arabic, hebrew, etc.)
chat_title = add_lrm(chat_title)
# Ignore if message is not from a new user that has not
# completed the captcha yet
if chat_id not in new_users:
return
if user_id not in new_users[chat_id]:
return
# Get chat language
lang = get_chat_config(chat_id, "Language")
printts("[{}] User {} requested a new captcha.".format(chat_id, user_name))
# Prepare inline keyboard button to let user request another captcha
keyboard = [[InlineKeyboardButton(TEXT[lang]["OTHER_CAPTCHA_BTN_TEXT"],
callback_data="image_captcha {}".format(str(query.from_user.id)))]]
reply_markup = InlineKeyboardMarkup(keyboard)
# Get captcha timeout
captcha_timeout = get_chat_config(chat_id, "Captcha_Time")
if captcha_timeout < CONST["T_SECONDS_IN_MIN"]:
timeout_str = "{} sec".format(captcha_timeout)
else:
timeout_min = int(captcha_timeout / CONST["T_SECONDS_IN_MIN"])
timeout_str = "{} min".format(timeout_min)
# Get current chat configurations
captcha_level = get_chat_config(chat_id, "Captcha_Difficulty_Level")
captcha_mode = new_users[chat_id][user_id]["join_data"]["captcha_mode"]
# Use nums mode if captcha_mode was changed while captcha was in progress
if captcha_mode not in { "nums", "hex", "ascii", "math" }:
captcha_mode = "nums"
# Generate a new captcha and edit previous captcha image message
captcha = create_image_captcha(chat_id, user_id, captcha_level, \
captcha_mode)
if captcha_mode == "math":
captcha_num = captcha["equation_result"]
printts("[{}] Sending new captcha msg: {} = {}...".format(chat_id, \
captcha["equation_str"], captcha_num))
img_caption = TEXT[lang]["NEW_USER_MATH_CAPTION"].format(user_name, \
chat_title, timeout_str)
else:
captcha_num = captcha["characters"]
printts("[{}] Sending new captcha msg: {}...".format( \
chat_id, captcha_num))
img_caption = TEXT[lang]["NEW_USER_IMG_CAPTION"].format(user_name, \
chat_title, timeout_str)
input_media = InputMediaPhoto(media=open(captcha["image"], "rb"), \
caption=img_caption)
edit_result = tlg_edit_msg_media(bot, chat_id, msg_id, media=input_media, \
reply_markup=reply_markup, timeout=20)
if edit_result["error"] == "":
# Set and modified to new expected captcha number
new_users[chat_id][user_id]["join_data"]["captcha_num"] = captcha_num
# Remove sent captcha image file from file system
if path.exists(captcha["image"]):
remove(captcha["image"])
printts("[{}] New captcha request process completed.".format(chat_id))
printts(" ")
def button_request_pass(bot, query):
'''Button "I'm not a bot" pressed handler'''
global new_users
# Get query data
chat_id = query.message.chat_id
user_id = query.from_user.id
user_name = query.from_user.full_name
# If has an alias, just use the alias
if query.from_user.username is not None:
user_name = "@{}".format(query.from_user.username)
chat_title = query.message.chat.title
# Add an unicode Left to Right Mark (LRM) to chat title (fix for arabic, hebrew, etc.)
chat_title = add_lrm(chat_title)
# Ignore if request doesn't come from a new user in captcha process
if chat_id not in new_users:
return
if user_id not in new_users[chat_id]:
return
# Get chat settings
lang = get_chat_config(chat_id, "Language")
rm_result_msg = get_chat_config(chat_id, "Rm_Result_Msg")
# Remove previous join messages
for msg in new_users[chat_id][user_id]["msg_to_rm"]:
tlg_delete_msg(bot, chat_id, msg)
# Remove user from captcha process
del new_users[chat_id][user_id]
# Send message solve message
printts("[{}] User {} solved a button-only challenge.".format(chat_id, user_name))
# Remove all restrictions on the user
tlg_unrestrict_user(bot, chat_id, user_id)
# Send captcha solved message
bot_msg = TEXT[lang]["CAPTCHA_SOLVED"].format(user_name)
if rm_result_msg:
tlg_send_selfdestruct_msg_in(bot, chat_id, bot_msg, CONST["T_FAST_DEL_MSG"])
else:
tlg_send_msg(bot, chat_id, bot_msg)
# Check for custom welcome message and send it
welcome_msg = get_chat_config(chat_id, "Welcome_Msg").format(escape_markdown(user_name, 2))
if welcome_msg != "-":
# Send the message as Markdown
rm_welcome_msg = get_chat_config(chat_id, "Rm_Welcome_Msg")
if rm_welcome_msg:
welcome_msg_time = get_chat_config(chat_id, "Welcome_Time")
sent_result = tlg_send_selfdestruct_msg_in(bot, chat_id, welcome_msg,
welcome_msg_time, parse_mode="MARKDOWN")
else:
sent_result = tlg_send_msg(bot, chat_id, welcome_msg, "MARKDOWN")
if sent_result is None:
printts("[{}] Error: Can't send the welcome message.".format(chat_id))
# Check for send just text message option and apply user restrictions
restrict_non_text_msgs = get_chat_config(chat_id, "Restrict_Non_Text")
# Restrict for 1 day
if restrict_non_text_msgs == 1:
tomorrow_epoch = get_unix_epoch() + CONST["T_RESTRICT_NO_TEXT_MSG"]
tlg_restrict_user(bot, chat_id, user_id, send_msg=True, send_media=False,
send_stickers_gifs=False, insert_links=False, send_polls=False,
invite_members=False, pin_messages=False, change_group_info=False,
until_date=tomorrow_epoch)
# Restrict forever
elif restrict_non_text_msgs == 2:
tlg_restrict_user(bot, chat_id, user_id, send_msg=True, send_media=False,
send_stickers_gifs=False, insert_links=False, send_polls=False,
invite_members=False, pin_messages=False, change_group_info=False)
printts("[{}] Button-only challenge pass request process completed.".format(chat_id))
printts(" ")
###############################################################################
### Received Telegram command messages handlers
def cmd_start(update: Update, context: CallbackContext):
'''Command /start message handler'''
bot = context.bot
# Ignore command if it was a edited message
update_msg = getattr(update, "message", None)
if update_msg is None:
return
chat_id = update_msg.chat_id
chat_type = update_msg.chat.type
lang = get_update_user_lang(update_msg.from_user)
if chat_type == "private":
tlg_send_msg(bot, chat_id, TEXT[lang]["START"])
else:
# Remove command message automatically after a while
tlg_msg_to_selfdestruct(update_msg)
# Ignore if not requested by a group Admin
user_id = update_msg.from_user.id
is_admin = tlg_user_is_admin(bot, user_id, chat_id)
if (is_admin is None) or (is_admin == False):
return
# Send the response message
lang = get_chat_config(chat_id, "Language")
tlg_send_selfdestruct_msg(bot, chat_id, TEXT[lang]["START"])
def cmd_help(update: Update, context: CallbackContext):
'''Command /help message handler'''
bot = context.bot
# Ignore command if it was a edited message
update_msg = getattr(update, "message", None)
if update_msg is None:
return
chat_id = update_msg.chat_id
chat_type = update_msg.chat.type
lang = get_update_user_lang(update_msg.from_user)
if chat_type == "private":
tlg_send_msg(bot, chat_id, TEXT[lang]["HELP"])
else:
# Remove command message automatically after a while
tlg_msg_to_selfdestruct(update_msg)
# Ignore if not requested by a group Admin
user_id = update_msg.from_user.id
is_admin = tlg_user_is_admin(bot, user_id, chat_id)
if (is_admin is None) or (is_admin == False):
return
# Send the response message
lang = get_chat_config(chat_id, "Language")
tlg_send_selfdestruct_msg(bot, chat_id, TEXT[lang]["HELP"])
def cmd_commands(update: Update, context: CallbackContext):
'''Command /commands message handler'''
bot = context.bot
# Ignore command if it was a edited message
update_msg = getattr(update, "message", None)
if update_msg is None:
return
chat_id = update_msg.chat_id
chat_type = update_msg.chat.type
lang = get_update_user_lang(update_msg.from_user)
if chat_type == "private":
tlg_send_msg(bot, chat_id, TEXT[lang]["COMMANDS"])
else:
# Remove command message automatically after a while
tlg_msg_to_selfdestruct(update_msg)
# Ignore if not requested by a group Admin
user_id = update_msg.from_user.id
is_admin = tlg_user_is_admin(bot, user_id, chat_id)
if (is_admin is None) or (is_admin == False):
return
# Send the response message
lang = get_chat_config(chat_id, "Language")
tlg_send_selfdestruct_msg(bot, chat_id, TEXT[lang]["COMMANDS"])
def cmd_connect(update: Update, context: CallbackContext):
'''Command /connect message handler'''
global connections
bot = context.bot
args = context.args
# Ignore command if it was a edited message
update_msg = getattr(update, "message", None)
if update_msg is None:
return
chat_id = update_msg.chat_id
chat_type = update_msg.chat.type
user_id = update_msg.from_user.id
user_alias = update_msg.from_user.username
if user_alias is not None:
user_alias = "@{}".format(user_alias)
lang = get_update_user_lang(update_msg.from_user)
# Ignore if command is not in private chat
if chat_type != "private":
# Remove command message automatically after a while
tlg_msg_to_selfdestruct(update_msg)
# Ignore if not requested by a group Admin
is_admin = tlg_user_is_admin(bot, user_id, chat_id)
if (is_admin is None) or (is_admin == False):
return
# Send just allowed in private chat message
lang = get_chat_config(chat_id, "Language")
tlg_send_msg_type_chat(bot, chat_type, chat_id,
TEXT[lang]["CMD_JUST_IN_PRIVATE"])
return
# Check for group chat ID
if len(args) == 0:
tlg_send_msg_type_chat(bot, chat_type, chat_id,
TEXT[lang]["CONNECT_USAGE"])
return
group_id = args[0]
# Add "-" if not present
if group_id[0] != "-":
group_id = "-{}".format(group_id)
if not tlg_is_valid_group(group_id):
tlg_send_msg_type_chat(bot, chat_type, chat_id,
TEXT[lang]["INVALID_GROUP_ID"])
return
# Check if requested by the Bot owner or an Admin of the group
if (str(user_id) != CONST["BOT_OWNER"]) and \
(user_alias != CONST["BOT_OWNER"]):
is_admin = tlg_user_is_admin(bot, user_id, group_id)
if (is_admin is None) or (is_admin == False):
tlg_send_msg_type_chat(bot, chat_type, chat_id,
TEXT[lang]["CONNECT_JUST_ADMIN"])
return
# Connection
group_lang = get_chat_config(group_id, "Language")
connections[user_id] = { "group_id": group_id, "lang": group_lang }
tlg_send_msg_type_chat(bot, chat_type, chat_id,
TEXT[lang]["CONNECT_OK"].format(group_id))
def cmd_disconnect(update: Update, context: CallbackContext):
'''Command /disconnect message handler'''
global connections
bot = context.bot
# Ignore command if it was a edited message
update_msg = getattr(update, "message", None)
if update_msg is None:
return
chat_id = update_msg.chat_id
chat_type = update_msg.chat.type
user_id = update_msg.from_user.id
lang = get_update_user_lang(update_msg.from_user)
# Ignore if command is not in private chat
if chat_type != "private":
# Remove command message automatically after a while
tlg_msg_to_selfdestruct(update_msg)
# Ignore if not requested by a group Admin
is_admin = tlg_user_is_admin(bot, user_id, chat_id)
if (is_admin is None) or (is_admin == False):
return
# Send just allowed in private chat message
lang = get_chat_config(chat_id, "Language")
tlg_send_msg_type_chat(bot, chat_type, chat_id,
TEXT[lang]["CMD_JUST_IN_PRIVATE"])
return
# Check if User is connected to some group
if user_id not in connections:
tlg_send_msg_type_chat(bot, chat_type, chat_id,
TEXT[lang]["DISCONNECT_NOT_CONNECTED"])
return
# Disconnection
lang = connections[user_id]["lang"]
group_id = connections[user_id]["group_id"]
del connections[user_id]
tlg_send_msg_type_chat(bot, chat_type, chat_id,
TEXT[lang]["DISCONNECT_OK"].format(group_id))
def cmd_checkcfg(update: Update, context: CallbackContext):
'''Command /checkcfg message handler'''
bot = context.bot
# Ignore command if it was a edited message
update_msg = getattr(update, "message", None)
if update_msg is None:
return
chat_id = update_msg.chat_id
user_id = update_msg.from_user.id
chat_type = update_msg.chat.type
lang = get_update_user_lang(update_msg.from_user)
if chat_type == "private":
if user_id not in connections:
tlg_send_msg_type_chat(bot, chat_type, chat_id,
TEXT[lang]["CMD_NEEDS_CONNECTION"])
return
group_id = connections[user_id]["group_id"]
else:
# Remove command message automatically after a while
tlg_msg_to_selfdestruct(update_msg)
# Ignore if not requested by a group Admin
is_admin = tlg_user_is_admin(bot, user_id, chat_id)
if (is_admin is None) or (is_admin == False):
return
# Get Group Chat ID and configured language
group_id = chat_id
lang = get_chat_config(group_id, "Language")
# Get all group configs
group_cfg = get_all_chat_config(group_id)
group_cfg = json_dumps(group_cfg, indent=4, sort_keys=True)
tlg_send_msg_type_chat(bot, chat_type, chat_id,
TEXT[lang]["CHECK_CFG"].format(escape_markdown(group_cfg, 2)),
parse_mode="MARKDOWN")
def cmd_language(update: Update, context: CallbackContext):
'''Command /language message handler'''
bot = context.bot
args = context.args
# Ignore command if it was a edited message
update_msg = getattr(update, "message", None)
if update_msg is None:
return
chat_id = update_msg.chat_id
user_id = update_msg.from_user.id
chat_type = update_msg.chat.type
lang = get_update_user_lang(update_msg.from_user)
# Check usage in private chat
if chat_type == "private":
if user_id not in connections:
tlg_send_msg_type_chat(bot, chat_type, chat_id,
TEXT[lang]["CMD_NEEDS_CONNECTION"])
return
group_id = connections[user_id]["group_id"]
else:
# Remove command message automatically after a while
tlg_msg_to_selfdestruct(update_msg)
# Ignore if not requested by a group Admin
is_admin = tlg_user_is_admin(bot, user_id, chat_id)
if (is_admin is None) or (is_admin == False):
return
# Get Group Chat ID and configured language
group_id = chat_id
lang = get_chat_config(group_id, "Language")
# Check if no argument was provided with the command
if len(args) == 0:
msg_text = TEXT[lang]["LANG_NOT_ARG"].format(
CONST["SUPPORTED_LANGS_CMDS"])
tlg_send_msg_type_chat(bot, chat_type, chat_id, msg_text)
return
# Get and configure chat to provided language
lang_provided = args[0].upper()
if lang_provided in TEXT:
if lang_provided != lang:
lang = lang_provided
save_config_property(group_id, "Language", lang)
if (chat_type == "private") and (user_id in connections):
connections[user_id]["lang"] = lang
msg_text = TEXT[lang]["LANG_CHANGE"]
else:
msg_text = TEXT[lang]["LANG_SAME"].format(
CONST["SUPPORTED_LANGS_CMDS"])
else:
msg_text = TEXT[lang]["LANG_BAD_LANG"].format(
CONST["SUPPORTED_LANGS_CMDS"])
tlg_send_msg_type_chat(bot, chat_type, chat_id, msg_text)
def cmd_time(update: Update, context: CallbackContext):
'''Command /time message handler'''
bot = context.bot
args = context.args
# Ignore command if it was a edited message
update_msg = getattr(update, "message", None)
if update_msg is None:
return
chat_id = update_msg.chat_id
user_id = update_msg.from_user.id
chat_type = update_msg.chat.type
lang = get_update_user_lang(update_msg.from_user)
# Check and deny usage in private chat
if chat_type == "private":
if user_id not in connections:
tlg_send_msg_type_chat(bot, chat_type, chat_id,
TEXT[lang]["CMD_NEEDS_CONNECTION"])
return
group_id = connections[user_id]["group_id"]
else:
# Remove command message automatically after a while
tlg_msg_to_selfdestruct(update_msg)
# Ignore if not requested by a group Admin
is_admin = tlg_user_is_admin(bot, user_id, chat_id)
if (is_admin is None) or (is_admin == False):
return
# Get Group Chat ID and configured language
group_id = chat_id
lang = get_chat_config(group_id, "Language")
# Check if no argument was provided with the command
if len(args) == 0:
tlg_send_msg_type_chat(bot, chat_type, chat_id,
TEXT[lang]["TIME_NOT_ARG"])
return
# Check if provided time argument is not a number
if not is_int(args[0]):
tlg_send_msg_type_chat(bot, chat_type, chat_id,
TEXT[lang]["TIME_NOT_NUM"])
return
# Get time arguments
new_time = int(args[0])
min_sec = "min"
if len(args) > 1:
min_sec = args[1].lower()
# Check if time format is not minutes or seconds
# Convert time value to seconds if min format
if min_sec in ["m", "min", "mins", "minutes"]:
min_sec = "min"
new_time_str = "{} min".format(new_time)
new_time = new_time * CONST["T_SECONDS_IN_MIN"]
elif min_sec in ["s", "sec", "secs", "seconds"]:
min_sec = "sec"
new_time_str = "{} sec".format(new_time)
else:
tlg_send_msg_type_chat(bot, chat_type, chat_id,
TEXT[lang]["TIME_NOT_ARG"])
return
# Check if time value is out of limits
if new_time < 10: # Lees than 10s
msg_text = TEXT[lang]["TIME_OUT_RANGE"].format( \
CONST["MAX_CONFIG_CAPTCHA_TIME"])
tlg_send_msg_type_chat(bot, chat_type, chat_id, msg_text)
return
if new_time > CONST["MAX_CONFIG_CAPTCHA_TIME"] * CONST["T_SECONDS_IN_MIN"]:
msg_text = TEXT[lang]["TIME_OUT_RANGE"].format( \
CONST["MAX_CONFIG_CAPTCHA_TIME"])
tlg_send_msg_type_chat(bot, chat_type, chat_id, msg_text)
return
# Set the new captcha time
save_config_property(group_id, "Captcha_Time", new_time)
msg_text = TEXT[lang]["TIME_CHANGE"].format(new_time_str)
tlg_send_msg_type_chat(bot, chat_type, chat_id, msg_text)
def cmd_difficulty(update: Update, context: CallbackContext):
'''Command /difficulty message handler'''
bot = context.bot
args = context.args
# Ignore command if it was a edited message
update_msg = getattr(update, "message", None)
if update_msg is None:
return
chat_id = update_msg.chat_id
user_id = update_msg.from_user.id
chat_type = update_msg.chat.type
lang = get_update_user_lang(update_msg.from_user)
# Check and deny usage in private chat
if chat_type == "private":
if user_id not in connections:
tlg_send_msg_type_chat(bot, chat_type, chat_id,
TEXT[lang]["CMD_NEEDS_CONNECTION"])
return
group_id = connections[user_id]["group_id"]
else:
# Remove command message automatically after a while
tlg_msg_to_selfdestruct(update_msg)
# Ignore if not requested by a group Admin
is_admin = tlg_user_is_admin(bot, user_id, chat_id)
if (is_admin is None) or (is_admin == False):
return
# Get Group Chat ID and configured language
group_id = chat_id
lang = get_chat_config(group_id, "Language")
# Check if no argument was provided with the command
if len(args) == 0:
tlg_send_msg_type_chat(bot, chat_type, chat_id,
TEXT[lang]["DIFFICULTY_NOT_ARG"])
return
# Get and configure chat to provided captcha difficulty
if is_int(args[0]):
new_difficulty = int(args[0])
if new_difficulty < 1:
new_difficulty = 1
if new_difficulty > 5:
new_difficulty = 5
save_config_property(group_id, "Captcha_Difficulty_Level",
new_difficulty)
bot_msg = TEXT[lang]["DIFFICULTY_CHANGE"].format(new_difficulty)
else:
bot_msg = TEXT[lang]["DIFFICULTY_NOT_NUM"]
tlg_send_msg_type_chat(bot, chat_type, chat_id, bot_msg)
def cmd_captcha_mode(update: Update, context: CallbackContext):
'''Command /captcha_mode message handler'''
bot = context.bot
args = context.args
# Ignore command if it was a edited message
update_msg = getattr(update, "message", None)
if update_msg is None:
return
chat_id = update_msg.chat_id
user_id = update_msg.from_user.id
chat_type = update_msg.chat.type
lang = get_update_user_lang(update_msg.from_user)
# Check and deny usage in private chat
if chat_type == "private":
if user_id not in connections:
tlg_send_msg_type_chat(bot, chat_type, chat_id,
TEXT[lang]["CMD_NEEDS_CONNECTION"])
return
group_id = connections[user_id]["group_id"]
else:
# Remove command message automatically after a while
tlg_msg_to_selfdestruct(update_msg)
# Ignore if not requested by a group Admin
is_admin = tlg_user_is_admin(bot, user_id, chat_id)
if (is_admin is None) or (is_admin == False):
return
# Get Group Chat ID and configured language
group_id = chat_id
lang = get_chat_config(group_id, "Language")
# Check if no argument was provided with the command
if len(args) == 0:
tlg_send_msg_type_chat(bot, chat_type, chat_id,
TEXT[lang]["CAPTCHA_MODE_NOT_ARG"])
return
# Get and configure chat to provided captcha mode
new_captcha_mode = args[0].lower()
if new_captcha_mode in {
"poll", "button", "nums", "hex", "ascii", "math", "random" }:
save_config_property(group_id, "Captcha_Chars_Mode", new_captcha_mode)
bot_msg = TEXT[lang]["CAPTCHA_MODE_CHANGE"].format(new_captcha_mode)
else:
bot_msg = TEXT[lang]["CAPTCHA_MODE_INVALID"]
tlg_send_msg_type_chat(bot, chat_type, chat_id, bot_msg)
def cmd_welcome_msg(update: Update, context: CallbackContext):
'''Command /welcome_msg message handler'''
bot = context.bot
args = context.args
# Ignore command if it was a edited message
update_msg = getattr(update, "message", None)
if update_msg is None:
return
chat_id = update_msg.chat_id
user_id = update_msg.from_user.id
chat_type = update_msg.chat.type
lang = get_update_user_lang(update_msg.from_user)
# Check and deny usage in private chat
if chat_type == "private":
if user_id not in connections:
tlg_send_msg_type_chat(bot, chat_type, chat_id,
TEXT[lang]["CMD_NEEDS_CONNECTION"])
return
group_id = connections[user_id]["group_id"]
else:
# Remove command message automatically after a while
tlg_msg_to_selfdestruct(update_msg)
# Ignore if not requested by a group Admin
is_admin = tlg_user_is_admin(bot, user_id, chat_id)
if (is_admin is None) or (is_admin == False):
return
# Get Group Chat ID and configured language
group_id = chat_id
lang = get_chat_config(group_id, "Language")
# Check if no argument was provided with the command
if len(args) == 0:
tlg_send_msg_type_chat(bot, chat_type, chat_id,
TEXT[lang]["WELCOME_MSG_SET_NOT_ARG"])
return
# Get welcome message in markdown and remove "/Welcome_msg " text from it
welcome_msg = update.message.text_markdown_v2[14:]
welcome_msg = welcome_msg.replace("{", "{{")
welcome_msg = welcome_msg.replace("}", "}}")
welcome_msg = welcome_msg.replace("$user", "{0}")
welcome_msg = welcome_msg[:CONST["MAX_WELCOME_MSG_LENGTH"]]
if welcome_msg == "disable":
welcome_msg = '-'
bot_msg = TEXT[lang]["WELCOME_MSG_UNSET"]
else:
bot_msg = TEXT[lang]["WELCOME_MSG_SET"]
save_config_property(group_id, "Welcome_Msg", welcome_msg)
tlg_send_msg_type_chat(bot, chat_type, chat_id, bot_msg)
def cmd_welcome_msg_time(update: Update, context: CallbackContext):
'''Command /welcome_msg_time message handler'''
bot = context.bot
args = context.args
# Ignore command if it was a edited message
update_msg = getattr(update, "message", None)
if update_msg is None:
return
chat_id = update_msg.chat_id
user_id = update_msg.from_user.id
chat_type = update_msg.chat.type
lang = get_update_user_lang(update_msg.from_user)
# Check and deny usage in private chat
if chat_type == "private":
if user_id not in connections:
tlg_send_msg_type_chat(bot, chat_type, chat_id,
TEXT[lang]["CMD_NEEDS_CONNECTION"])
return
group_id = connections[user_id]["group_id"]
else:
# Remove command message automatically after a while
tlg_msg_to_selfdestruct(update_msg)
# Ignore if not requested by a group Admin
is_admin = tlg_user_is_admin(bot, user_id, chat_id)
if (is_admin is None) or (is_admin == False):
return
# Get Group Chat ID and configured language
group_id = chat_id
lang = get_chat_config(group_id, "Language")
# Check if no argument was provided with the command
if len(args) == 0:
tlg_send_msg_type_chat(bot, chat_type, chat_id,
TEXT[lang]["WELCOME_TIME_NOT_ARG"])
return
# Check if provided time argument is not a number
if not is_int(args[0]):
tlg_send_msg_type_chat(bot, chat_type, chat_id,
TEXT[lang]["TIME_NOT_NUM"])
return
# Get time arguments
new_time = int(args[0])
min_sec = "min"
if len(args) > 1:
min_sec = args[1].lower()
# Check if time format is not minutes or seconds
# Convert time value to seconds if min format
if min_sec in ["m", "min", "mins", "minutes"]:
min_sec = "min"
new_time_str = "{} min".format(new_time)
new_time = new_time * CONST["T_SECONDS_IN_MIN"]
elif min_sec in ["s", "sec", "secs", "seconds"]:
min_sec = "sec"
new_time_str = "{} sec".format(new_time)
else:
tlg_send_msg_type_chat(bot, chat_type, chat_id,
TEXT[lang]["WELCOME_TIME_NOT_ARG"])
return
# Check if time value is out of limits
if new_time < 10: # Lees than 10s
msg_text = TEXT[lang]["TIME_OUT_RANGE"].format( \
CONST["MAX_CONFIG_CAPTCHA_TIME"])
tlg_send_msg_type_chat(bot, chat_type, chat_id, msg_text)
return
if new_time > CONST["MAX_CONFIG_CAPTCHA_TIME"] * CONST["T_SECONDS_IN_MIN"]:
msg_text = TEXT[lang]["TIME_OUT_RANGE"].format( \
CONST["MAX_CONFIG_CAPTCHA_TIME"])
tlg_send_msg_type_chat(bot, chat_type, chat_id, msg_text)
return
# Set the new captcha time
save_config_property(group_id, "Welcome_Time", new_time)
msg_text = TEXT[lang]["WELCOME_TIME_CHANGE"].format(new_time_str)
tlg_send_msg_type_chat(bot, chat_type, chat_id, msg_text)
def cmd_captcha_poll(update: Update, context: CallbackContext):
'''Command /captcha_poll message handler'''
bot = context.bot
args = context.args
# Ignore command if it was a edited message
update_msg = getattr(update, "message", None)
if update_msg is None:
return
chat_id = update_msg.chat_id
user_id = update_msg.from_user.id
chat_type = update_msg.chat.type
lang = get_update_user_lang(update_msg.from_user)
# Check and deny usage in private chat
if chat_type == "private":
if user_id not in connections:
tlg_send_msg_type_chat(bot, chat_type, chat_id,
TEXT[lang]["CMD_NEEDS_CONNECTION"])
return
group_id = connections[user_id]["group_id"]
else:
# Remove command message automatically after a while
tlg_msg_to_selfdestruct(update_msg)
# Ignore if not requested by a group Admin
is_admin = tlg_user_is_admin(bot, user_id, chat_id)
if (is_admin is None) or (is_admin == False):
return
# Get Group Chat ID and configured language
group_id = chat_id
lang = get_chat_config(group_id, "Language")
# Format command usage text
text_cmd_usage = TEXT[lang]["CAPTCHA_POLL_USAGE"].format(
CONST["MAX_POLL_QUESTION_LENGTH"],
CONST["MAX_POLL_OPTION_LENGTH"],
CONST["MAX_POLL_OPTIONS"])
# Check if no argument was provided with the command
if len(args) < 2:
tlg_send_msg_type_chat(bot, chat_type, chat_id, text_cmd_usage)
return
# Get poll message command
poll_cmd = args[0]
print("poll_cmd: {}".format(poll_cmd))
if poll_cmd not in ["question", "option", "correct_option"]:
tlg_send_msg_type_chat(bot, chat_type, chat_id, text_cmd_usage)
return
if poll_cmd == "question":
# get Poll Question
poll_question = " ".join(args[1:])
print("poll_question: {}".format(poll_question))
if len(poll_question) > CONST["MAX_POLL_QUESTION_LENGTH"]:
poll_question = poll_question[:CONST["MAX_POLL_QUESTION_LENGTH"]]
# Save Poll Question
save_config_property(group_id, "Poll_Q", poll_question)
tlg_send_msg_type_chat(bot, chat_type, chat_id,
TEXT[lang]["POLL_QUESTION_CONFIGURED"])
elif poll_cmd == "correct_option":
# get Poll correct option and check if is a number
option_num = args[1]
if not is_int(option_num):
tlg_send_msg_type_chat(bot, chat_type, chat_id, text_cmd_usage)
return
option_num = int(option_num)
# Check if correct option number is configured
if (option_num < 1) or (option_num > CONST["MAX_POLL_OPTIONS"]):
tlg_send_msg_type_chat(bot, chat_type, chat_id, text_cmd_usage)
return
poll_options = get_chat_config(group_id, "Poll_A")
if option_num > num_config_poll_options(poll_options):
tlg_send_msg_type_chat(bot, chat_type, chat_id,
TEXT[lang]["POLL_CORRECT_OPTION_NOT_CONFIGURED"].format(
option_num))
return
# Save Poll correct option number
save_config_property(group_id, "Poll_C_A", option_num)
tlg_send_msg_type_chat(bot, chat_type, chat_id,
TEXT[lang]["POLL_CORRECT_OPTION_CONFIGURED"].format(
option_num))
elif poll_cmd == "option":
# Check if option argument is valid
if len(args) < 3:
tlg_send_msg_type_chat(bot, chat_type, chat_id, text_cmd_usage)
return
option_num = args[1]
print("option_num: {}".format(option_num))
if not is_int(option_num):
tlg_send_msg_type_chat(bot, chat_type, chat_id, text_cmd_usage)
return
option_num = int(option_num)
if (option_num < 1) or (option_num > CONST["MAX_POLL_OPTIONS"]):
tlg_send_msg_type_chat(bot, chat_type, chat_id, text_cmd_usage)
return
option_num = option_num - 1
# Resize poll options list if missing options slots
poll_options = get_chat_config(group_id, "Poll_A")
if len(poll_options) < CONST["MAX_POLL_OPTIONS"]:
missing_options = CONST["MAX_POLL_OPTIONS"] - len(poll_options)
for _ in range(missing_options, CONST["MAX_POLL_OPTIONS"]):
poll_options.append("")
# Modify option number if previous option is not defined
for i in range(0, CONST["MAX_POLL_OPTIONS"]):
if (poll_options[i] == "") and (i < option_num):
option_num = i
break
# Parse provided Poll option text to configure and limit length
poll_option = " ".join(args[2:])
print("poll_option: {}".format(poll_option))
if len(poll_option) > CONST["MAX_POLL_OPTION_LENGTH"]:
poll_option = poll_option[:CONST["MAX_POLL_OPTION_LENGTH"]]
# Check if requested an option remove and remove it
if poll_option.lower() == "remove":
del poll_options[option_num]
poll_options.append("")
else:
poll_options[option_num] = poll_option
# Save Poll option
save_config_property(group_id, "Poll_A", poll_options)
tlg_send_msg_type_chat(bot, chat_type, chat_id,
TEXT[lang]["POLL_OPTION_CONFIGURED"].format(option_num+1))
def cmd_restrict_non_text(update: Update, context: CallbackContext):
'''Command /restrict_non_text message handler'''
bot = context.bot
args = context.args
# Ignore command if it was a edited message
update_msg = getattr(update, "message", None)
if update_msg is None:
return
chat_id = update_msg.chat_id
user_id = update_msg.from_user.id
chat_type = update_msg.chat.type
lang = get_update_user_lang(update_msg.from_user)
# Check and deny usage in private chat
if chat_type == "private":
if user_id not in connections:
tlg_send_msg_type_chat(bot, chat_type, chat_id,
TEXT[lang]["CMD_NEEDS_CONNECTION"])
return
group_id = connections[user_id]["group_id"]
else:
# Remove command message automatically after a while
tlg_msg_to_selfdestruct(update_msg)
# Ignore if not requested by a group Admin
is_admin = tlg_user_is_admin(bot, user_id, chat_id)
if (is_admin is None) or (is_admin == False):
return
# Get Group Chat ID and configured language
group_id = chat_id
lang = get_chat_config(group_id, "Language")
# Check if no argument was provided with the command
if len(args) == 0:
tlg_send_msg_type_chat(bot, chat_type, chat_id,
TEXT[lang]["RESTRICT_NON_TEXT_MSG_NOT_ARG"])
return
# Check for valid expected argument values
restrict_non_text_msgs = args[0]
if restrict_non_text_msgs != "enable" and restrict_non_text_msgs != "disable":
tlg_send_msg_type_chat(bot, chat_type, chat_id,
TEXT[lang]["RESTRICT_NON_TEXT_MSG_NOT_ARG"])
return
# Check for forever restriction argument
restrict_forever = False
if (len(args) > 1) and (args[1] == "forever"):
restrict_forever = True
# Enable/Disable just text messages option
if restrict_non_text_msgs == "enable":
if restrict_forever:
save_config_property(group_id, "Restrict_Non_Text", 2)
else:
save_config_property(group_id, "Restrict_Non_Text", 1)
bot_msg = TEXT[lang]["RESTRICT_NON_TEXT_MSG_ENABLED"]
else:
save_config_property(group_id, "Restrict_Non_Text", 0)
bot_msg = TEXT[lang]["RESTRICT_NON_TEXT_MSG_DISABLED"]
tlg_send_msg_type_chat(bot, chat_type, chat_id, bot_msg)
def cmd_add_ignore(update: Update, context: CallbackContext):
'''Command /add_ignore message handler'''
bot = context.bot
args = context.args
# Ignore command if it was a edited message
update_msg = getattr(update, "message", None)
if update_msg is None:
return
chat_id = update_msg.chat_id
user_id = update_msg.from_user.id
chat_type = update_msg.chat.type
lang = get_update_user_lang(update_msg.from_user)
# Check and deny usage in private chat
if chat_type == "private":
if user_id not in connections:
tlg_send_msg_type_chat(bot, chat_type, chat_id,
TEXT[lang]["CMD_NEEDS_CONNECTION"])
return
group_id = connections[user_id]["group_id"]
else:
# Remove command message automatically after a while
tlg_msg_to_selfdestruct(update_msg)
# Ignore if not requested by a group Admin
is_admin = tlg_user_is_admin(bot, user_id, chat_id)
if (is_admin is None) or (is_admin == False):
return
# Get Group Chat ID and configured language
group_id = chat_id
lang = get_chat_config(group_id, "Language")
# Check if no argument was provided with the command
if len(args) == 0:
tlg_send_msg_type_chat(bot, chat_type, chat_id,
TEXT[lang]["IGNORE_LIST_ADD_NOT_ARG"])
return
# Check and add user ID/alias form ignore list
user_id_alias = args[0]
if tlg_is_valid_user_id_or_alias(user_id_alias):
ignore_list = get_chat_config(group_id, "Ignore_List")
# Ignore list limit enforcement
if len(ignore_list) < CONST["IGNORE_LIST_MAX"]:
if user_id_alias not in ignore_list:
ignore_list.append(user_id_alias)
save_config_property(group_id, "Ignore_List", ignore_list)
bot_msg = TEXT[lang]["IGNORE_LIST_ADD_SUCCESS"]
else:
bot_msg = TEXT[lang]["IGNORE_LIST_ADD_DUPLICATED"]
else:
bot_msg = TEXT[lang]["IGNORE_LIST_ADD_LIMIT_EXCEEDED"]
else:
bot_msg = TEXT[lang]["IGNORE_LIST_ADD_INVALID"]
tlg_send_msg_type_chat(bot, chat_type, chat_id, bot_msg)
def cmd_remove_ignore(update: Update, context: CallbackContext):
'''Command /remove_ignore message handler'''
bot = context.bot
args = context.args
# Ignore command if it was a edited message
update_msg = getattr(update, "message", None)
if update_msg is None:
return
chat_id = update_msg.chat_id
user_id = update_msg.from_user.id
chat_type = update_msg.chat.type
lang = get_update_user_lang(update_msg.from_user)
# Check and deny usage in private chat
if chat_type == "private":
if user_id not in connections:
tlg_send_msg_type_chat(bot, chat_type, chat_id,
TEXT[lang]["CMD_NEEDS_CONNECTION"])
return
group_id = connections[user_id]["group_id"]
else:
# Remove command message automatically after a while
tlg_msg_to_selfdestruct(update_msg)
# Ignore if not requested by a group Admin
is_admin = tlg_user_is_admin(bot, user_id, chat_id)
if (is_admin is None) or (is_admin == False):
return
# Get Group Chat ID and configured language
group_id = chat_id
lang = get_chat_config(group_id, "Language")
# Check if no argument was provided with the command
if len(args) == 0:
tlg_send_msg_type_chat(bot, chat_type, chat_id,
TEXT[lang]["IGNORE_LIST_REMOVE_NOT_ARG"])
return
# Check and remove user ID/alias form ignore list
ignore_list = get_chat_config(group_id, "Ignore_List")
user_id_alias = args[0]
if list_remove_element(ignore_list, user_id_alias):
save_config_property(group_id, "Ignore_List", ignore_list)
bot_msg = TEXT[lang]["IGNORE_LIST_REMOVE_SUCCESS"]
else:
bot_msg = TEXT[lang]["IGNORE_LIST_REMOVE_NOT_IN_LIST"]
tlg_send_msg_type_chat(bot, chat_type, chat_id, bot_msg)
def cmd_ignore_list(update: Update, context: CallbackContext):
'''Command /ignore_list message handler'''
bot = context.bot
# Ignore command if it was a edited message
update_msg = getattr(update, "message", None)
if update_msg is None:
return
chat_id = update_msg.chat_id
user_id = update_msg.from_user.id
chat_type = update_msg.chat.type
lang = get_update_user_lang(update_msg.from_user)
# Check and deny usage in private chat
if chat_type == "private":
if user_id not in connections:
tlg_send_msg_type_chat(bot, chat_type, chat_id,
TEXT[lang]["CMD_NEEDS_CONNECTION"])
return
group_id = connections[user_id]["group_id"]
else:
# Remove command message automatically after a while
tlg_msg_to_selfdestruct(update_msg)
# Ignore if not requested by a group Admin
is_admin = tlg_user_is_admin(bot, user_id, chat_id)
if (is_admin is None) or (is_admin == False):
return
# Get Group Chat ID and configured language
group_id = chat_id
lang = get_chat_config(group_id, "Language")
# Get and show Ignore list
ignore_list = get_chat_config(group_id, "Ignore_List")
if not ignore_list:
bot_msg = TEXT[lang]["IGNORE_LIST_EMPTY"]
else:
bot_msg = "\n".join([str(x) for x in ignore_list])
tlg_send_msg_type_chat(bot, chat_type, chat_id, bot_msg)
def cmd_remove_solve_kick_msg(update: Update, context: CallbackContext):
'''Command /remove_solve_kick_msg message handler'''
bot = context.bot
args = context.args
# Ignore command if it was a edited message
update_msg = getattr(update, "message", None)
if update_msg is None:
return
chat_id = update_msg.chat_id
user_id = update_msg.from_user.id
chat_type = update_msg.chat.type
lang = get_update_user_lang(update_msg.from_user)
# Check and deny usage in private chat
if chat_type == "private":
if user_id not in connections:
tlg_send_msg_type_chat(bot, chat_type, chat_id,
TEXT[lang]["CMD_NEEDS_CONNECTION"])
return
group_id = connections[user_id]["group_id"]
else:
# Remove command message automatically after a while
tlg_msg_to_selfdestruct(update_msg)
# Ignore if not requested by a group Admin
is_admin = tlg_user_is_admin(bot, user_id, chat_id)
if (is_admin is None) or (is_admin == False):
return
# Get Group Chat ID and configured language
group_id = chat_id
lang = get_chat_config(group_id, "Language")
# Check if no argument was provided with the command
if len(args) == 0:
tlg_send_msg_type_chat(bot, chat_type, chat_id,
TEXT[lang]["RM_SOLVE_KICK_MSG"])
return
# Get remove solve/kick messages config to set
yes_or_no = args[0].lower()
if yes_or_no == "yes":
save_config_property(group_id, "Rm_Result_Msg", True)
bot_msg = TEXT[lang]["RM_SOLVE_KICK_MSG_YES"]
elif yes_or_no == "no":
save_config_property(group_id, "Rm_Result_Msg", False)
bot_msg = TEXT[lang]["RM_SOLVE_KICK_MSG_NO"]
else:
bot_msg = TEXT[lang]["RM_SOLVE_KICK_MSG"]
tlg_send_msg_type_chat(bot, chat_type, chat_id, bot_msg)
def cmd_remove_welcome_msg(update: Update, context: CallbackContext):
'''Command /remove_welcome_msg message handler'''
bot = context.bot
args = context.args
# Ignore command if it was a edited message
update_msg = getattr(update, "message", None)
if update_msg is None:
return
chat_id = update_msg.chat_id
user_id = update_msg.from_user.id
chat_type = update_msg.chat.type
lang = get_update_user_lang(update_msg.from_user)
# Check and deny usage in private chat
if chat_type == "private":
if user_id not in connections:
tlg_send_msg_type_chat(bot, chat_type, chat_id,
TEXT[lang]["CMD_NEEDS_CONNECTION"])
return
group_id = connections[user_id]["group_id"]
else:
# Remove command message automatically after a while
tlg_msg_to_selfdestruct(update_msg)
# Ignore if not requested by a group Admin
is_admin = tlg_user_is_admin(bot, user_id, chat_id)
if (is_admin is None) or (is_admin == False):
return
# Get Group Chat ID and configured language
group_id = chat_id
lang = get_chat_config(group_id, "Language")
# Check if no argument was provided with the command
if len(args) == 0:
tlg_send_msg_type_chat(bot, chat_type, chat_id,
TEXT[lang]["RM_WELCOME_MSG"])
return
# Get remove welcome messages config to set
yes_or_no = args[0].lower()
if yes_or_no == "yes":
save_config_property(group_id, "Rm_Welcome_Msg", True)
bot_msg = TEXT[lang]["RM_WELCOME_MSG_YES"]
elif yes_or_no == "no":
save_config_property(group_id, "Rm_Welcome_Msg", False)
bot_msg = TEXT[lang]["RM_WELCOME_MSG_NO"]
else:
bot_msg = TEXT[lang]["RM_WELCOME_MSG"]
tlg_send_msg_type_chat(bot, chat_type, chat_id, bot_msg)
def cmd_remove_all_msg_kick_on(update: Update, context: CallbackContext):
'''Command /remove_all_msg_kick_on message handler'''
bot = context.bot
# Ignore command if it was a edited message
update_msg = getattr(update, "message", None)
if update_msg is None:
return
chat_id = update_msg.chat_id
user_id = update_msg.from_user.id
chat_type = update_msg.chat.type
lang = get_update_user_lang(update_msg.from_user)
# Check and deny usage in private chat
if chat_type == "private":
if user_id not in connections:
tlg_send_msg_type_chat(bot, chat_type, chat_id,
TEXT[lang]["CMD_NEEDS_CONNECTION"])
return
group_id = connections[user_id]["group_id"]
else:
# Remove command message automatically after a while
tlg_msg_to_selfdestruct(update_msg)
# Ignore if not requested by a group Admin
is_admin = tlg_user_is_admin(bot, user_id, chat_id)
if (is_admin is None) or (is_admin == False):
return
# Get Group Chat ID and configured language
group_id = chat_id
lang = get_chat_config(group_id, "Language")
# Check and enable users send URLs in the chat
enable = get_chat_config(group_id, "RM_All_Msg")
if enable:
bot_msg = TEXT[lang]["CONFIG_ALREADY_SET"]
else:
enable = True
save_config_property(group_id, "RM_All_Msg", enable)
bot_msg = TEXT[lang]["RM_ALL_MSGS_AFTER_KICK_ON"]
tlg_send_msg_type_chat(bot, chat_type, chat_id, bot_msg)
def cmd_remove_all_msg_kick_off(update: Update, context: CallbackContext):
'''Command /remove_all_msg_kick_off message handler'''
bot = context.bot
# Ignore command if it was a edited message
update_msg = getattr(update, "message", None)
if update_msg is None:
return
chat_id = update_msg.chat_id
user_id = update_msg.from_user.id
chat_type = update_msg.chat.type
lang = get_update_user_lang(update_msg.from_user)
# Check and deny usage in private chat
if chat_type == "private":
if user_id not in connections:
tlg_send_msg_type_chat(bot, chat_type, chat_id,
TEXT[lang]["CMD_NEEDS_CONNECTION"])
return
group_id = connections[user_id]["group_id"]
else:
# Remove command message automatically after a while
tlg_msg_to_selfdestruct(update_msg)
# Ignore if not requested by a group Admin
is_admin = tlg_user_is_admin(bot, user_id, chat_id)
if (is_admin is None) or (is_admin == False):
return
# Get Group Chat ID and configured language
group_id = chat_id
lang = get_chat_config(group_id, "Language")
# Check and enable users send URLs in the chat
enable = get_chat_config(group_id, "RM_All_Msg")
if not enable:
bot_msg = TEXT[lang]["CONFIG_ALREADY_UNSET"]
else:
enable = False
save_config_property(group_id, "RM_All_Msg", enable)
bot_msg = TEXT[lang]["RM_ALL_MSGS_AFTER_KICK_OFF"]
tlg_send_msg_type_chat(bot, chat_type, chat_id, bot_msg)
def cmd_url_enable(update: Update, context: CallbackContext):
'''Command /url_enable message handler'''
bot = context.bot
# Ignore command if it was a edited message
update_msg = getattr(update, "message", None)
if update_msg is None:
return
chat_id = update_msg.chat_id
user_id = update_msg.from_user.id
chat_type = update_msg.chat.type
lang = get_update_user_lang(update_msg.from_user)
# Check and deny usage in private chat
if chat_type == "private":
if user_id not in connections:
tlg_send_msg_type_chat(bot, chat_type, chat_id,
TEXT[lang]["CMD_NEEDS_CONNECTION"])
return
group_id = connections[user_id]["group_id"]
else:
# Remove command message automatically after a while
tlg_msg_to_selfdestruct(update_msg)
# Ignore if not requested by a group Admin
is_admin = tlg_user_is_admin(bot, user_id, chat_id)
if (is_admin is None) or (is_admin == False):
return
# Get Group Chat ID and configured language
group_id = chat_id
lang = get_chat_config(group_id, "Language")
# Check and enable users send URLs in the chat
enable = get_chat_config(group_id, "URL_Enabled")
if enable:
bot_msg = TEXT[lang]["CONFIG_ALREADY_SET"]
else:
enable = True
save_config_property(group_id, "URL_Enabled", enable)
bot_msg = TEXT[lang]["URL_ENABLE"]
tlg_send_msg_type_chat(bot, chat_type, chat_id, bot_msg)
def cmd_url_disable(update: Update, context: CallbackContext):
'''Command /url_disable message handler'''
bot = context.bot
# Ignore command if it was a edited message
update_msg = getattr(update, "message", None)
if update_msg is None:
return
chat_id = update_msg.chat_id
user_id = update_msg.from_user.id
chat_type = update_msg.chat.type
lang = get_update_user_lang(update_msg.from_user)
# Check and deny usage in private chat
if chat_type == "private":
if user_id not in connections:
tlg_send_msg_type_chat(bot, chat_type, chat_id,
TEXT[lang]["CMD_NEEDS_CONNECTION"])
return
group_id = connections[user_id]["group_id"]
else:
# Remove command message automatically after a while
tlg_msg_to_selfdestruct(update_msg)
# Ignore if not requested by a group Admin
is_admin = tlg_user_is_admin(bot, user_id, chat_id)
if (is_admin is None) or (is_admin == False):
return
# Get Group Chat ID and configured language
group_id = chat_id
lang = get_chat_config(group_id, "Language")
# Check and disable users send URLs in the chat
enable = get_chat_config(group_id, "URL_Enabled")
if not enable:
bot_msg = TEXT[lang]["CONFIG_ALREADY_UNSET"]
else:
enable = False
save_config_property(group_id, "URL_Enabled", enable)
bot_msg = TEXT[lang]["URL_DISABLE"]
tlg_send_msg_type_chat(bot, chat_type, chat_id, bot_msg)
def cmd_enable(update: Update, context: CallbackContext):
'''Command /enable message handler'''
bot = context.bot
# Ignore command if it was a edited message
update_msg = getattr(update, "message", None)
if update_msg is None:
return
chat_id = update_msg.chat_id
user_id = update_msg.from_user.id
chat_type = update_msg.chat.type
lang = get_update_user_lang(update_msg.from_user)
# Check and deny usage in private chat
if chat_type == "private":
tlg_send_msg(bot, chat_id, TEXT[lang]["CMD_NOT_ALLOW_PRIVATE"])
return
# Remove command message automatically after a while
tlg_msg_to_selfdestruct(update_msg)
# Ignore if not requested by a group Admin
is_admin = tlg_user_is_admin(bot, user_id, chat_id)
if (is_admin is None) or (is_admin == False):
return
# Get actual chat configured language
lang = get_chat_config(chat_id, "Language")
# Check and enable captcha protection in the chat
enable = get_chat_config(chat_id, "Enabled")
if enable:
bot_msg = TEXT[lang]["ALREADY_ENABLE"]
else:
enable = True
save_config_property(chat_id, "Enabled", enable)
bot_msg = TEXT[lang]["ENABLE"]
tlg_send_selfdestruct_msg(bot, chat_id, bot_msg)
def cmd_disable(update: Update, context: CallbackContext):
'''Command /disable message handler'''
bot = context.bot
# Ignore command if it was a edited message
update_msg = getattr(update, "message", None)
if update_msg is None:
return
chat_id = update_msg.chat_id
user_id = update_msg.from_user.id
chat_type = update_msg.chat.type
lang = get_update_user_lang(update_msg.from_user)
# Check and deny usage in private chat
if chat_type == "private":
tlg_send_msg(bot, chat_id, TEXT[lang]["CMD_NOT_ALLOW_PRIVATE"])
return
# Remove command message automatically after a while
tlg_msg_to_selfdestruct(update_msg)
# Ignore if not requested by a group Admin
is_admin = tlg_user_is_admin(bot, user_id, chat_id)
if (is_admin is None) or (is_admin == False):
return
# Get actual chat configured language
lang = get_chat_config(chat_id, "Language")
# Check and disable captcha protection in the chat
enable = get_chat_config(chat_id, "Enabled")
if not enable:
bot_msg = TEXT[lang]["ALREADY_DISABLE"]
else:
enable = False
save_config_property(chat_id, "Enabled", enable)
bot_msg = TEXT[lang]["DISABLE"]
tlg_send_selfdestruct_msg(bot, chat_id, bot_msg)
def cmd_chatid(update: Update, context: CallbackContext):
'''Command /chatid message handler'''
bot = context.bot
# Ignore command if it was a edited message
update_msg = getattr(update, "message", None)
if update_msg is None:
return
chat_id = update_msg.chat_id
chat_type = update_msg.chat.type
if chat_type == "private":
msg_text = "Your Chat ID:\n—————————\n{}".format(chat_id)
tlg_send_msg(bot, chat_id, msg_text)
else:
msg_text = "Group Chat ID:\n—————————\n{}".format(chat_id)
tlg_msg_to_selfdestruct(update_msg)
tlg_send_selfdestruct_msg(bot, chat_id, msg_text)
def cmd_version(update: Update, context: CallbackContext):
'''Command /version message handler'''
bot = context.bot
# Ignore command if it was a edited message
update_msg = getattr(update, "message", None)
if update_msg is None:
return
chat_id = update_msg.chat_id
chat_type = update_msg.chat.type
user_id = update_msg.from_user.id
lang = get_update_user_lang(update_msg.from_user)
if chat_type == "private":
msg_text = TEXT[lang]["VERSION"].format(CONST["VERSION"])
tlg_send_msg(bot, chat_id, msg_text)
else:
# Remove command message automatically after a while
tlg_msg_to_selfdestruct(update_msg)
# Ignore if not requested by a group Admin
is_admin = tlg_user_is_admin(bot, user_id, chat_id)
if (is_admin is None) or (is_admin == False):
return
# Send the message
lang = get_chat_config(chat_id, "Language")
msg_text = TEXT[lang]["VERSION"].format(CONST["VERSION"])
tlg_send_selfdestruct_msg(bot, chat_id, msg_text)
def cmd_about(update: Update, context: CallbackContext):
'''Command /about handler'''
bot = context.bot
# Ignore command if it was a edited message
update_msg = getattr(update, "message", None)
if update_msg is None:
return
chat_id = update_msg.chat_id
chat_type = update_msg.chat.type
lang = get_update_user_lang(update_msg.from_user)
if chat_type != "private":
lang = get_chat_config(chat_id, "Language")
msg_text = TEXT[lang]["ABOUT_MSG"].format(CONST["DEVELOPER"],
CONST["REPOSITORY"], CONST["DEV_DONATION_ADDR"])
tlg_send_msg(bot, chat_id, msg_text)
def cmd_captcha(update: Update, context: CallbackContext):
'''Command /captcha message handler. Useful to test.
Just Bot Owner can use it.'''
bot = context.bot
# Ignore command if it was a edited message
update_msg = getattr(update, "message", None)
if update_msg is None:
return
chat_id = update_msg.chat_id
user = update_msg.from_user
user_id = user.id
user_alias = ""
if user.username is not None:
user_alias = "@{}".format(user.username)
# Remove command message automatically after a while
tlg_msg_to_selfdestruct(update_msg)
# Check if command was execute by Bot owner
if (str(user_id) != CONST["BOT_OWNER"]) and \
(user_alias != CONST["BOT_OWNER"]):
tlg_send_selfdestruct_msg(bot, chat_id, CONST["CMD_JUST_ALLOW_OWNER"])
return
# Generate a random difficulty captcha
difficulty = randint(1, 5)
captcha_mode = choice(["nums", "hex", "ascii", "math"])
captcha = create_image_captcha(chat_id, user_id, difficulty, captcha_mode)
if captcha_mode == "math":
captcha_code = "{} = {}".format(captcha["equation_str"], \
captcha["equation_result"])
else:
captcha_code = captcha["characters"]
printts("[{}] Sending captcha msg: {}".format(chat_id, captcha_code))
# Note: Img caption must be <= 1024 chars
img_caption = "Captcha Level: {}\nCaptcha Mode: {}\n" \
"Captcha Code: {}".format(difficulty, captcha_mode, captcha_code)
tlg_send_image(bot, chat_id, open(captcha["image"],"rb"), img_caption)
# Remove sent captcha image file from file system
if path.exists(captcha["image"]):
remove(captcha["image"])
def cmd_allowuserlist(update: Update, context: CallbackContext):
'''Command /allowuserlist message handler. To Global allowed list blind users.
Just Bot Owner can use it.'''
bot = context.bot
args = context.args
# Ignore command if it was a edited message
update_msg = getattr(update, "message", None)
if update_msg is None:
return
chat_id = update_msg.chat_id
user = update_msg.from_user
user_id = user.id
user_alias = ""
if user.username is not None:
user_alias = "@{}".format(user.username)
lang = get_update_user_lang(update_msg.from_user)
# Check if command was execute by Bot owner
if (str(user_id) != CONST["BOT_OWNER"]) and (user_alias != CONST["BOT_OWNER"]):
tlg_send_selfdestruct_msg(bot, chat_id, CONST["CMD_JUST_ALLOW_OWNER"])
return
# Check if no argument was provided with the command
if len(args) == 0:
# Show Actual Global allowed list Users
l_white_users = file_read(CONST["F_ALLOWED_USERS"])
bot_msg = "\n".join([str(user) for user in l_white_users])
bot_msg = "Global Allowed Users List:\n--------------------\n{}".format(bot_msg)
tlg_send_msg(bot, chat_id, bot_msg)
tlg_send_msg(bot, chat_id, CONST["ALLOWUSERLIST_USAGE"])
return
else:
if len(args) <= 1:
tlg_send_msg(bot, chat_id, CONST["ALLOWUSERLIST_USAGE"])
return
if (args[0] != "add") and (args[0] != "rm"):
tlg_send_msg(bot, chat_id, CONST["ALLOWUSERLIST_USAGE"])
return
add_rm = args[0]
user = args[1]
l_white_users = file_read(CONST["F_ALLOWED_USERS"])
if add_rm == "add":
if not tlg_is_valid_user_id_or_alias(user):
tlg_send_msg(bot, chat_id, "Invalid User ID/Alias.")
return
if user not in l_white_users:
file_write(CONST["F_ALLOWED_USERS"], "{}\n".format(user))
tlg_send_msg(bot, chat_id, "User added to Global allowed list.")
else:
tlg_send_msg(bot, chat_id, "The User is already in Global allowed list.")
return
if add_rm == "rm":
if not tlg_is_valid_user_id_or_alias(user):
tlg_send_msg(bot, chat_id, "Invalid User ID/Alias.")
return
if list_remove_element(l_white_users, user):
file_write(CONST["F_ALLOWED_USERS"], l_white_users, "w")
tlg_send_msg(bot, chat_id, "User removed from Global allowed list.")
else:
tlg_send_msg(bot, chat_id, "The User is not in Global allowed list.")
def cmd_allowgroup(update: Update, context: CallbackContext):
'''Command /allowgroup message handler. To allow Bot usage in groups when Bot is private.
Just Bot Owner can use it.'''
bot = context.bot
args = context.args
# Ignore command if it was a edited message
update_msg = getattr(update, "message", None)
if update_msg is None:
return
chat_id = update_msg.chat_id
user = update_msg.from_user
user_id = user.id
user_alias = ""
if user.username is not None:
user_alias = "@{}".format(user.username)
lang = get_update_user_lang(update_msg.from_user)
# Check if command was execute by Bot owner
if (str(user_id) != CONST["BOT_OWNER"]) and (user_alias != CONST["BOT_OWNER"]):
tlg_send_selfdestruct_msg(bot, chat_id, CONST["CMD_JUST_ALLOW_OWNER"])
return
# Check if no argument was provided with the command
if len(args) == 0:
# Show Actual Allowed Groups
l_allowed_groups = file_read(CONST["F_ALLOWED_GROUPS"])
bot_msg = "\n".join([str(group) for group in l_allowed_groups])
bot_msg = "Allowed Groups:\n--------------------\n{}".format(bot_msg)
tlg_send_msg(bot, chat_id, bot_msg)
tlg_send_msg(bot, chat_id, CONST["ALLOWGROUP_USAGE"])
return
else:
if len(args) <= 1:
tlg_send_msg(bot, chat_id, CONST["ALLOWGROUP_USAGE"])
return
if (args[0] != "add") and (args[0] != "rm"):
tlg_send_msg(bot, chat_id, CONST["ALLOWGROUP_USAGE"])
return
add_rm = args[0]
group = args[1]
l_allowed_groups = file_read(CONST["F_ALLOWED_GROUPS"])
if add_rm == "add":
if not tlg_is_valid_group(group):
tlg_send_msg(bot, chat_id, "Invalid Group ID.")
return
if group not in l_allowed_groups:
file_write(CONST["F_ALLOWED_GROUPS"], "{}\n".format(group))
tlg_send_msg(bot, chat_id, "Group added to allowed list.")
else:
tlg_send_msg(bot, chat_id, "The group is already in the allowed list.")
return
if add_rm == "rm":
if not tlg_is_valid_group(group):
tlg_send_msg(bot, chat_id, "Invalid Group ID.")
return
if list_remove_element(l_allowed_groups, group):
file_write(CONST["F_ALLOWED_GROUPS"], l_allowed_groups, "w")
tlg_send_msg(bot, chat_id, "Group removed from allowed list.")
else:
tlg_send_msg(bot, chat_id, "The group is not in allowed list.")
###############################################################################
### Bot automatic remove sent messages thread
def th_selfdestruct_messages(bot):
'''Handle remove messages sent by the Bot with the timed self-delete function'''
global to_delete_in_time_messages_list
while not force_exit:
# Thread sleep for each iteration
sleep(0.01)
# Check each Bot sent message
i = 0
while i < len(to_delete_in_time_messages_list):
# Check for break iterating if script must exit
if force_exit:
return
sent_msg = to_delete_in_time_messages_list[i]
# Sleep each 100 iterations
i = i + 1
if (i > 1) and ((i % 1000) == 0):
sleep(0.01)
# Check if delete time has arrive for this message
if time() - sent_msg["time"] < sent_msg["delete_time"]:
continue
# Delete message
printts("[{}] Delete message: {}".format(
sent_msg["Chat_id"], sent_msg["Msg_id"]))
delete_result = tlg_delete_msg(bot, sent_msg["Chat_id"], sent_msg["Msg_id"])
# The bot has no privileges to delete messages
if delete_result["error"] == "Message can't be deleted":
lang = get_chat_config(sent_msg["Chat_id"], "Language")
sent_result = tlg_send_msg(bot, sent_msg["Chat_id"],
TEXT[lang]["CANT_DEL_MSG"], reply_to_message_id=sent_msg["Msg_id"])
if sent_result["msg"] is not None:
tlg_msg_to_selfdestruct(sent_result["msg"])
list_remove_element(to_delete_in_time_messages_list, sent_msg)
sleep(0.01)
###############################################################################
### Handle time to kick users thread
def th_time_to_kick_not_verify_users(bot):
'''Check if the time for ban new users that has not completed the captcha has arrived'''
global new_users
while not force_exit:
# Thread sleep for each iteration
sleep(0.01)
# Get all id from users in captcha process (shallow copy to list)
users_id = []
chats_id_list = list(new_users.keys()).copy()
for chat_id in chats_id_list:
users_id_list = list(new_users[chat_id].keys()).copy()
for user_id in users_id_list:
if user_id not in users_id:
users_id.append(user_id)
# For each user id check for time to kick in each chat
i = 0
for user_id in users_id:
for chat_id in chats_id_list:
# Sleep each 1000 iterations
i = i + 1
if i > 1000:
i = 0
sleep(0.01)
# Check for end thread when iterating if script must exit
if force_exit:
return
# Ignore if user is not in this chat
if user_id not in new_users[chat_id]:
continue
try:
user_join_time = new_users[chat_id][user_id]["join_data"]["join_time"]
captcha_timeout = new_users[chat_id][user_id]["join_data"]["captcha_timeout"]
if new_users[chat_id][user_id]["join_data"]["kicked_ban"]:
# Remove from new users list the remaining kicked users that have not solve
# the captcha in 30 mins (user ban just happen if a user try to join the group
# and fail to solve the captcha 5 times in the past 30 mins)
if time() - user_join_time < captcha_timeout + 1800:
continue
printts("Removing kicked user {} after 30 mins".format(user_id))
del new_users[chat_id][user_id]
else:
# If time for kick/ban has not arrived yet
if time() - user_join_time < captcha_timeout:
continue
user_name = new_users[chat_id][user_id]["join_data"]["user_name"]
printts("[{}] Captcha reply timed out for user {}.".format(chat_id, user_name))
captcha_fail_kick_ban_member(bot, chat_id, user_id,
CONST["MAX_FAIL_BAN"])
sleep(0.01)
except Exception as e:
printts("Error handling kick/ban:\n{}".format(str(e)))
###############################################################################
### Telegram Errors Callback
def tlg_error_callback(update, context):
'''Telegram errors handler.'''
try:
raise context.error
except Unauthorized:
printts("TLG Error: Unauthorized")
except BadRequest:
printts("TLG Error: Bad Request")
except TimedOut:
printts("TLG Error: Timeout (slow connection issue)")
except NetworkError:
printts("TLG Error: network problem")
except TelegramError as e:
printts("TLG Error: {}".format(str(e)))
###############################################################################
### Main Function
def main():
'''Main Function'''
global updater
global th_0
global th_1
# Check if Bot Token has been set or has default value
if CONST["TOKEN"] == "XXXXXXXXX:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX":
printts("Error: Bot Token has not been set.")
printts("Please add your Bot Token to settings.py file.")
printts("Exit.\n")
exit(0)
# Check if Bot owner has been set in Private Bot mode
if (CONST["BOT_OWNER"] == "XXXXXXXXX") and CONST["BOT_PRIVATE"]:
printts("Error: Bot Owner has not been set for Private Bot.")
printts("Please add the Bot Owner to settings.py file.")
printts("Exit.\n")
exit(0)
printts("Bot started.")
# Initialize resources by populating files list and configs with chats found files
initialize_resources()
printts("Resources initialized.")
restore_session()
# Set messages to be sent silently by default
msgs_defaults = Defaults(disable_notification=True)
# Create an event handler (updater) for a Bot with the given Token and get the dispatcher
updater = Updater(CONST["TOKEN"], workers=12, defaults=msgs_defaults)
dp = updater.dispatcher
# Set Telegram errors handler
dp.add_error_handler(tlg_error_callback)
# Set to dispatcher all expected commands messages handler
dp.add_handler(CommandHandler("start", cmd_start))
dp.add_handler(CommandHandler("help", cmd_help))
dp.add_handler(CommandHandler("commands", cmd_commands))
dp.add_handler(CommandHandler("checkcfg", cmd_checkcfg))
dp.add_handler(CommandHandler("connect", cmd_connect, pass_args=True))
dp.add_handler(CommandHandler("disconnect", cmd_disconnect))
dp.add_handler(CommandHandler("language", cmd_language, pass_args=True))
dp.add_handler(CommandHandler("time", cmd_time, pass_args=True))
dp.add_handler(CommandHandler("difficulty", cmd_difficulty, pass_args=True))
dp.add_handler(CommandHandler("captcha_mode", cmd_captcha_mode, pass_args=True))
dp.add_handler(CommandHandler("welcome_msg", cmd_welcome_msg, pass_args=True))
dp.add_handler(CommandHandler("welcome_msg_time", cmd_welcome_msg_time, pass_args=True))
dp.add_handler(CommandHandler("captcha_poll", cmd_captcha_poll, pass_args=True))
dp.add_handler(CommandHandler("restrict_non_text", cmd_restrict_non_text, pass_args=True))
dp.add_handler(CommandHandler("add_ignore", cmd_add_ignore, pass_args=True))
dp.add_handler(CommandHandler("remove_ignore", cmd_remove_ignore, pass_args=True))
dp.add_handler(CommandHandler("ignore_list", cmd_ignore_list))
dp.add_handler(CommandHandler("remove_solve_kick_msg", cmd_remove_solve_kick_msg, pass_args=True))
dp.add_handler(CommandHandler("remove_welcome_msg", cmd_remove_welcome_msg, pass_args=True))
dp.add_handler(CommandHandler("remove_all_msg_kick_on", cmd_remove_all_msg_kick_on))
dp.add_handler(CommandHandler("remove_all_msg_kick_off", cmd_remove_all_msg_kick_off))
dp.add_handler(CommandHandler("url_enable", cmd_url_enable))
dp.add_handler(CommandHandler("url_disable", cmd_url_disable))
dp.add_handler(CommandHandler("enable", cmd_enable))
dp.add_handler(CommandHandler("disable", cmd_disable))
dp.add_handler(CommandHandler("chatid", cmd_chatid))
dp.add_handler(CommandHandler("version", cmd_version))
dp.add_handler(CommandHandler("about", cmd_about))
if (CONST["BOT_OWNER"] != "XXXXXXXXX"):
dp.add_handler(CommandHandler("captcha", cmd_captcha))
dp.add_handler(CommandHandler("allowuserlist", cmd_allowuserlist, pass_args=True))
if (CONST["BOT_OWNER"] != "XXXXXXXXX") and CONST["BOT_PRIVATE"]:
dp.add_handler(CommandHandler("allowgroup", cmd_allowgroup, pass_args=True))
# Set to dispatcher a not-command text messages handler
dp.add_handler(MessageHandler(Filters.text, msg_nocmd, run_async=True))
# Set to dispatcher not text messages handler
dp.add_handler(MessageHandler(Filters.photo | Filters.audio | Filters.voice |
Filters.video | Filters.sticker | Filters.document | Filters.location |
Filters.contact, msg_notext))
# Set to dispatcher a new member join the group and member left the group events handlers
dp.add_handler(ChatMemberHandler(chat_bot_status_change, ChatMemberHandler.MY_CHAT_MEMBER))
dp.add_handler(ChatMemberHandler(chat_member_status_change, ChatMemberHandler.ANY_CHAT_MEMBER))
# Set to dispatcher "USER joined the group" messages event handlers
dp.add_handler(MessageHandler(Filters.status_update.new_chat_members, msg_user_joined_group))
# Set to dispatcher inline keyboard callback handler for new captcha request and
# button captcha challenge
dp.add_handler(CallbackQueryHandler(key_inline_keyboard))
# Set to dispatcher users poll vote handler
dp.add_handler(PollAnswerHandler(receive_poll_answer, run_async=True))
# Launch the Bot ignoring pending messages (clean=True) and get all updates (allowed_uptades=[])
if CONST["WEBHOOK_HOST"] == "None":
printts("Setup Bot for Polling.")
updater.start_polling(
drop_pending_updates=True,
allowed_updates=Update.ALL_TYPES
)
else:
printts("Setup Bot for Webhook.")
updater.start_webhook(
drop_pending_updates=True, listen="0.0.0.0", port=CONST["WEBHOOK_PORT"], url_path=CONST["TOKEN"],
key=CONST["WEBHOOK_CERT_PRIV_KEY"], cert=CONST["WEBHOOK_CERT"],
webhook_url="https://{}:{}/{}".format(CONST["WEBHOOK_HOST"], CONST["WEBHOOK_PORT"],
CONST["TOKEN"]), allowed_updates=Update.ALL_TYPES
)
printts("Bot setup completed. Bot is now running.")
# Launch delete mesages and kick users threads
th_0 = Thread(target=th_selfdestruct_messages, args=(updater.bot,))
th_1 = Thread(target=th_time_to_kick_not_verify_users, args=(updater.bot,))
th_0.name = "th_selfdestruct_messages"
th_1.name = "th_time_to_kick_not_verify_users"
th_0.start()
sleep(0.05)
th_1.start()
# Set main thread to idle
# Using Bot idle() catch external signals instead our signal handler
updater.idle()
print("Bot Threads end")
if os_system() == "Windows":
kill(getpid(), SIGTERM)
else:
kill(getpid(), SIGUSR1)
sleep(1)
printts("Exit 1")
exit(1)
if __name__ == "__main__":
main()