├── .gitignore
├── .template.env
├── LICENSE
├── README.md
├── bot
├── __init__.py
├── callbacks
│ ├── __init__.py
│ ├── admin.py
│ ├── commands.py
│ ├── core.py
│ ├── mailing.py
│ └── service.py
├── constants.py
├── handlers.py
├── models.py
├── tools_old.py
└── utils
│ ├── __init__.py
│ └── checkers.py
├── images
├── example_en.png
└── example_ru.png
├── main.py
├── requirements.txt
└── settings.py
/.gitignore:
--------------------------------------------------------------------------------
1 | __pycache__
2 | venv
3 | db.sqlite3
4 | .idea
5 | .DS_Store
6 | .env
7 |
--------------------------------------------------------------------------------
/.template.env:
--------------------------------------------------------------------------------
1 | DEBUG=
2 |
3 | BOT_TOKEN=
4 | ADMINS=
5 | DEVELOPER=
6 |
7 | DB_NAME=
8 | DB_USER=
9 | DB_PASSWORD=
10 | DB_HOST=
11 | DB_PORT=
12 |
13 | SQLITE_DB_PATH=
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Adrian Kalinin
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CheckNicknameBot
2 | Telegram bot that checks if an username is available in social networks.
3 |
4 | # User Usage
5 |
6 | Everything is pretty simple – just send an username to the bot; or press the button if you want to check your own.
7 |
8 | 
9 |
10 | # Admin Usage
11 |
12 | There are some features for admins of the bot. Firt off all, you should enter `/admin` command. Then you can check statistics and restart the bot.
13 |
14 | 
15 |
16 | One more great feature is the possibility to send a message to all the users.
17 |
18 | 
19 |
20 | # Deployment
21 |
22 | ### Configurate `config.py`:
23 |
24 | Create a new Telegram Bot at t.me/BotFather and get the token of your bot, then put it as `token` variable.
25 | Then you can enter for `admins` some ids of users who can use the admins' commands.
26 | Fill your `host` (server's ip) and `port` (443, 80, 88 or 8443).
27 |
28 | ### Generate quick'n'dirty SSL certificate (in terminal):
29 |
30 | `openssl genrsa -out webhook_pkey.pem 2048`
31 |
32 | `openssl req -new -x509 -days 3650 -key webhook_pkey.pem -out webhook_cert.pem`
33 |
34 | Attention! When asked for "Common Name (e.g. server FQDN or YOUR name)" you should reply with the same value as your server's ip addres.
35 |
36 | ### Create virtual environment for Python and install all requiremetns (in terminal):
37 |
38 | `virtualenv venv --python=python3`
39 |
40 | `source venv/bin/activate`
41 |
42 | `pip install -r requiremetns.txt`
43 |
44 | Just enter `python main.py` in your terminal.
45 |
--------------------------------------------------------------------------------
/bot/__init__.py:
--------------------------------------------------------------------------------
1 | # __init__.py
2 |
--------------------------------------------------------------------------------
/bot/callbacks/__init__.py:
--------------------------------------------------------------------------------
1 | from .service import error_callback
2 |
3 | from .commands import (
4 | admin_command_callback, start_command_callback
5 | )
6 |
7 | from .admin import (
8 | statistics_callback, mailing_callback
9 | )
10 |
11 | from .mailing import (
12 | mailing_message_callback, preview_mailing_callback,
13 | cancel_mailing_callback, send_mailing_callback
14 | )
15 |
16 | from .core import (
17 | check_username_callback, check_my_username_callback,
18 | how_to_use_callback
19 | )
20 |
--------------------------------------------------------------------------------
/bot/callbacks/admin.py:
--------------------------------------------------------------------------------
1 | from telegram import Update, ParseMode
2 | from telegram.ext import CallbackContext
3 |
4 | from peewee import fn
5 |
6 | from ..models import User
7 | from ..constants import Message, States
8 |
9 |
10 | def statistics_callback(update: Update, context: CallbackContext):
11 | total_users = User.select().count()
12 | active_users = User.select().where(User.active == True).count()
13 | total_requests = User.select(fn.sum(User.requests).alias('total')).dicts()[0].get('total')
14 |
15 | response = Message.statistics.format(
16 | total_users=total_users,
17 | active_users=active_users,
18 | total_requests=total_requests
19 | )
20 |
21 | context.bot.edit_message_text(
22 | chat_id=update.effective_chat.id,
23 | message_id=update.effective_message.message_id,
24 | text=response, parse_mode=ParseMode.HTML
25 | )
26 |
27 |
28 | def mailing_callback(update: Update, context: CallbackContext):
29 | context.bot.edit_message_text(
30 | chat_id=update.effective_chat.id,
31 | message_id=update.effective_message.message_id,
32 | text=Message.mailing
33 | )
34 |
35 | return States.prepare_mailing
36 |
--------------------------------------------------------------------------------
/bot/callbacks/commands.py:
--------------------------------------------------------------------------------
1 | from telegram import Update, ParseMode
2 | from telegram.ext import CallbackContext
3 |
4 | from ..models import User
5 | from ..constants import Message, Keyboard
6 |
7 |
8 | def admin_command_callback(update: Update, context: CallbackContext):
9 | context.bot.send_message(
10 | chat_id=update.effective_chat.id,
11 | text=Message.admin, reply_markup=Keyboard.admin
12 | )
13 |
14 |
15 | def start_command_callback(update: Update, context: CallbackContext):
16 | user, created = User.get_or_create(user_id=update.effective_user.id)
17 |
18 | if not user.active:
19 | user.active = True
20 | user.save()
21 |
22 | context.bot.send_message(
23 | chat_id=update.effective_chat.id,
24 | text=Message.start, parse_mode=ParseMode.HTML,
25 | reply_markup=Keyboard.main
26 | )
27 |
--------------------------------------------------------------------------------
/bot/callbacks/core.py:
--------------------------------------------------------------------------------
1 | from telegram import Update, Bot, ParseMode
2 | from telegram.ext import CallbackContext
3 |
4 | import re
5 |
6 | from ..models import User
7 | from ..constants import Message, USERNAME_STATUSES
8 | from ..utils import checkers
9 |
10 |
11 | def __process_data(username):
12 | data = {social_media: None for social_media in checkers.keys()}
13 | statuses = {key + '_status': USERNAME_STATUSES[value]['emoji'] for key, value in data.items()}
14 |
15 | yield {**data, **statuses}
16 |
17 | for social_media in checkers.keys():
18 | result = checkers[social_media](username)
19 |
20 | if result in [False, None]:
21 | data[social_media] = USERNAME_STATUSES[result]['text']
22 | statuses[social_media + '_status'] = USERNAME_STATUSES[result]['emoji']
23 |
24 | else:
25 | data[social_media] = USERNAME_STATUSES[True]['text'].format(result)
26 | statuses[social_media + '_status'] = USERNAME_STATUSES[True]['emoji']
27 |
28 | yield {**data, **statuses}
29 |
30 |
31 | def __check_username(bot: Bot, username: str, user_id: int):
32 | checker = (__process_data(username))
33 |
34 | data = next(checker)
35 |
36 | message = bot.send_message(
37 | chat_id=user_id,
38 | text=Message.result.format(username=username, bot_username=bot.username, **data),
39 | parse_mode=ParseMode.HTML,
40 | disable_web_page_preview=True
41 | )
42 |
43 | for data in checker:
44 | message.edit_text(
45 | text=Message.result.format(username=username, bot_username=bot.username, **data),
46 | parse_mode=ParseMode.HTML,
47 | disable_web_page_preview=True
48 | )
49 |
50 |
51 | def check_username_callback(update: Update, context: CallbackContext):
52 | username = update.message.text.strip(' ')
53 |
54 | if re.match('^[A-Za-z0-9_-]*$', username) and username.lower() != 'admin':
55 | query = User.update(requests=User.requests + 1).where(User.user_id == update.effective_user.id)
56 | query.execute()
57 |
58 | __check_username(context.bot, username, update.effective_user.id)
59 |
60 | else:
61 | context.bot.send_message(
62 | chat_id=update.effective_chat.id,
63 | text=Message.invalid_username
64 | )
65 |
66 |
67 | def check_my_username_callback(update: Update, context: CallbackContext):
68 | if username := update.effective_user.username:
69 | __check_username(context.bot, username, update.effective_chat.id)
70 |
71 | else:
72 | context.bot.send_message(
73 | chat_id=update.effective_chat.id,
74 | text=Message.no_username
75 | )
76 |
77 |
78 | def how_to_use_callback(update: Update, context: CallbackContext):
79 | context.bot.send_message(
80 | chat_id=update.effective_chat.id,
81 | text=Message.how_to_use
82 | )
83 |
--------------------------------------------------------------------------------
/bot/callbacks/mailing.py:
--------------------------------------------------------------------------------
1 | from telegram.ext import CallbackContext, ConversationHandler
2 | from telegram import (
3 | Update, Message as TelegramMessage,
4 | TelegramError, Bot, PhotoSize,
5 | Animation, Video, ParseMode
6 | )
7 |
8 | from threading import Thread
9 | import logging
10 | import time
11 |
12 | from ..constants import Message, Keyboard, States
13 | from ..models import User
14 |
15 |
16 | def __send_photo(bot: Bot, message: TelegramMessage, user_id: int):
17 | photo = PhotoSize(
18 | file_id=message.photo[0].file_id,
19 | file_unique_id=message.photo[0].file_unique_id,
20 | width=message.photo[0].width,
21 | height=message.photo[0].height,
22 | file_size=message.photo[0].file_size
23 | )
24 |
25 | bot.send_photo(
26 | chat_id=user_id,
27 | photo=photo,
28 | caption=message.caption_html,
29 | parse_mode=ParseMode.HTML,
30 | reply_markup=message.reply_markup
31 | )
32 |
33 |
34 | def __send_animation(bot: Bot, message: TelegramMessage, user_id: int):
35 | animation = Animation(
36 | file_id=message.animation.file_id,
37 | file_unique_id=message.animation.file_unique_id,
38 | width=message.animation.width,
39 | height=message.animation.height,
40 | duration=message.animation.file_size
41 | )
42 |
43 | bot.send_animation(
44 | chat_id=user_id,
45 | animation=animation,
46 | caption=message.caption_html,
47 | parse_mode=ParseMode.HTML,
48 | reply_markup=message.reply_markup
49 | )
50 |
51 |
52 | def __send_video(bot: Bot, message: TelegramMessage, user_id: int):
53 | video = Video(
54 | file_id=message.video.file_id,
55 | file_unique_id=message.video.file_unique_id,
56 | width=message.video.width,
57 | height=message.video.height,
58 | duration=message.video.file_size
59 | )
60 |
61 | bot.send_video(
62 | chat_id=user_id,
63 | video=video,
64 | caption=message.caption_html,
65 | parse_mode=ParseMode.HTML,
66 | reply_markup=message.reply_markup
67 | )
68 |
69 |
70 | def __send_message(bot: Bot, message: TelegramMessage, user_id: int):
71 | if message.photo:
72 | __send_photo(bot, message, user_id)
73 |
74 | if message.animation:
75 | __send_animation(bot, message, user_id)
76 |
77 | if message.video:
78 | __send_video(bot, message, user_id)
79 |
80 | elif message.text:
81 | bot.send_message(
82 | chat_id=user_id,
83 | text=message.text_html,
84 | parse_mode=ParseMode.HTML,
85 | reply_markup=message.reply_markup
86 | )
87 |
88 |
89 | def __send_mailing(context: CallbackContext):
90 | message = context.user_data['mailing_message']
91 | users = User.select().where(User.active == True)
92 |
93 | sent_count = 0
94 |
95 | for user in users:
96 | try:
97 | __send_message(context.bot, message, user.user_id)
98 | sent_count += 1
99 | time.sleep(1 / 20)
100 |
101 | logging.info(f'Mailing message sent to user {user.user_id} (total: {sent_count})')
102 |
103 | except TelegramError as ex:
104 | if ex.message == 'Forbidden: bot was blocked by the user':
105 | user.active = False
106 | user.save()
107 |
108 | logging.info(f'User {user.user_id} became inactive')
109 |
110 | else:
111 | logging.error(ex)
112 |
113 | except Exception as ex:
114 | logging.error(ex)
115 |
116 | return sent_count
117 |
118 |
119 | def mailing_message_callback(update: Update, context: CallbackContext):
120 | context.user_data['mailing_message'] = update.message
121 |
122 | context.bot.send_message(
123 | chat_id=update.effective_chat.id,
124 | text=Message.received_mailing,
125 | reply_markup=Keyboard.mailing
126 | )
127 |
128 | return States.received_mailing
129 |
130 |
131 | def preview_mailing_callback(update: Update, context: CallbackContext):
132 | message = context.user_data['mailing_message']
133 | __send_message(context.bot, message, update.effective_user.id)
134 | return States.received_mailing
135 |
136 |
137 | def cancel_mailing_callback(update: Update, context: CallbackContext):
138 | del context.user_data['mailing_message']
139 |
140 | context.bot.send_message(
141 | chat_id=update.effective_chat.id,
142 | text=Message.mailing_canceled,
143 | reply_markup=Keyboard.main
144 | )
145 |
146 | return ConversationHandler.END
147 |
148 |
149 | def __send_mailing_callback(update: Update, context: CallbackContext):
150 | context.bot.send_message(
151 | chat_id=update.effective_chat.id,
152 | text=Message.mailing_started,
153 | reply_markup=Keyboard.main
154 | )
155 |
156 | logging.info('Mailing has started')
157 | sent_count = __send_mailing(context)
158 |
159 | context.bot.send_message(
160 | chat_id=update.effective_chat.id,
161 | text=Message.mailing_finished.format(sent_count=sent_count)
162 | )
163 |
164 | logging.info('Mailing has finished')
165 | return ConversationHandler.END
166 |
167 |
168 | def send_mailing_callback(update: Update, context: CallbackContext):
169 | mailing_thread = Thread(target=__send_mailing_callback, args=(update, context))
170 | mailing_thread.start()
171 |
--------------------------------------------------------------------------------
/bot/callbacks/service.py:
--------------------------------------------------------------------------------
1 | from telegram import Update, TelegramError, ParseMode
2 | from telegram.ext import CallbackContext
3 | import logging
4 |
5 | from settings import DEVELOPER
6 |
7 | from ..constants import Message
8 |
9 |
10 | # catch errors
11 | def error_callback(update: Update, context: CallbackContext):
12 | try:
13 | raise context.error
14 |
15 | except TelegramError as ex:
16 | logging.error(ex)
17 |
18 | context.bot.send_message(
19 | chat_id=DEVELOPER,
20 | text=Message.unexpected_error.format(error=context.error, update=update),
21 | parse_mode=ParseMode.HTML
22 | )
23 |
24 | except Exception as ex:
25 | logging.error(ex)
26 |
--------------------------------------------------------------------------------
/bot/constants.py:
--------------------------------------------------------------------------------
1 | from telegram import InlineKeyboardMarkup, InlineKeyboardButton, ReplyKeyboardMarkup
2 |
3 |
4 | URLS = {
5 | 'instagram': 'https://www.instagram.com/{}/',
6 | 'twitter': 'https://twitter.com/{}/',
7 | 'vk': 'https://vk.com/{}/',
8 | 'facebook': 'https://www.facebook.com/{}/',
9 | 'github': 'https://github.com/{}/',
10 | 'telegram': 'https://t.me/{}/',
11 | 'tiktok': 'https://www.tiktok.com/@{}?'
12 | }
13 |
14 | USERNAME_STATUSES = {
15 | True: {
16 | 'emoji': '⛔',
17 | 'text': 'unavailable'
18 | },
19 |
20 | False: {
21 | 'emoji': '✅',
22 | 'text': 'available'
23 | },
24 |
25 | None: {
26 | 'emoji': '🔎',
27 | 'text': 'in progress'
28 | }
29 | }
30 |
31 |
32 | class States:
33 | prepare_mailing = 1
34 | received_mailing = 2
35 |
36 |
37 | class CallbackData:
38 | statistics = 'statistics'
39 | mailing = 'mailing'
40 | backup = 'backup'
41 |
42 |
43 | class ReplyButtons:
44 | check_my_username = '⚙️ Check my username'
45 | how_to_use = '💬 How to use?'
46 |
47 | send_mailing = 'Send'
48 | preview_mailing = 'Preview'
49 | cancel_mailing = 'Cancel'
50 |
51 |
52 | class Keyboard:
53 | main = ReplyKeyboardMarkup([
54 | [ReplyButtons.check_my_username, ReplyButtons.how_to_use]
55 | ], resize_keyboard=True)
56 |
57 | admin = InlineKeyboardMarkup([
58 | [InlineKeyboardButton('View statistics', callback_data=CallbackData.statistics)],
59 | [InlineKeyboardButton('Create broadcast', callback_data=CallbackData.mailing)]
60 | ])
61 |
62 | mailing = ReplyKeyboardMarkup([
63 | [ReplyButtons.send_mailing],
64 | [ReplyButtons.preview_mailing, ReplyButtons.cancel_mailing]
65 | ])
66 |
67 |
68 | class Message:
69 | result = (
70 | 'Information about username @{username}
:\n\n'
71 | '{instagram_status} Instagram: {instagram}\n'
72 | '{twitter_status} Twitter: {twitter}\n'
73 | '{vk_status} Vkontakte: {vk}\n'
74 | '{facebook_status} Facebook: {facebook}\n'
75 | '{github_status} Github: {github}\n'
76 | '{tiktok_status} Tiktok: {tiktok}\n'
77 | '{telegram_status} Telegram: {telegram}\n\n'
78 | 'via @{bot_username}'
79 | )
80 |
81 | start = (
82 | 'Hey there 👋\n\n'
83 | "Just send me any username and I will check if the it's available on social medias. "
84 | 'Remember that usernames can contain only letters, numbers and underscores.\n\n'
85 | 'Have a good one!'
86 | )
87 |
88 | how_to_use = (
89 | "Just send me any username and I will check if the it's available on social medias. "
90 | 'Remember that usernames can contain only letters, numbers and underscores.\n\n'
91 | )
92 |
93 | invalid_username = '💬 Usernames can only contain letters, numbers, underscores and dashes'
94 |
95 | no_username = "💬 Your profile doesn't have an username, you can set one in the settings"
96 |
97 | admin = 'Welcome to the admin panel!'
98 |
99 | statistics = (
100 | "Bot's statistics:\n\n"
101 | 'Users in total: {total_users}\n'
102 | 'Active users: {active_users}\n'
103 | 'Number of requests: {total_requests}'
104 | )
105 |
106 | mailing = 'Send a messaged for the broadcast'
107 |
108 | received_mailing = "The message has been received. What's next?"
109 |
110 | mailing_canceled = 'Broadcast has been cancelled'
111 |
112 | mailing_started = 'Broadcast had started'
113 |
114 | mailing_finished = (
115 | 'Message has been sent:\n\n'
116 | 'Users that received the message: {sent_count}'
117 | )
118 |
119 | unexpected_error = 'Telegram Error: {error}.\n\n{update}
'
120 |
--------------------------------------------------------------------------------
/bot/handlers.py:
--------------------------------------------------------------------------------
1 | from telegram.ext import CommandHandler, MessageHandler, CallbackQueryHandler, ConversationHandler, Filters
2 |
3 | from settings import ADMINS
4 |
5 | from .constants import CallbackData, States, ReplyButtons
6 | from .callbacks import *
7 |
8 |
9 | # command handlers
10 | admin_handler = CommandHandler(
11 | command='admin', callback=admin_command_callback,
12 | filters=Filters.user(user_id=ADMINS)
13 | )
14 |
15 | start_handler = CommandHandler(
16 | command='start', callback=start_command_callback
17 | )
18 |
19 | # admin handlers
20 | statistics_handler = CallbackQueryHandler(
21 | pattern=CallbackData.statistics,
22 | callback=statistics_callback
23 | )
24 |
25 | # mailing handlers
26 | mailing_conversation_handler = ConversationHandler(
27 | entry_points=[CallbackQueryHandler(pattern=CallbackData.mailing, callback=mailing_callback)],
28 | states={
29 | States.prepare_mailing: [MessageHandler(callback=mailing_message_callback, filters=Filters.all)],
30 | States.received_mailing: [
31 | MessageHandler(filters=Filters.text(ReplyButtons.preview_mailing), callback=preview_mailing_callback),
32 | MessageHandler(filters=Filters.text(ReplyButtons.cancel_mailing), callback=cancel_mailing_callback),
33 | MessageHandler(filters=Filters.text(ReplyButtons.send_mailing), callback=send_mailing_callback)
34 | ]
35 | },
36 | fallbacks=[],
37 | run_async=True
38 |
39 | )
40 |
41 | # core handlers
42 | check_my_username_handler = MessageHandler(
43 | filters=Filters.text(ReplyButtons.check_my_username),
44 | callback=check_my_username_callback,
45 | run_async=True
46 | )
47 |
48 | how_to_use_handler = MessageHandler(
49 | filters=Filters.text(ReplyButtons.how_to_use),
50 | callback=how_to_use_callback
51 | )
52 |
53 | check_username_handler = MessageHandler(
54 | filters=Filters.text,
55 | callback=check_username_callback,
56 | run_async=True
57 | )
58 |
--------------------------------------------------------------------------------
/bot/models.py:
--------------------------------------------------------------------------------
1 | from peewee import Model, IntegerField, BooleanField
2 |
3 | from settings import DEBUG
4 |
5 | if not DEBUG:
6 | from peewee import PostgresqlDatabase
7 | from settings import (
8 | DB_NAME, DB_USER, DB_PASSWORD,
9 | DB_HOST, DB_PORT
10 | )
11 |
12 | database = PostgresqlDatabase(
13 | database=DB_NAME,
14 | user=DB_USER, password=DB_PASSWORD,
15 | host=DB_HOST, port=DB_PORT
16 | )
17 |
18 | else:
19 | from peewee import SqliteDatabase
20 | from settings import SQLITE_DB_PATH
21 |
22 | database = SqliteDatabase(SQLITE_DB_PATH)
23 |
24 |
25 | # base model for other models
26 | class BaseModel(Model):
27 | class Meta:
28 | database = database
29 |
30 |
31 | # model that represents user
32 | class User(BaseModel):
33 | user_id = IntegerField(primary_key=True, unique=True)
34 | requests = IntegerField(default=0)
35 | active = BooleanField(default=True)
36 |
--------------------------------------------------------------------------------
/bot/tools_old.py:
--------------------------------------------------------------------------------
1 | from telegram import Update, InlineKeyboardMarkup, InlineKeyboardButton, PhotoSize
2 | from telegram.ext import CallbackContext
3 | from telegram.error import TelegramError, NetworkError
4 |
5 | from bot.utils.constants_old import username_statuses
6 | from .database import DataBase
7 | from bot.utils.checkers import checkers
8 |
9 | from functools import wraps
10 | import validators
11 | import logging
12 | import time
13 | import sys
14 | import os
15 |
16 |
17 | def send_action(action):
18 | def decorator(func):
19 | @wraps(func)
20 | def command_func(update: Update, context: CallbackContext, *args, **kwargs):
21 | context.bot.send_chat_action(chat_id=update.effective_message.chat_id, action=action)
22 | return func(update, context, *args, **kwargs)
23 | return command_func
24 | return decorator
25 |
26 |
27 | def strip(string):
28 | return string.strip()
29 |
30 |
31 | def stop_and_restart():
32 | from main import updater
33 | updater.stop()
34 | logging.info('Bot has been stopped.')
35 | os.execl(sys.executable, sys.executable, *sys.argv)
36 |
37 |
38 | def send_mailing(bot, data: dict):
39 | successful, failed = 0, 0
40 | markup = None
41 |
42 | if data['photo'] or data['text']:
43 | yield True
44 | else:
45 | yield False
46 |
47 | if data['button']:
48 | text, url = map(strip, data['button'].split('-'))
49 | button = InlineKeyboardButton(text, url=url)
50 | markup = InlineKeyboardMarkup([[button]])
51 |
52 | with DataBase() as db:
53 |
54 | for user_id in db.get_users():
55 | try:
56 | if data['photo']:
57 | bot.send_photo(
58 | chat_id=user_id, photo=PhotoSize(*data['photo']),
59 | caption=data['text'], reply_markup=markup,
60 | parse_mode='HTML', disable_web_page_preview=True
61 | )
62 |
63 | elif data['text']:
64 | bot.send_message(
65 | chat_id=user_id, text=data['text'],
66 | reply_markup=markup, parse_mode='HTML',
67 | disable_web_page_preview=True
68 | )
69 |
70 | successful += 1
71 | time.sleep(1 / 20)
72 |
73 | except NetworkError as ex:
74 | if ex == 'Chat not found':
75 | db.del_user(user_id=user_id)
76 | failed += 1
77 |
78 | except TelegramError:
79 | db.del_user(user_id=user_id)
80 | failed += 1
81 |
82 | yield successful, failed
83 |
84 |
85 | def check_username(username: str):
86 | data = {social_media: None for social_media, _ in checkers.items()}
87 | yield data
88 |
89 | for social_media, checker in checkers.items():
90 | data[social_media] = checker(username)
91 | yield data
92 |
93 |
94 | def process_data(data: dict, lang: str):
95 | text, status = None, None
96 | new_data = dict()
97 |
98 | for social_media, result in data.items():
99 | if result is False:
100 | text = username_statuses[False][lang]
101 | status = '️✅'
102 | elif result is None:
103 | text = username_statuses[None][lang]
104 | status = '🔎'
105 | elif validators.url(result):
106 | text = username_statuses[True][lang].format(result)
107 | status = '⛔'
108 |
109 | new_data[social_media] = text
110 | new_data[social_media + '_status'] = status
111 | return new_data
112 |
--------------------------------------------------------------------------------
/bot/utils/__init__.py:
--------------------------------------------------------------------------------
1 | from .checkers import checkers
2 |
--------------------------------------------------------------------------------
/bot/utils/checkers.py:
--------------------------------------------------------------------------------
1 | import requests
2 |
3 | from ..constants import URLS
4 |
5 |
6 | HEADERS = {'user-agent': (
7 | 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) '
8 | 'AppleWebKit/605.1.15 (KHTML, like Gecko) '
9 | 'Version/14.1.1 Safari/605.1.15')
10 | }
11 |
12 |
13 | def _check_request(link: str):
14 | response = requests.get(link)
15 | if response.status_code == 200:
16 | return link
17 | return False
18 |
19 |
20 | def _check_instagram(username: str):
21 | link = URLS['instagram'].format(username) # TODO
22 | return _check_request(link)
23 |
24 |
25 | def _check_twitter(username: str):
26 | link = URLS['twitter'].format(username) # TODO
27 | return _check_request(link)
28 |
29 |
30 | def _check_vk(username: str):
31 | link = URLS['vk'].format(username)
32 | return _check_request(link)
33 |
34 |
35 | def _check_facebook(username: str):
36 | link = URLS['facebook'].format(username)
37 | return _check_request(link)
38 |
39 |
40 | def _check_github(username: str):
41 | link = URLS['github'].format(username)
42 | return _check_request(link)
43 |
44 |
45 | def _check_tiktok(username):
46 | link = URLS['tiktok'].format(username)
47 | if 'video-feed' in requests.get(link, headers=HEADERS).text:
48 | return link
49 | return False
50 |
51 |
52 | def _check_telegram(username):
53 | link = URLS['telegram'].format(username)
54 | if 'tgme_page_title' in requests.get(link, headers=HEADERS).text:
55 | return link
56 | return False
57 |
58 |
59 | checkers = {
60 | 'instagram': _check_instagram,
61 | 'twitter': _check_twitter,
62 | 'vk': _check_vk,
63 | 'facebook': _check_facebook,
64 | 'github': _check_github,
65 | 'tiktok': _check_tiktok,
66 | 'telegram': _check_telegram
67 | }
68 |
--------------------------------------------------------------------------------
/images/example_en.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adrian-kalinin/CheckNicknameBot/c5b38d400f7189d804aaf687cc7b12ee5187f0d7/images/example_en.png
--------------------------------------------------------------------------------
/images/example_ru.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adrian-kalinin/CheckNicknameBot/c5b38d400f7189d804aaf687cc7b12ee5187f0d7/images/example_ru.png
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | from telegram.ext import Updater
2 | import logging
3 |
4 | from settings import BOT_TOKEN
5 |
6 | from bot.models import database, User
7 | from bot.callbacks import error_callback
8 |
9 | from bot.handlers import (
10 | start_handler, admin_handler,
11 | statistics_handler, mailing_conversation_handler,
12 | check_my_username_handler, how_to_use_handler,
13 | check_username_handler
14 | )
15 |
16 |
17 | # set up logger
18 | logging.basicConfig(
19 | format='%(asctime)s – %(levelname)s – %(message)s',
20 | datefmt='%m/%d/%Y %I:%M:%S %p',
21 | level=logging.INFO
22 | )
23 |
24 |
25 | # create updater
26 | updater = Updater(BOT_TOKEN)
27 | dispatcher = updater.dispatcher
28 |
29 |
30 | # bound handlers to dispatcher
31 | def bound_handlers():
32 | # noinspection PyTypeChecker
33 | dispatcher.add_error_handler(error_callback)
34 |
35 | # command handlers
36 | dispatcher.add_handler(admin_handler)
37 | dispatcher.add_handler(start_handler)
38 |
39 | # admin handlers
40 | dispatcher.add_handler(statistics_handler)
41 |
42 | # mailing handlers
43 | dispatcher.add_handler(mailing_conversation_handler)
44 |
45 | # core handlers
46 | dispatcher.add_handler(check_my_username_handler)
47 | dispatcher.add_handler(how_to_use_handler)
48 | dispatcher.add_handler(check_username_handler)
49 |
50 |
51 | # set up database
52 | def configure_database():
53 | database.connect()
54 | database.create_tables([User])
55 | database.close()
56 | logging.info('Database has been configured')
57 |
58 |
59 | # set up webhook
60 | def configure_webhook():
61 | pass
62 |
63 |
64 | def main():
65 | # setting up application
66 | bound_handlers()
67 | configure_database()
68 | configure_webhook()
69 |
70 | # start bot
71 | updater.start_polling()
72 | logging.info('Bot has started')
73 |
74 |
75 | if __name__ == '__main__':
76 | main()
77 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | python-telegram-bot==13.3
2 | peewee==3.14.1
3 | requests==2.32.0
4 | python-environ==0.4.54
5 | psycopg2==2.9.1
--------------------------------------------------------------------------------
/settings.py:
--------------------------------------------------------------------------------
1 | import environ
2 |
3 |
4 | env = environ.Env()
5 |
6 | READ_DOT_ENV_FILE = env.bool('READ_DOT_ENV_FILE', default=False)
7 |
8 | if READ_DOT_ENV_FILE:
9 | environ.Env.read_env()
10 |
11 | DEBUG = env.bool('DEBUG', False)
12 |
13 | BOT_TOKEN = env('BOT_TOKEN')
14 | ADMINS = list(map(int, env.list('ADMINS')))
15 | DEVELOPER = env.int('DEVELOPER')
16 |
17 | if not DEBUG:
18 | DB_NAME = env('DB_NAME')
19 | DB_USER = env('DB_USER')
20 | DB_PASSWORD = env('DB_PASSWORD')
21 | DB_HOST = env('DB_HOST')
22 | DB_PORT = env.int('DB_PORT')
23 |
24 | else:
25 | SQLITE_DB_PATH = env('SQLITE_DB_PATH')
26 |
--------------------------------------------------------------------------------