diff --git a/api/webhook.py b/api/webhook.py index 70c4e8e..fe1746c 100644 --- a/api/webhook.py +++ b/api/webhook.py @@ -1,7 +1,7 @@ from sanic import Sanic from sanic.response import text -from tgbot.config import WEBHOOK, FEEDBACK_CHAT_ID +from tgbot.config import WEBHOOK, FEEDBACK_CHAT_ID, BUTTON_VOUCH from tgbot.handlers.handle_feedback import handle_feedback, handle_answer from tgbot.handlers.handle_members_change import handle_join, handle_left @@ -12,7 +12,7 @@ from tgbot.handlers.command_graph import handle_command_graph from tgbot.handlers.callback_vouch import handle_button from tgbot.handlers.callback_unlink import handle_unlink -from tgbot.api import register_webhook +from tgbot.api import register_webhook, send_message app = Sanic(name="welcomecenter") @@ -41,22 +41,25 @@ async def handle(req): # видимые сообщения msg = update.get('message', update.get('edited_message')) if msg: - if msg['chat']['id'] == msg['from']['id']: - if msg['text'] == '/my': - handle_command_my(msg) + if 'text' in msg: + if msg['chat']['id'] == msg['from']['id']: + if msg['text'] == '/my': + handle_command_my(msg) + else: + handle_feedback(msg) + elif str(msg['chat']['id']) == FEEDBACK_CHAT_ID: + if 'reply_to_message' in msg: + handle_answer(msg) + elif msg['text'] == '/graph': + await handle_command_graph(msg) else: - handle_feedback(msg) - elif str(msg['chat']['id']) == FEEDBACK_CHAT_ID: - if 'reply_to_message' in msg: - handle_answer(msg) - elif 'text' in msg and msg['text'] == '/graph': - await handle_command_graph(msg) + handle_default(msg) elif 'new_chat_member' in msg: handle_join(msg) elif 'left_chat_member' in msg: handle_left(msg) else: - handle_default(msg) + print('message without text') # кнопки elif 'callback_query' in update: @@ -78,5 +81,6 @@ async def handle(req): except Exception: import traceback + r = send_message(FEEDBACK_CHAT_ID, f'
{traceback.format_exc()}') traceback.print_exc() return text('ok') diff --git a/requirements.txt b/requirements.txt index 56f79e1..9d3ddd8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -sanic +sanic==19.6.0 requests redis aiohttp \ No newline at end of file diff --git a/tgbot/api.py b/tgbot/api.py index 045757d..4b3c7d4 100644 --- a/tgbot/api.py +++ b/tgbot/api.py @@ -96,9 +96,10 @@ def approve_chat_join_request(chat_id, user_id): async def send_document(chat_id, data='', filename='chart.svg'): url = apiBase + "sendDocument" params = { "chat_id": chat_id } - files = { "document": data } + filedata = aiohttp.FormData() + filedata.add_field('document', data, filename=filename) - async with aiohttp.ClientSession(headers=headers) as session: + async with aiohttp.ClientSession() as session: async with session.post(url, params=params, data=filedata) as response: if response.status != 200: error_text = await response.text() @@ -114,18 +115,20 @@ async def send_document(chat_id, data='', filename='chart.svg'): # https://core.telegram.org/bots/api#sendphoto -async def send_photo(png_data, chat_id): +async def send_photo(chat_id, img_data, filename='chart.png'): url = apiBase + f"sendPhoto" - headers = {"Content-Type": "multipart/form-data"} - files = {"photo": ("chart.png", png_data)} - params = {"chat_id": chat_id} + params = {"chat_id": chat_id } + filedata = aiohttp.FormData() + filedata.add_field('photo', img_data, filename=filename) - async with aiohttp.ClientSession(headers=headers) as session: + async with aiohttp.ClientSession() as session: async with session.post(url, params=params, data=filedata) as response: if response.status != 200: - print(f"Error sending photo: {response.status}") + error_text = await response.text() + print(f"Error sending photo: {response.status} - {error_text}") return None + try: return await response.json() except ValueError as e: diff --git a/tgbot/handlers/callback_vouch.py b/tgbot/handlers/callback_vouch.py index 91feaa6..82f615d 100644 --- a/tgbot/handlers/callback_vouch.py +++ b/tgbot/handlers/callback_vouch.py @@ -1,5 +1,6 @@ from tgbot.api import send_message, forward_message, delete_message from tgbot.storage import Profile +from tgbot.config import BUTTON_VOUCH def handle_button(callback_query): diff --git a/tgbot/handlers/command_graph.py b/tgbot/handlers/command_graph.py index d757cfb..516225f 100644 --- a/tgbot/handlers/command_graph.py +++ b/tgbot/handlers/command_graph.py @@ -25,5 +25,6 @@ async def handle_command_graph(msg): members.append(member) print(f'found {len(members)} members') data = generate_chart(members) - r = await send_document(msg['chat']['id'], data, 'chart.svg') - print(r) + if data: + r = await send_document(msg['chat']['id'], data, 'chart.svg') + print(r) diff --git a/tgbot/handlers/command_my.py b/tgbot/handlers/command_my.py index 25fe850..b8df0fe 100644 --- a/tgbot/handlers/command_my.py +++ b/tgbot/handlers/command_my.py @@ -1,5 +1,5 @@ from tgbot.storage import Profile -from tgbot.api import get_member +from tgbot.api import get_member, send_message def construct_unlink_buttons(actor): buttons = [] diff --git a/tgbot/handlers/handle_default.py b/tgbot/handlers/handle_default.py index 9dca22b..be28c74 100644 --- a/tgbot/handlers/handle_default.py +++ b/tgbot/handlers/handle_default.py @@ -1,7 +1,7 @@ from tgbot.api import send_message, delete_message, get_chat_administrators from tgbot.storage import Profile from tgbot.handlers.send_button import show_request_msg - +from tgbot.storage import storage def handle_default(msg): print(f'default handler for all messages') @@ -18,14 +18,14 @@ def handle_default(msg): print(r) # удалить предыдушее сообщение с кнопкой в этом чате - if sender['request_msg_id'].startswith(chat_id): - chat_id, rmid = sender['request_msg_id'].split(':') - r = delete_message(chat_id, rmid) + prev_msg_id = storage.get(f'btn-{chat_id}-{from_id}') + if prev_msg_id: + r = delete_message(chat_id, prev_msg_id) print(r) # показать новое сообщение с кнопкой - request_msg_id = show_request_msg(msg) - sender['request_msg_id'] = f'{chat_id}:{request_msg_id}' + btn_msg_id = show_request_msg(msg) + storage.set(f'btn-{chat_id}-{from_id}', btn_msg_id) else: # любое другое сообщение if len(sender['parents']) == 0: @@ -33,11 +33,13 @@ def handle_default(msg): print(f'setting owner as parent for {from_id}') r = get_chat_administrators(chat_id) print(r) + owner_id = '' + for admin in r['result']: + if admin['status'] == 'creator': + owner_id = admin['user']['id'] + break - owner_id = r['result'][0]['id'] # DEBUG!! - sender['parents'].append(owner_id) - # обновляем профиль владельца owner = Profile.get(owner_id) owner['children'].append(from_id) diff --git a/tgbot/handlers/handle_feedback.py b/tgbot/handlers/handle_feedback.py index 3b8a79e..6229fd6 100644 --- a/tgbot/handlers/handle_feedback.py +++ b/tgbot/handlers/handle_feedback.py @@ -19,11 +19,14 @@ def handle_feedback(msg): def handle_answer(msg): - print(f'handle answer from support') - support_msg_id = str(msg['reply_to_message']['message_id']) - # получение сохраненного айди сообщения из личной переписки с ботом - stored_feedback = storage.get(f'fbk-{support_msg_id}') - stored_feedback = json.loads(stored_feedback) - r = send_message(f'{stored_feedback["chat_id"]}', msg['text'], reply_to=stored_feedback["message_id"]) - print(r) + answered_msg = msg['reply_to_message'] + if answered_msg['from']['is_bot']: + support_msg_id = str(answered_msg['message_id']) + # получение сохраненного информации о сообщении для ответа + stored_feedback = storage.get(f'fbk-{support_msg_id}') + if stored_feedback: + print(f'handle answer from support') + stored_feedback = json.loads(stored_feedback) + r = send_message(f'{stored_feedback["chat_id"]}', msg['text'], reply_to=stored_feedback["message_id"]) + print(r) diff --git a/tgbot/handlers/handle_join_request.py b/tgbot/handlers/handle_join_request.py index ca60650..8c5ed07 100644 --- a/tgbot/handlers/handle_join_request.py +++ b/tgbot/handlers/handle_join_request.py @@ -1,6 +1,6 @@ from tgbot.api import approve_chat_join_request, delete_message from tgbot.handlers.send_button import show_request_msg -from tgbot.storage import Profile +from tgbot.storage import Profile, storage def handle_join_request(msg): @@ -11,13 +11,13 @@ def handle_join_request(msg): if len(actor['parents']) == 0: # показываем сообщение с кнопкой "поручиться" - request_msg_id = show_request_msg(msg) + btn_msg_id = show_request_msg(msg) # удаляем предыдущее сообщение с кнопкой в этом чате - if actor['request_msg_id'].startswith(chat_id): - chat_id, rmid = actor['request_msg_id'].split(':') - r = delete_message(chat_id, rmid) + prev_msg_id = storage.get(f'btn-{chat_id}-{from_id}') + if prev_msg_id: + r = delete_message(chat_id, prev_msg_id) print(r) - actor['request_msg_id'] = f'{chat_id}:{request_msg_id}' + storage.set(f'btn-{chat_id}-{from_id}', btn_msg_id) else: # за пользователя поручились ранее r = approve_chat_join_request(chat_id, from_id) diff --git a/tgbot/handlers/handle_members_change.py b/tgbot/handlers/handle_members_change.py index 85f0412..6d13ddb 100644 --- a/tgbot/handlers/handle_members_change.py +++ b/tgbot/handlers/handle_members_change.py @@ -1,6 +1,6 @@ from tgbot.handlers.send_button import show_request_msg from tgbot.api import unmute_member, mute_member, delete_message -from tgbot.storage import Profile +from tgbot.storage import Profile, storage def handle_join(msg): chat_id = str(msg['chat']['id']) @@ -12,15 +12,13 @@ def handle_join(msg): if from_id == newcomer_id: if len(actor['parents']) == 0: # показываем сообщение с кнопкой "поручиться" - request_msg_id = show_request_msg(msg) + btn_msg_id = show_request_msg(msg) + storage.set(f'btn-{chat_id}-{from_id}', btn_msg_id) # до одобрения - мьют r = mute_member(chat_id, newcomer_id) print(r) - # обновляем профиль присоединившегося - actor['request_msg_id'] = f'{chat_id}:{request_msg_id}' - Profile.save(actor) else: # за пользователя поручились ранее pass @@ -44,14 +42,11 @@ def handle_left(msg): member_id = msg["left_chat_member"]["id"] chat_id = msg['chat']['id'] - # профиль покидающего чат - leaver = Profile.get(member_id) - # удаление сообщения с кнопкой в этом чате - if leaver['request_msg_id'].startswith(chat_id): - chat_id, rmid = leaver['request_msg_id'].split(':') - r = delete_message(chat_id, rmid) + prev_msg_id = storage.get(f'btn-{chat_id}-{member_id}') + if prev_msg_id: + r = delete_message(chat_id, prev_msg_id) print(r) + storage.remove(f'btn-{chat_id}-{member_id}') - Profile.leaving(leaver) diff --git a/tgbot/handlers/send_button.py b/tgbot/handlers/send_button.py index a6a20ac..b847272 100644 --- a/tgbot/handlers/send_button.py +++ b/tgbot/handlers/send_button.py @@ -1,6 +1,6 @@ from tgbot.api import send_message from tgbot.config import BUTTON_VOUCH, NEWCOMER_MSG -from tgbot.utils import mention +from tgbot.utils.mention import mention def show_request_msg(msg): reply_markup = { @@ -20,8 +20,6 @@ def show_request_msg(msg): reply_to=msg.get('message_id', ''), reply_markup=reply_markup ) - request_msg_id = r['result']['message_id'] - chat_id = msg['chat']['id'] - print(r) - print(f'request message id: {chat_id}:{request_msg_id}') - return request_msg_id + btn_msg_id = r['result']['message_id'] + print(f'request message id: {btn_msg_id}') + return btn_msg_id diff --git a/tgbot/storage/__init__.py b/tgbot/storage/__init__.py index 9aba69f..c82adfa 100644 --- a/tgbot/storage/__init__.py +++ b/tgbot/storage/__init__.py @@ -2,7 +2,7 @@ import redis from tgbot.storage.profile import Profile as ProfileObj from tgbot.config import REDIS_URL -# сохраняет сессии и пересылаемые сообщения между перезагрузками +# сохраняет сессии, айди кнопок в чатах для удаления и пересылаемые сообщения между перезагрузками storage = redis.from_url(REDIS_URL) # хранение необходимой информации о пользователях diff --git a/tgbot/storage/profile.py b/tgbot/storage/profile.py index 5b0fc77..2099180 100644 --- a/tgbot/storage/profile.py +++ b/tgbot/storage/profile.py @@ -9,7 +9,6 @@ class Profile: def create(self, member_id, msg=None): s = { "id": member_id, - "request_msg_id": 0, "parents": [], "children": [], "chats": [] @@ -38,7 +37,3 @@ class Profile: else: r = json.loads(data) return r - - def leaving(self, s): - if len(s['parents']) == 0: - self.storage.delete(f'usr-{s["id"]}')