0.0.7 fixes and graph

This commit is contained in:
tonyrewin 2023-04-22 04:21:24 +03:00
parent cbe9db4932
commit 297445fd50
8 changed files with 140 additions and 21 deletions

View File

@ -1,3 +1,9 @@
## [0.0.7]
- исправления
- команда, генерирующая граф связей
## [0.0.6]
- совместимость с механизмом заявок для публичных групп

View File

@ -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)

View File

@ -1,3 +1,4 @@
sanic==19.6.0
requests
redis
redis
cairosvg

View File

@ -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
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()

View File

@ -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")

64
tgbot/graph.py Normal file
View File

@ -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'<line x1="{x1}" y1="{y1}" x2="{x2}" y2="{y2}" stroke="{line_color}" stroke-width="2"/>')
# Рисуем узлы
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'<rect x="{x}" y="{y}" width="{node_width}" height="{node_height}" rx="{node_radius}" fill="{node_color}" stroke="{node_stroke_color}" stroke-width="2"/>')
# Добавляем текст в центр узла
member_name = member['name'][:16]
text_x = x + node_width / 2
text_y = y + node_height / 2
svg_lines.append(f'<text x="{text_x}" y="{text_y}" text-anchor="middle" dominant-baseline="central" font-size="16" fill="{node_text_color}">{member_name}</text>')
# Создаем SVG-код
svg = f'<svg viewBox="0 0 {canvas_width} {canvas_height}" xmlns="http://www.w3.org/2000/svg" style="background-color:{background_color};">'
for line in svg_lines:
svg += line
svg += '</svg>'
# конвертировать SVG в PNG
png_data = cairosvg.svg2png(bytestring=svg_data)
return png_data

View File

@ -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)
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())

View File

@ -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: