cleanup; antispam

This commit is contained in:
bakatrouble 2020-05-19 17:43:53 +03:00
parent aad42f059e
commit eb969e9dfb
7 changed files with 124 additions and 129 deletions

Binary file not shown.

BIN
MORJ.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 179 KiB

Binary file not shown.

198
main.py
View File

@ -5,43 +5,55 @@ import os
import re import re
import traceback import traceback
from datetime import datetime, timedelta from datetime import datetime, timedelta
from hashlib import sha1
from html import escape from html import escape
from queue import Queue, Empty from queue import Queue, Empty
from time import sleep from time import sleep
from threading import Thread from threading import Thread, Event
from typing import Dict, List from typing import Dict, List
from uuid import uuid4
import sentry_sdk import sentry_sdk
from redis import Redis
from telegram.error import Unauthorized, TelegramError from telegram.error import Unauthorized, TelegramError
from telegram.ext import Updater, CommandHandler, MessageHandler, Filters, CallbackQueryHandler from telegram.ext import Updater, CommandHandler, MessageHandler, Filters, CallbackQueryHandler, CallbackContext
from telegram import Message, Update, Bot, InlineKeyboardMarkup, InlineKeyboardButton, User, InputMediaPhoto, \ from telegram import Message, Update, Bot, InlineKeyboardMarkup, InlineKeyboardButton, User, InputMediaPhoto, \
InputMediaVideo, InputMediaAnimation, InputMediaAudio, InputMediaDocument InputMediaVideo, InputMediaAnimation, InputMediaAudio, InputMediaDocument
from config import BOT_TOKEN, SENTRY_DSN, MANAGEMENT_CHAT, DEBUG from config import BOT_TOKEN, SENTRY_DSN, MANAGEMENT_CHAT, DEBUG
from db import get_conn, Subscriber, PersistentMapping, commit from db import get_conn, Subscriber, commit
from morj import draw_morj
from send_users_list import send_users_list from send_users_list import send_users_list
from shepherd import draw_shepherd
logging.basicConfig(level=logging.WARNING) logging.basicConfig(level=logging.WARNING)
queue = Queue() queue = Queue()
sentry_sdk.init(dsn=SENTRY_DSN) sentry_sdk.init(dsn=SENTRY_DSN)
conn = get_conn() conn = get_conn()
redis = Redis()
MAX_MESSAGE_LENGTH = 4096 MAX_MESSAGE_LENGTH = 4096
MAX_CAPTION_LENGTH = 1024 MAX_CAPTION_LENGTH = 1024
def _antispam(args):
if not args:
return True
args = '|'.join(map(str, args))
digest = sha1(args.encode()).digest()
key = 'lono-' + digest.hex()
if redis.get(key):
return False
redis.set(key, '1', ex=30)
return True
def _notify_access_request(bot: Bot, user: User): def _notify_access_request(bot: Bot, user: User):
markup = InlineKeyboardMarkup([[InlineKeyboardButton('Добавить', callback_data=f'add {user.id}')]]) markup = InlineKeyboardMarkup([[InlineKeyboardButton('Добавить', callback_data=f'add {user.id}')]])
bot.send_message(MANAGEMENT_CHAT, f'<a href="tg://user?id={user.id}">{escape(user.full_name)}</a> запросил доступ', bot.send_message(MANAGEMENT_CHAT, f'<a href="tg://user?id={user.id}">{escape(user.full_name)}</a> запросил доступ',
parse_mode='html', reply_markup=markup) parse_mode='html', reply_markup=markup)
def welcome(bot: Bot, update: Update): def welcome(update: Update, ctx: CallbackContext):
if DEBUG: if DEBUG:
_add_user(bot, update.effective_user.id) _add_user(ctx.bot, update.effective_user.id)
update.message.reply_text('Добро пожаловать (debug)') update.message.reply_text('Добро пожаловать (debug)')
return return
@ -49,14 +61,14 @@ def welcome(bot: Bot, update: Update):
update.message.reply_text('Вы уже являетесь участником ЛОНО') update.message.reply_text('Вы уже являетесь участником ЛОНО')
else: else:
update.message.reply_text('Пожалуйста, обратитесь к @lono_contactbot') update.message.reply_text('Пожалуйста, обратитесь к @lono_contactbot')
_notify_access_request(bot, update.message.from_user) _notify_access_request(ctx.bot, update.message.from_user)
def unsubscribe(bot: Bot, update: Update): def unsubscribe(update: Update, ctx: CallbackContext):
user = _remove_user(update.message.chat_id) user = _remove_user(update.message.chat_id)
update.message.reply_text('Вы были отписаны от бота. ' update.message.reply_text('Вы были отписаны от бота. '
'Обратитесь к @lono_contactbot если вы хотите подписаться снова.') 'Обратитесь к @lono_contactbot если вы хотите подписаться снова.')
bot.send_message(MANAGEMENT_CHAT, f'<a href="tg://user?id={user.id}">{escape(user.name)}</a> отписался') ctx.bot.send_message(MANAGEMENT_CHAT, f'<a href="tg://user?id={user.id}">{escape(user.name)}</a> отписался')
def _add_user(bot, uid): def _add_user(bot, uid):
@ -65,7 +77,7 @@ def _add_user(bot, uid):
return user return user
def add_user(bot: Bot, update: Update, groups=(), args=()): def add_user(update: Update, ctx: CallbackContext, groups=(), args=()):
if update.callback_query: if update.callback_query:
update.callback_query.answer() update.callback_query.answer()
@ -78,7 +90,7 @@ def add_user(bot: Bot, update: Update, groups=(), args=()):
elif update.message and update.message.reply_to_message and update.message.reply_to_message.forward_from: elif update.message and update.message.reply_to_message and update.message.reply_to_message.forward_from:
uid = update.message.reply_to_message.forward_from.id uid = update.message.reply_to_message.forward_from.id
else: else:
return bot.send_message(MANAGEMENT_CHAT, 'Укажите ID пользователя или ответьте на его сообщение') return ctx.bot.send_message(MANAGEMENT_CHAT, 'Укажите ID пользователя или ответьте на его сообщение')
try: try:
uid = int(uid) uid = int(uid)
@ -86,14 +98,14 @@ def add_user(bot: Bot, update: Update, groups=(), args=()):
pass pass
try: try:
user = _add_user(bot, uid) user = _add_user(ctx.bot, uid)
if update.callback_query: if update.callback_query:
update.callback_query.message.edit_reply_markup() update.callback_query.message.edit_reply_markup()
bot.send_message(MANAGEMENT_CHAT, f'<a href="tg://user?id={uid}">{escape(user.name)}</a> был добавлен', ctx.bot.send_message(MANAGEMENT_CHAT, f'<a href="tg://user?id={uid}">{escape(user.name)}</a> был добавлен',
parse_mode='html') parse_mode='html')
bot.send_message(uid, 'Добро пожаловать. Снова.') ctx.bot.send_message(uid, 'Добро пожаловать. Снова.')
except TelegramError as e: except TelegramError as e:
bot.send_message(MANAGEMENT_CHAT, str(e)) ctx.bot.send_message(MANAGEMENT_CHAT, str(e))
def _remove_user(uid): def _remove_user(uid):
@ -103,7 +115,7 @@ def _remove_user(uid):
return user return user
def remove_user(bot: Bot, update: Update, groups=(), args=()): def remove_user(update: Update, ctx: CallbackContext, groups=(), args=()):
if update.callback_query: if update.callback_query:
update.callback_query.answer() update.callback_query.answer()
@ -116,7 +128,7 @@ def remove_user(bot: Bot, update: Update, groups=(), args=()):
elif update.message and update.message.reply_to_message and update.message.reply_to_message.forward_from: elif update.message and update.message.reply_to_message and update.message.reply_to_message.forward_from:
uid = update.message.reply_to_message.forward_from.id uid = update.message.reply_to_message.forward_from.id
else: else:
return bot.send_message(MANAGEMENT_CHAT, 'Укажите ID пользователя или ответьте на его сообщение') return ctx.bot.send_message(MANAGEMENT_CHAT, 'Укажите ID пользователя или ответьте на его сообщение')
try: try:
uid = int(uid) uid = int(uid)
@ -125,19 +137,19 @@ def remove_user(bot: Bot, update: Update, groups=(), args=()):
try: try:
user = _remove_user(uid) user = _remove_user(uid)
bot.send_message(MANAGEMENT_CHAT, f'<a href="tg://user?id={uid}">{escape(user.name)}</a> был удален', ctx.bot.send_message(MANAGEMENT_CHAT, f'<a href="tg://user?id={uid}">{escape(user.name)}</a> был удален',
parse_mode='html') parse_mode='html')
if update.callback_query: if update.callback_query:
update.callback_query.message.edit_reply_markup() update.callback_query.message.edit_reply_markup()
except KeyError: except KeyError:
bot.send_message(MANAGEMENT_CHAT, f'Пользователь id={uid} не был найден') ctx.bot.send_message(MANAGEMENT_CHAT, f'Пользователь id={uid} не был найден')
def users(bot: Bot, update: Update): def users(update: Update, ctx: CallbackContext):
send_users_list() send_users_list()
def msg(bot: Bot, update: Update): def msg(update: Update, ctx: CallbackContext):
queue.put(update.message) queue.put(update.message)
@ -219,7 +231,7 @@ def _process_media_group(bot: Bot, messages: List[Message]):
_remove_user(uid) _remove_user(uid)
def users_list(bot: Bot, update: Update): def users_list(update: Update, ctx: CallbackContext):
current_chat = update.effective_chat.id current_chat = update.effective_chat.id
subs = conn.root.subscribers # type: Dict[int, Subscriber] subs = conn.root.subscribers # type: Dict[int, Subscriber]
if current_chat not in subs: if current_chat not in subs:
@ -238,22 +250,6 @@ def users_list(bot: Bot, update: Update):
update.effective_message.reply_text('\n'.join(messages[i:i+40]), parse_mode='html') update.effective_message.reply_text('\n'.join(messages[i:i+40]), parse_mode='html')
def morj(bot: Bot, update: Update):
text = update.effective_message.text[6:]
fname = '/tmp/morj{}.png'.format(uuid4())
draw_morj(text, fname)
update.effective_message.reply_photo(open(fname, 'rb'))
os.unlink(fname)
def shepherd(bot: Bot, update: Update):
text = update.effective_message.text[10:]
fname = '/tmp/shepherd{}.png'.format(uuid4())
draw_shepherd(text, fname)
update.effective_message.reply_photo(open(fname, 'rb'))
os.unlink(fname)
def _process_message(bot: Bot, m: Message): def _process_message(bot: Bot, m: Message):
if m.sticker or m.animation: if m.sticker or m.animation:
delta = datetime.now() - conn.root.last_media delta = datetime.now() - conn.root.last_media
@ -282,8 +278,67 @@ def _process_message(bot: Bot, m: Message):
if m.reply_to_message and m.reply_to_message.message_id in users[current_chat].messages_forward: if m.reply_to_message and m.reply_to_message.message_id in users[current_chat].messages_forward:
reply_to_message_internal_id = users[current_chat].messages_forward[m.reply_to_message.message_id] reply_to_message_internal_id = users[current_chat].messages_forward[m.reply_to_message.message_id]
func = None
args = []
kwargs = {}
if m.forward_date:
func = m.forward
elif hasattr(m, 'audio') and m.audio:
a = m.audio
func = bot.send_audio
args = [a.file_id, a.duration, a.performer, a.title, caption]
kwargs = dict(parse_mode='html')
elif hasattr(m, 'document') and m.document:
d = m.document
func = bot.send_document
args = [d.file_id, d.file_name, caption]
kwargs = dict(parse_mode='html')
elif hasattr(m, 'photo') and m.photo:
p = m.photo
func = bot.send_photo
args = [p[-1].file_id, caption]
kwargs = dict(parse_mode='html')
elif hasattr(m, 'sticker') and m.sticker:
s = m.sticker
func = bot.send_sticker
args = [s.file_id]
elif hasattr(m, 'video') and m.video:
v = m.video
func = bot.send_video
args = [v.file_id, v.duration, caption]
kwargs = dict(parse_mode='html')
elif hasattr(m, 'voice') and m.voice:
v = m.voice
func = bot.send_voice
args = [v.file_id, v.duration, caption]
kwargs = dict(parse_mode='html')
elif hasattr(m, 'video_note') and m.video_note:
vn = m.video_note
func = bot.send_video_note
args = [vn.file_id, vn.duration, vn.length]
elif hasattr(m, 'contact') and m.contact:
c = m.contact
func = bot.send_contact
args = [c.phone_number, c.first_name, c.last_name]
elif hasattr(m, 'location') and m.location:
l = m.location
func = bot.send_location
args = [l.latitude, l.longitude]
elif hasattr(m, 'venue') and m.venue:
v = m.venue
l = v.location
func = bot.send_venue
args = [l.latitude, l.longitude, v.title, v.address, v.foursquare_id]
elif hasattr(m, 'text') and m.text:
func = bot.send_message
args = [text, 'html']
if not _antispam(args):
return m.reply_text('Не вайпи', quote=True)
remove_uids = [] remove_uids = []
if func:
for uid, user in users.items(): for uid, user in users.items():
sleep(.02) sleep(.02)
@ -292,50 +347,7 @@ def _process_message(bot: Bot, m: Message):
reply_to_message_id = user.messages_reverse.get(reply_to_message_internal_id, None) reply_to_message_id = user.messages_reverse.get(reply_to_message_internal_id, None)
try: try:
r = None r = func(*([uid] + args), **kwargs, reply_to_message_id=reply_to_message_id)
if m.forward_date:
r = m.forward(uid)
elif hasattr(m, 'audio') and m.audio:
a = m.audio
r = bot.send_audio(uid, a.file_id, a.duration, a.performer, a.title, caption,
reply_to_message_id=reply_to_message_id, parse_mode='html')
elif hasattr(m, 'document') and m.document:
d = m.document
r = bot.send_document(uid, d.file_id, d.file_name, caption, reply_to_message_id=reply_to_message_id,
parse_mode='html')
elif hasattr(m, 'photo') and m.photo:
p = m.photo
r = bot.send_photo(uid, p[-1].file_id, caption, reply_to_message_id=reply_to_message_id,
parse_mode='html')
elif hasattr(m, 'sticker') and m.sticker:
s = m.sticker
r = bot.send_sticker(uid, s.file_id, reply_to_message_id=reply_to_message_id)
elif hasattr(m, 'video') and m.video:
v = m.video
r = bot.send_video(uid, v.file_id, v.duration, caption, reply_to_message_id=reply_to_message_id,
parse_mode='html')
elif hasattr(m, 'voice') and m.voice:
v = m.voice
r = bot.send_voice(uid, v.file_id, v.duration, caption, reply_to_message_id=reply_to_message_id,
parse_mode='html')
elif hasattr(m, 'video_note') and m.video_note:
vn = m.video_note
r = bot.send_video_note(uid, vn.file_id, vn.duration, vn.length,
reply_to_message_id=reply_to_message_id)
elif hasattr(m, 'contact') and m.contact:
c = m.contact
r = bot.send_contact(uid, c.phone_number, c.first_name, c.last_name,
reply_to_message_id=reply_to_message_id)
elif hasattr(m, 'location') and m.location:
l = m.location
r = bot.send_location(uid, l.latitude, l.longitude, reply_to_message_id=reply_to_message_id)
elif hasattr(m, 'venue') and m.venue:
v = m.venue
l = v.location
r = bot.send_venue(uid, l.latitude, l.longitude, v.title, v.address, v.foursquare_id,
reply_to_message_id=reply_to_message_id)
elif hasattr(m, 'text') and m.text:
r = bot.send_message(uid, text, 'html', reply_to_message_id=reply_to_message_id)
if r: if r:
user.update_from_message(r) user.update_from_message(r)
user.messages_forward[r.message_id] = conn.root.counter user.messages_forward[r.message_id] = conn.root.counter
@ -353,9 +365,9 @@ def _process_message(bot: Bot, m: Message):
_remove_user(uid) _remove_user(uid)
def task_queue(u: Updater): def task_queue(u: Updater, stop_signal: Event):
while True: while True:
if not u.running: if not u.running or stop_signal.is_set():
return return
try: try:
@ -375,8 +387,8 @@ def task_queue(u: Updater):
sentry_sdk.capture_exception() sentry_sdk.capture_exception()
if __name__ == '__main__': def main():
updater = Updater(BOT_TOKEN, workers=4) updater = Updater(BOT_TOKEN, workers=4, use_context=True)
updater.dispatcher.add_handler(CommandHandler('start', welcome, Filters.private)) updater.dispatcher.add_handler(CommandHandler('start', welcome, Filters.private))
updater.dispatcher.add_handler(CommandHandler('stop', unsubscribe, Filters.private)) updater.dispatcher.add_handler(CommandHandler('stop', unsubscribe, Filters.private))
@ -385,17 +397,21 @@ if __name__ == '__main__':
updater.dispatcher.add_handler(CommandHandler('remove', remove_user, Filters.chat(MANAGEMENT_CHAT), pass_args=True)) updater.dispatcher.add_handler(CommandHandler('remove', remove_user, Filters.chat(MANAGEMENT_CHAT), pass_args=True))
updater.dispatcher.add_handler(CallbackQueryHandler(remove_user, pattern=r'^remove (\d+)$', pass_groups=True)) updater.dispatcher.add_handler(CallbackQueryHandler(remove_user, pattern=r'^remove (\d+)$', pass_groups=True))
updater.dispatcher.add_handler(CommandHandler('users', users_list, Filters.private)) updater.dispatcher.add_handler(CommandHandler('users', users_list, Filters.private))
updater.dispatcher.add_handler(CommandHandler('morj', morj))
updater.dispatcher.add_handler(CommandHandler('shepherd', shepherd))
updater.dispatcher.add_handler(MessageHandler(Filters.private, msg)) updater.dispatcher.add_handler(MessageHandler(Filters.private, msg))
updater.start_polling() updater.start_polling()
tq = Thread(target=task_queue, args=(updater,)) stop_signal = Event()
tq = Thread(target=task_queue, args=(updater, stop_signal))
tq.start() tq.start()
logging.warning('LONO has started') logging.warning('LONO has started')
updater.idle() updater.idle()
stop_signal.set()
logging.warning('LONO is stopping...') logging.warning('LONO is stopping...')
commit() commit()
conn.close() conn.close()
if __name__ == '__main__':
main()

10
morj.py
View File

@ -1,10 +0,0 @@
from image_text import ImageText
FONT_FILE = 'CenturyGothicBold.ttf'
BG = 'MORJ.png'
def draw_morj(text, filename):
img = ImageText(BG)
img.write_text_box((67, 70), text, box_width=800, font_filename=FONT_FILE, font_size=40, color=(255, 255, 255))
img.save(filename)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

View File

@ -1,11 +0,0 @@
from image_text import ImageText
FONT_FILE = 'lobster.ttf'
BG = 'shepherd.jpg'
def draw_shepherd(text, filename):
img = ImageText(BG)
img.write_text_box((74, 310), text, box_width=600, font_filename=FONT_FILE, font_size=40, color=(0, 0, 0),
place='center')
img.save(filename)