0.0.7 fixes and graph
This commit is contained in:
@@ -1,3 +1,9 @@
|
|||||||
|
## [0.0.7]
|
||||||
|
|
||||||
|
- исправления
|
||||||
|
- команда, генерирующая граф связей
|
||||||
|
|
||||||
|
|
||||||
## [0.0.6]
|
## [0.0.6]
|
||||||
|
|
||||||
- совместимость с механизмом заявок для публичных групп
|
- совместимость с механизмом заявок для публичных групп
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
from tgbot.config import WEBHOOK, FEEDBACK_CHAT_ID # init storage there
|
from tgbot.config import WEBHOOK, FEEDBACK_CHAT_ID # init storage there
|
||||||
from tgbot.handlers import handle_feedback, handle_answer, \
|
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 tgbot.api import register_webhook
|
||||||
from sanic import Sanic
|
from sanic import Sanic
|
||||||
from sanic.response import text
|
from sanic.response import text
|
||||||
@@ -31,9 +32,11 @@ async def handle(req):
|
|||||||
msg = update.get('message', update.get('edited_message'))
|
msg = update.get('message', update.get('edited_message'))
|
||||||
if msg['chat']['type'] == 'private':
|
if msg['chat']['type'] == 'private':
|
||||||
handle_feedback(msg)
|
handle_feedback(msg)
|
||||||
elif str(msg['chat']['id']) == FEEDBACK_CHAT_ID \
|
elif str(msg['chat']['id']) == FEEDBACK_CHAT_ID:
|
||||||
and 'reply_to_message' in msg:
|
if 'reply_to_message' in msg:
|
||||||
handle_answer(msg)
|
handle_answer(msg)
|
||||||
|
elif 'text' in msg and msg['text'] == '/graph':
|
||||||
|
handle_graph(msg)
|
||||||
else:
|
else:
|
||||||
if 'new_chat_member' in msg:
|
if 'new_chat_member' in msg:
|
||||||
handle_join(msg)
|
handle_join(msg)
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
sanic==19.6.0
|
sanic==19.6.0
|
||||||
requests
|
requests
|
||||||
redis
|
redis
|
||||||
|
cairosvg
|
11
tgbot/api.py
11
tgbot/api.py
@@ -85,4 +85,13 @@ def approve_chat_join_request(chat_id, user_id):
|
|||||||
url = apiBase + f"approveChatJoinRequest?chat_id={chat_id}" + \
|
url = apiBase + f"approveChatJoinRequest?chat_id={chat_id}" + \
|
||||||
f'&user_id={user_id}'
|
f'&user_id={user_id}'
|
||||||
r = requests.post(url)
|
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()
|
@@ -3,6 +3,6 @@ import os
|
|||||||
|
|
||||||
WEBHOOK = os.environ.get('VERCEL_URL') or 'http://localhost:8000'
|
WEBHOOK = os.environ.get('VERCEL_URL') or 'http://localhost:8000'
|
||||||
REDIS_URL = os.environ.get('REDIS_URL') or 'redis://localhost:6379'
|
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!'
|
BUTTON_VOUCH = os.environ.get('BUTTON_VOUCH') or 'My connection!'
|
||||||
FEEDBACK_CHAT_ID = os.environ.get('FEEDBACK_CHAT_ID').replace("-", "-100")
|
FEEDBACK_CHAT_ID = os.environ.get('FEEDBACK_CHAT_ID').replace("-", "-100")
|
64
tgbot/graph.py
Normal file
64
tgbot/graph.py
Normal 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
|
@@ -1,6 +1,7 @@
|
|||||||
from tgbot.api import send_message, forward_message, delete_message, \
|
from tgbot.api import send_message, forward_message, delete_message, \
|
||||||
ban_member, unban_member, mute_member, unmute_member, \
|
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
|
from tgbot.config import REDIS_URL, FEEDBACK_CHAT_ID, BUTTON_VOUCH, NEWCOMER_MSG
|
||||||
import json
|
import json
|
||||||
import redis
|
import redis
|
||||||
@@ -13,7 +14,7 @@ storage = redis.from_url(REDIS_URL)
|
|||||||
# хранение необходимой информации о пользователях
|
# хранение необходимой информации о пользователях
|
||||||
Profile = ProfileObj(storage)
|
Profile = ProfileObj(storage)
|
||||||
|
|
||||||
def show_request_msg(msg):
|
def newcomer_show(msg):
|
||||||
reply_markup = {
|
reply_markup = {
|
||||||
"inline_keyboard": [
|
"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(
|
r = send_message(
|
||||||
msg['chat']['id'],
|
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_to=msg['message_id'],
|
||||||
reply_markup=reply_markup
|
reply_markup=reply_markup
|
||||||
)
|
)
|
||||||
@@ -67,11 +70,15 @@ def handle_join(msg):
|
|||||||
|
|
||||||
actor["name"] = msg['from']['first_name'] + msg['from'].get('last_name', '')
|
actor["name"] = msg['from']['first_name'] + msg['from'].get('last_name', '')
|
||||||
actor["mention"] = msg['from'].get('username', '')
|
actor["mention"] = msg['from'].get('username', '')
|
||||||
|
newcomer_id = str(msg['new_chat_member']['id'])
|
||||||
if from_id == str(msg['new_chat_member']['id']):
|
if from_id == newcomer_id:
|
||||||
if len(actor['parents']) == 0:
|
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
|
actor['welcome_id'] = welcome_msg_id
|
||||||
@@ -88,6 +95,8 @@ def handle_join(msg):
|
|||||||
newcomer['parents'].append(from_id)
|
newcomer['parents'].append(from_id)
|
||||||
Profile.save(newcomer)
|
Profile.save(newcomer)
|
||||||
actor['children'].append(m['id'])
|
actor['children'].append(m['id'])
|
||||||
|
r = unmute_member(chat_id, newcomer['id'])
|
||||||
|
print(r.json())
|
||||||
|
|
||||||
# обновляем профиль пригласившего
|
# обновляем профиль пригласившего
|
||||||
Profile.save(actor)
|
Profile.save(actor)
|
||||||
@@ -118,7 +127,7 @@ def handle_button(callback_query):
|
|||||||
|
|
||||||
newcomer_id = callback_data[len(BUTTON_VOUCH):]
|
newcomer_id = callback_data[len(BUTTON_VOUCH):]
|
||||||
newcomer = Profile.get(newcomer_id)
|
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']:
|
actor_id not in newcomer['parents']:
|
||||||
newcomer['parents'].append(newcomer_id)
|
newcomer['parents'].append(newcomer_id)
|
||||||
actor['children'].append(actor_id)
|
actor['children'].append(actor_id)
|
||||||
@@ -127,13 +136,14 @@ def handle_button(callback_query):
|
|||||||
try:
|
try:
|
||||||
chat_id = str(callback_query['message']['chat']['id'])
|
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')
|
print('unmute newcomer')
|
||||||
r = unmute_member(chat_id, newcomer_id)
|
r = unmute_member(chat_id, newcomer_id)
|
||||||
print(r.json())
|
print(r.json())
|
||||||
|
|
||||||
|
print('accept join request')
|
||||||
|
r = approve_chat_join_request(chat_id, newcomer_id)
|
||||||
|
print(r.json())
|
||||||
|
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -150,4 +160,26 @@ def handle_join_request(update):
|
|||||||
if from_id == str(update['message']['new_chat_member']['id']):
|
if from_id == str(update['message']['new_chat_member']['id']):
|
||||||
if len(actor['parents']) == 0:
|
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())
|
||||||
|
@@ -19,10 +19,14 @@ class Profile:
|
|||||||
return s
|
return s
|
||||||
|
|
||||||
def save(self, 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):
|
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):
|
def leaving(self, s):
|
||||||
if len(s['parents']) == 0:
|
if len(s['parents']) == 0:
|
||||||
|
Reference in New Issue
Block a user