diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8eb829a..a61cd88 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+## [0.0.7]
+
+- исправления
+- команда, генерирующая граф связей
+
+
## [0.0.6]
- совместимость с механизмом заявок для публичных групп
diff --git a/api/webhook.py b/api/webhook.py
index 485fba3..15f2b84 100644
--- a/api/webhook.py
+++ b/api/webhook.py
@@ -1,6 +1,7 @@
from tgbot.config import WEBHOOK, FEEDBACK_CHAT_ID # init storage there
from tgbot.handlers import handle_feedback, handle_answer, \
- handle_join, handle_left, handle_button, handle_join_request
+ handle_join, handle_left, handle_button, handle_join_request, \
+ handle_graph
from tgbot.api import register_webhook
from sanic import Sanic
from sanic.response import text
@@ -31,9 +32,11 @@ async def handle(req):
msg = update.get('message', update.get('edited_message'))
if msg['chat']['type'] == 'private':
handle_feedback(msg)
- elif str(msg['chat']['id']) == FEEDBACK_CHAT_ID \
- and 'reply_to_message' in 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':
+ handle_graph(msg)
else:
if 'new_chat_member' in msg:
handle_join(msg)
diff --git a/requirements.txt b/requirements.txt
index 794dc30..db1d882 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,3 +1,4 @@
sanic==19.6.0
requests
-redis
\ No newline at end of file
+redis
+cairosvg
\ No newline at end of file
diff --git a/tgbot/api.py b/tgbot/api.py
index 2023f38..58aeb49 100644
--- a/tgbot/api.py
+++ b/tgbot/api.py
@@ -85,4 +85,13 @@ def approve_chat_join_request(chat_id, user_id):
url = apiBase + f"approveChatJoinRequest?chat_id={chat_id}" + \
f'&user_id={user_id}'
r = requests.post(url)
- return r
\ No newline at end of file
+ return r
+
+
+def send_graph(png_data, chat_id):
+ url = apiBase + f"sendPhoto"
+ headers = {"Content-Type": "multipart/form-data"}
+ files = {"photo": ("chart.png", png_data)}
+ params = {"chat_id": chat_id}
+ response = requests.post(url, headers=headers, files=files, params=params)
+ return response.json()
\ No newline at end of file
diff --git a/tgbot/config.py b/tgbot/config.py
index b942a8f..fd6d6da 100644
--- a/tgbot/config.py
+++ b/tgbot/config.py
@@ -3,6 +3,6 @@ import os
WEBHOOK = os.environ.get('VERCEL_URL') or 'http://localhost:8000'
REDIS_URL = os.environ.get('REDIS_URL') or 'redis://localhost:6379'
-NEWCOMER_MSG = os.environ.get('WELCOME_MSG') or "There is a newcomer, press the button if you are connected"
+NEWCOMER_MSG = os.environ.get('NEWCOMER_MSG') or "There is a newcomer, press the button if you are connected"
BUTTON_VOUCH = os.environ.get('BUTTON_VOUCH') or 'My connection!'
FEEDBACK_CHAT_ID = os.environ.get('FEEDBACK_CHAT_ID').replace("-", "-100")
\ No newline at end of file
diff --git a/tgbot/graph.py b/tgbot/graph.py
new file mode 100644
index 0000000..bb165b4
--- /dev/null
+++ b/tgbot/graph.py
@@ -0,0 +1,64 @@
+import cairosvg
+
+def generate_chart(members):
+ # Размеры прямоугольника узла
+ node_width = 150
+ node_height = 50
+
+ # Размеры холста
+ canvas_width = 800
+ canvas_height = 600
+
+ # Радиус узла (для закругленных прямоугольников)
+ node_radius = 10
+
+ # Цвета
+ background_color = "#F2F2F2"
+ node_color = "#EFEFEF"
+ node_stroke_color = "#999"
+ node_text_color = "#333"
+ line_color = "#CCC"
+
+ # Список строк SVG-кода
+ svg_lines = []
+
+ # Рассчитываем координаты для каждого узла
+ coordinates = {}
+ for member in members:
+ x = member['x']
+ y = member['y']
+ coordinates[member['id']] = {'x': x, 'y': y}
+
+ # Рисуем линии-связи между узлами
+ for member in members:
+ member_id = member['id']
+ x1 = coordinates[member_id]['x'] * node_width + node_width / 2
+ y1 = coordinates[member_id]['y'] * node_height + node_height / 2
+ for parent_id in member['parents']:
+ x2 = coordinates[parent_id]['x'] * node_width + node_width / 2
+ y2 = coordinates[parent_id]['y'] * node_height + node_height / 2
+ svg_lines.append(f'')
+
+ # Рисуем узлы
+ for member in members:
+ member_id = member['id']
+ x = coordinates[member_id]['x'] * node_width
+ y = coordinates[member_id]['y'] * node_height
+
+ # Рисуем фоновый прямоугольник
+ svg_lines.append(f'')
+
+ # Добавляем текст в центр узла
+ member_name = member['name'][:16]
+ text_x = x + node_width / 2
+ text_y = y + node_height / 2
+ svg_lines.append(f'{member_name}')
+
+ # Создаем SVG-код
+ svg = f''
+ # конвертировать SVG в PNG
+ png_data = cairosvg.svg2png(bytestring=svg_data)
+ return png_data
diff --git a/tgbot/handlers.py b/tgbot/handlers.py
index 86eab72..a39f518 100644
--- a/tgbot/handlers.py
+++ b/tgbot/handlers.py
@@ -1,6 +1,7 @@
from tgbot.api import send_message, forward_message, delete_message, \
ban_member, unban_member, mute_member, unmute_member, \
- approve_chat_join_request
+ approve_chat_join_request, send_graph
+from tgbot.graph import generate_chart
from tgbot.config import REDIS_URL, FEEDBACK_CHAT_ID, BUTTON_VOUCH, NEWCOMER_MSG
import json
import redis
@@ -13,7 +14,7 @@ storage = redis.from_url(REDIS_URL)
# хранение необходимой информации о пользователях
Profile = ProfileObj(storage)
-def show_request_msg(msg):
+def newcomer_show(msg):
reply_markup = {
"inline_keyboard": [
[
@@ -24,10 +25,12 @@ def show_request_msg(msg):
]
]
}
-
+ identity = f"{msg['from']['first_name']} {msg['from'].get('last_name', '')}"
+ if 'username' in msg['from']:
+ identity += f" @{msg['from']['username']}"
r = send_message(
msg['chat']['id'],
- NEWCOMER_MSG + f"{msg['from']['first_name']} {msg['from']['last_name']}({msg['from']['username']})",
+ NEWCOMER_MSG + identity,
reply_to=msg['message_id'],
reply_markup=reply_markup
)
@@ -67,11 +70,15 @@ def handle_join(msg):
actor["name"] = msg['from']['first_name'] + msg['from'].get('last_name', '')
actor["mention"] = msg['from'].get('username', '')
-
- if from_id == str(msg['new_chat_member']['id']):
+ newcomer_id = str(msg['new_chat_member']['id'])
+ if from_id == newcomer_id:
if len(actor['parents']) == 0:
# показываем сообщение с кнопкой "поручиться"
- welcome_msg_id = show_request_msg(msg)
+ welcome_msg_id = newcomer_show(msg)
+
+ # до одобрения - мьют
+ r = mute_member(chat_id, newcomer_id)
+ print(r.json())
# обновляем профиль присоединившегося
actor['welcome_id'] = welcome_msg_id
@@ -88,6 +95,8 @@ def handle_join(msg):
newcomer['parents'].append(from_id)
Profile.save(newcomer)
actor['children'].append(m['id'])
+ r = unmute_member(chat_id, newcomer['id'])
+ print(r.json())
# обновляем профиль пригласившего
Profile.save(actor)
@@ -118,7 +127,7 @@ def handle_button(callback_query):
newcomer_id = callback_data[len(BUTTON_VOUCH):]
newcomer = Profile.get(newcomer_id)
- if newcomer_id not in inviter['children'] and \
+ if newcomer_id not in actor['children'] and \
actor_id not in newcomer['parents']:
newcomer['parents'].append(newcomer_id)
actor['children'].append(actor_id)
@@ -127,13 +136,14 @@ def handle_button(callback_query):
try:
chat_id = str(callback_query['message']['chat']['id'])
- print('accept join request for public chat')
- r = approve_chat_join_request(chat_id, newcomer_id)
- print(r.json())
-
print('unmute newcomer')
r = unmute_member(chat_id, newcomer_id)
print(r.json())
+
+ print('accept join request')
+ r = approve_chat_join_request(chat_id, newcomer_id)
+ print(r.json())
+
except:
pass
@@ -150,4 +160,26 @@ def handle_join_request(update):
if from_id == str(update['message']['new_chat_member']['id']):
if len(actor['parents']) == 0:
# показываем сообщение с кнопкой "поручиться"
- welcome_msg_id = show_request_msg(update)
\ No newline at end of file
+ welcome_msg_id = show_request_msg(update)
+
+
+def handle_graph(_msg):
+ cursor = 0
+ keys = []
+ while True:
+ # Scan for keys starting with 'urs-*' in batches of 100
+ cursor, batch_keys = r.scan(cursor=cursor, match='urs-*', count=100)
+ keys += batch_keys
+ # If the cursor is 0, then we've reached the end of the keys
+ if cursor == 0:
+ break
+ # Get the values of all the keys
+ values = r.mget(keys)
+ # Parse the JSON data from each value
+ members = []
+ for value in values:
+ member = json.loads(value)
+ members.append(member)
+ png_data = generate_chart(values)
+ r = send_graph(png_data, chat_id)
+ print(r.json())
diff --git a/tgbot/profile.py b/tgbot/profile.py
index a64a675..0339bf8 100644
--- a/tgbot/profile.py
+++ b/tgbot/profile.py
@@ -19,10 +19,14 @@ class Profile:
return s
def save(self, s):
- self.storage.set(f'usr-{member_id}', json.dumps(s))
+ self.storage.set(f'usr-{s["id"]}', json.dumps(s))
def get(self, member_id):
- return json.loads(self.storage.get(f'usr-{member_id}')) or self.create_session(member_id)
+ data = self.storage.get(f'usr-{member_id}')
+ if data is None:
+ return self.create(member_id)
+ else:
+ return json.loads(data)
def leaving(self, s):
if len(s['parents']) == 0: