diff --git a/bot.py b/bot.py index ee67cef..30f2e36 100644 --- a/bot.py +++ b/bot.py @@ -1,32 +1,37 @@ import logging import os import re -import threading -import ujson - -import cherrypy import redis import requests import telebot +import threading +import traceback +import ujson import vk import wget from PIL import Image from telebot import types -from credentials import token, vk_app_id, local_port +import cherrypy + +from credentials import token, vk_app_id, local_port, bot_url from vk_messages import VkMessage, VkPolling logging.basicConfig(format='%(levelname)-8s [%(asctime)s] %(message)s', level=logging.WARNING, filename='vk.log') vk_threads = {} +vk_dialogs = {} + FILE_URL = 'https://api.telegram.org/file/bot{0}/{1}' tokens_pool = redis.ConnectionPool(host='localhost', port=6379, db=0) vk_tokens = redis.StrictRedis(connection_pool=tokens_pool) +currentchat = {} + bot = telebot.AsyncTeleBot(token) -bot.remove_webhook() +# bot.remove_webhook() link = 'https://oauth.vk.com/authorize?client_id={}&' \ 'display=page&redirect_uri=https://oauth.vk.com/blank.html&scope=friends,messages,offline,docs,photos,video' \ @@ -35,13 +40,14 @@ mark = types.InlineKeyboardMarkup() yes = types.InlineKeyboardButton('ВХОД', url=link) mark.add(yes) -"""def get_pages_switcher(offset, dialogs, markup): - if offset - 10 >= 0: - leftbutton = types.InlineKeyboardButton('◀', callback_data='page{}'.format(offset - 10)) # callback + +def get_pages_switcher(markup, page, pages): + if page != 0: + leftbutton = types.InlineKeyboardButton('◀', callback_data='page{}'.format(page - 1)) # callback else: leftbutton = None - if dialogs[0] - (offset + 10) >= -10: - rightbutton = types.InlineKeyboardButton('▶', callback_data='page{}'.format(offset + 10)) + if page + 1 < len(pages): + rightbutton = types.InlineKeyboardButton('▶', callback_data='page{}'.format(page + 1)) else: rightbutton = None @@ -54,44 +60,61 @@ mark.add(yes) markup.row(rightbutton) -def request_user_dialogs(session): +def replace_shields(text): + text = text.replace('<', '<') + text = text.replace('>', '>') + text = text.replace('&', '&') + text = text.replace('©', '©') + text = text.replace('®', '®') + text = text.replace('«', '«') + text = text.replace('»', '«') + text = text.replace('°', '°') + text = text.replace('™', '™') + text = text.replace('±', '±') + return text + + +def request_user_dialogs(session, userid): order = [] users_ids = [] - dialogs = vk.API(session).messages.getDialogs(offset=offset, count=10) + dialogs = vk.API(session).messages.getDialogs(count=200) for chat in dialogs[1:]: if 'chat_id' in chat: - order.append({'title': chat['title'], 'id': chat['chat_id']}) + if chat['title'].replace('\\', ''): + chat['title'] = chat['title'].replace('\\', '') + chat['title'] = replace_shields(chat['title']) + order.append({'title': chat['title'], 'id': 'group' + str(chat['chat_id'])}) elif chat['uid'] > 0: - order.append({'title': chat['title'], 'id': chat['uid']}) + order.append({'title': None, 'id': chat['uid']}) users_ids.append(chat['uid']) - users = vk.API(session).users.get(user_ids=users_ids, fields=[]) + users = vk.API(session).users.get(user_ids=users_ids, fields=['first_name', 'last_name', 'uid']) for output in order: - if output['title'] == ' ... ': + if output['title'] == ' ... ' or not output['title']: for x in users: if x['uid'] == output['id']: current_user = x break output['title'] = '{} {}'.format(current_user['first_name'], current_user['last_name']) - if not output['title']: - order.remove(output) - return order - - -def get_user_dialogs(message, order, edit=False): - markup = types.InlineKeyboardMarkup(row_width=2) for button in range(len(order)): order[button] = types.InlineKeyboardButton(order[button]['title'], callback_data=str(order[button]['id'])) rows = [order[x:x + 2] for x in range(0, len(order), 2)] - for row in rows: - try: - markup.row(row[0], row[1]) - except: - markup.row(row[0]) - get_pages_switcher(offset, dialogs, markup) + pages = [rows[x:x + 4] for x in range(0, len(rows), 4)] + vk_dialogs[str(userid)] = pages + + +def create_markup(message, user, page, edit=False): + markup = types.InlineKeyboardMarkup(row_width=2) + for i in vk_dialogs[str(user)][page]: + markup.row(*i) + get_pages_switcher(markup, page, vk_dialogs[str(user)]) if edit: - print(bot.edit_message_reply_markup(message.chat.id, message.message_id, reply_markup=markup).wait()) + bot.edit_message_text( + 'Выберите Диалог: {}/{} стр.'.format(page + 1, len(vk_dialogs[str(user)])), + message.chat.id, message.message_id, + parse_mode='HTML', reply_markup=markup).wait() else: - bot.send_message(message.chat.id, 'Выберите Диалог', + bot.send_message(message.chat.id, + 'Выберите Диалог: {}/{} стр.'.format(page + 1, len(vk_dialogs[str(user)])), parse_mode='HTML', reply_markup=markup).wait() @@ -100,9 +123,28 @@ def callback_buttons(call): if call.message: if 'page' in call.data: bot.answer_callback_query(call.id).wait() - print(call.data.split('page')[1]) - get_user_dialogs(call.message, VkMessage(vk_tokens.get(str(call.from_user.id))).session, - int(call.data.split('page')[1]), True)""" + create_markup(call.message, call.from_user.id, int(call.data.split('page')[1]), True) + elif 'group' in call.data: + session = VkMessage(vk_tokens.get(str(call.from_user.id))).session + chat = vk.API(session).messages.getChat(chat_id=call.data.split('group')[1], fields=[]) + bot.answer_callback_query(call.id, + 'Вы в беседе {}'.format(replace_shields(chat['title']))).wait() + if chat['title'].replace('\\', ''): + chat['title'] = chat['title'].replace('\\', '') + bot.send_message(call.message.chat.id, + 'Вы в беседе {}'.format(chat['title']), + parse_mode='HTML').wait() + currentchat[str(call.from_user.id)] = call.data + + elif call.data.isdigit(): + session = VkMessage(vk_tokens.get(str(call.from_user.id))).session + user = vk.API(session).users.get(user_ids=call.data, fields=[])[0] + bot.answer_callback_query(call.id, + 'Вы в чате с {} {}'.format(user['first_name'], user['last_name'])).wait() + bot.send_message(call.message.chat.id, + 'Вы в чате с {} {}'.format(user['first_name'], user['last_name']), + parse_mode='HTML').wait() + currentchat[str(call.from_user.id)] = call.data def create_thread(uid, vk_token): @@ -121,10 +163,11 @@ def check_thread(uid): return True -# Creating VkPolling threads after bot reboot using existing tokens +# Creating VkPolling threads and dialogs info after bot reboot using existing tokens for uid in vk_tokens.scan_iter(): if check_thread(uid.decode("utf-8")): create_thread(uid.decode("utf-8"), vk_tokens.get(uid)) + request_user_dialogs(VkMessage(vk_tokens.get(uid.decode("utf-8"))).session, uid.decode("utf-8")) def stop_thread(message): @@ -155,9 +198,11 @@ def info_extractor(info): return info -"""@bot.message_handler(commands=['dialogs']) +@bot.message_handler(commands=['dialogs']) def dialogs_command(message): - get_user_dialogs(message, VkMessage(vk_tokens.get(str(message.from_user.id))).session, 0)""" + session = VkMessage(vk_tokens.get(str(message.from_user.id))).session + request_user_dialogs(session, message.from_user.id) + create_markup(message, message.from_user.id, 0) @bot.message_handler(commands=['stop']) @@ -171,7 +216,6 @@ def stop_command(message): @bot.message_handler(commands=['start']) def start_command(message): - # TODO: Dialogs Menu if check_thread(message.from_user.id): bot.send_message(message.chat.id, 'Привет, этот бот поможет тебе общаться ВКонтакте, войди по кнопке ниже' @@ -186,15 +230,15 @@ def form_request(message, method, info): if message.text and message.text.startswith('!'): if len(message.text) - 1: message.text = message.text[1:] - if info[1] != 'None': - method(message, info[1], group=True, forward_messages=info[1]) + if info[2] != 'None': + method(message, info[1], group=True, forward_messages=info[2]) else: method(message, info[1], group=True) elif message.caption and message.caption.startswith('!'): if len(message.caption) - 1: message.caption = message.caption[1:] - if info[1] != 'None': - method(message, info[1], group=True, forward_messages=info[1]) + if info[2] != 'None': + method(message, info[1], group=True, forward_messages=info[2]) else: method(message, info[1], group=True) else: @@ -204,26 +248,44 @@ def form_request(message, method, info): if info[1] != 'None': method(message, info[0], group=False, forward_messages=info[1]) else: - method(message, info[1], group=True) + method(message, info[0], group=False) elif message.caption and message.caption.startswith('!'): if len(message.caption) - 1: message.caption = message.caption[1:] if info[1] != 'None': method(message, info[0], group=False, forward_messages=info[1]) else: - method(message, info[1], group=True) + method(message, info[0], group=False) else: method(message, info[0], group=False) +def logged(message): + if vk_tokens.get(str(message.from_user.id)): + return True + else: + bot.send_message(message.chat.id, 'Вход не выполнен! /start для входа').wait() + return False + + def vk_sender(message, method): - if message.reply_to_message: - if vk_tokens.get(str(message.from_user.id)): + if logged(message): + if message.reply_to_message: info = info_extractor(message.reply_to_message.entities) if info is not None: form_request(message, method, info) - else: - bot.send_message(message.chat.id, 'Вход не выполнен! /start для входа').wait() + + elif str(message.from_user.id) in currentchat: + info = [] + if 'group' in currentchat[str(message.from_user.id)]: + info.append('0') + info.append(currentchat[str(message.from_user.id)].split('group')[1]) + info.append('1') + else: + info.append(currentchat[str(message.from_user.id)]) + info.append('0') + info.append('0') + form_request(message, method, info) def audio_title_creator(message, performer=None, title=None): @@ -257,7 +319,7 @@ def send_doc(message, userid, group, forward_messages=None): openedfile.close() os.remove(file) - if filetype == 'voice': + elif filetype == 'voice': openedfile = open(file, 'rb') files = {'file': openedfile} fileonserver = ujson.loads(requests.post(vk.API(session).docs.getUploadServer()['upload_url'], @@ -267,7 +329,7 @@ def send_doc(message, userid, group, forward_messages=None): openedfile.close() os.remove(file) - if filetype == 'audio': + else: # filetype == 'audio': newfile = file.split('.')[0] + '.aac' os.rename(file, newfile) openedfile = open(newfile, 'rb') @@ -416,45 +478,47 @@ def send_contact(message, userid, group, forward_messages=None): def reply_document(message): try: vk_sender(message, send_doc) - except Exception as e: + except: bot.reply_to(message, 'Файл слишком большой, максимально допустимый размер *20мб*!', parse_mode='Markdown').wait() - print('Error: {}'.format(e)) @bot.message_handler(content_types=['sticker']) def reply_sticker(message): try: vk_sender(message, send_sticker) - except Exception as e: - bot.reply_to(message, 'Произошла неизвестная ошибка при отправке', - parse_mode='Markdown').wait() - print('Error: {}'.format(e)) + except Exception: + bot.reply_to(message, '*Произошла неизвестная ошибка при отправке*', + parse_mode='Markdown').wait() # TODO?: Bugreport system + print('Error: {}'.format(traceback.format_exc())) @bot.message_handler(content_types=['photo']) def reply_photo(message): try: vk_sender(message, send_photo) - except Exception as e: + except: bot.send_message(message.chat.id, 'Фото слишком большое, максимально допустимый размер *20мб*!', parse_mode='Markdown').wait() - print('Error: {}'.format(e)) @bot.message_handler(content_types=['video', 'video_note']) def reply_video(message): try: vk_sender(message, send_video) - except Exception as e: + except: bot.reply_to(message, 'Файл слишком большой, максимально допустимый размер *20мб*!', parse_mode='Markdown').wait() - print('Error: {}'.format(e)) @bot.message_handler(content_types=['contact']) def reply_contact(message): - vk_sender(message, send_contact) + try: + vk_sender(message, send_contact) + except Exception: + bot.reply_to(message, '*Произошла неизвестная ошибка при отправке*', + parse_mode='Markdown').wait() + print('Error: {}'.format(traceback.format_exc())) @bot.message_handler(content_types=['text']) @@ -468,6 +532,7 @@ def reply_text(message): verifycode(code) create_thread(message.from_user.id, code) bot.send_message(message.chat.id, 'Вход выполнен!').wait() + # ---------------- INSTRUCTIONS ---------------- # bot.send_message(message.chat.id, 'Бот позволяет получать и отвечать на текстовые сообщения' ' из ВКонтакте\nПример личного сообщения:').wait() bot.send_message(message.chat.id, '*Иван Петров:*\nПривет, я тут классный мессенджер нашёл,' @@ -480,6 +545,7 @@ def reply_text(message): bot.send_message(message.chat.id, 'Чтобы ответить, используй Reply на нужное сообщение.' ' (нет, на эти не сработает, нужно реальное)', parse_mode='Markdown').wait() + # ---------------- INSTRUCTIONS ---------------- # except: bot.send_message(message.chat.id, 'Неверная ссылка, попробуй ещё раз!').wait() else: @@ -488,13 +554,13 @@ def reply_text(message): try: vk_sender(message, send_text) - except Exception as e: + except Exception: bot.reply_to(message, 'Произошла неизвестная ошибка при отправке', parse_mode='Markdown').wait() - print('Error: {}'.format(e)) + print('Error: {}'.format(traceback.format_exc())) -# bot.polling() +# bot.polling(none_stop=True) class WebhookServer(object): # index равнозначно /, т.к. отсутствию части после ip-адреса (грубо говоря) @cherrypy.expose @@ -508,7 +574,7 @@ class WebhookServer(object): if __name__ == '__main__': bot.remove_webhook() - bot.set_webhook('https://bot.asergey.me/{}/'.format(token)) + bot.set_webhook('https://{}/{}/'.format(bot_url, token)) cherrypy.config.update( {'server.socket_host': '127.0.0.1', 'server.socket_port': local_port, 'engine.autoreload.on': False}) cherrypy.quickstart(WebhookServer(), '/', {'/': {}}) diff --git a/vk_messages.py b/vk_messages.py index 73bbaad..09de34d 100644 --- a/vk_messages.py +++ b/vk_messages.py @@ -1,10 +1,10 @@ -import vk -import time -import requests -import wget import os -import ujson import redis +import requests +import time +import traceback +import vk +import wget tokens_pool = redis.ConnectionPool(host='localhost', port=6379, db=0) vk_tokens = redis.StrictRedis(connection_pool=tokens_pool) @@ -22,11 +22,11 @@ class VkPolling: updates = [] try: updates = vk_user.get_new_messages() - except Exception as e: - print('Error: {}'.format(e)) + except Exception: + print('Error: {}'.format(traceback.format_exc())) if updates: handle_updates(vk_user, bot, chat_id, updates) - for i in range(45): + for i in range(50): if self._running: time.sleep(0.1) else: