1014 lines
45 KiB
Python
1014 lines
45 KiB
Python
from googleapiclient.discovery import build
|
||
from google.oauth2.service_account import Credentials
|
||
import asyncio
|
||
import logging
|
||
from aiogram.types import ChatType
|
||
from aiogram.utils.exceptions import BadRequest
|
||
from aiogram.utils.exceptions import MessageCantBeDeleted
|
||
import gspread
|
||
from google.oauth2 import service_account
|
||
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton, WebAppInfo, ReplyKeyboardMarkup, KeyboardButton
|
||
import sentry_sdk
|
||
from aiogram import Bot, Dispatcher, types
|
||
from aiogram.contrib.fsm_storage.memory import MemoryStorage
|
||
from aiogram.utils import executor
|
||
from aiogram.dispatcher import FSMContext
|
||
from aiogram.dispatcher.filters.state import StatesGroup, State
|
||
import subprocess
|
||
from googleapiclient.discovery import build
|
||
from googleapiclient.errors import HttpError
|
||
import os
|
||
import json
|
||
from googleapiclient.discovery import build
|
||
from googleapiclient.errors import HttpError
|
||
from datetime import datetime
|
||
import subprocess
|
||
from aiogram import types
|
||
from aiogram.dispatcher import filters
|
||
from aiogram.types import Message
|
||
from datetime import datetime, timedelta
|
||
|
||
|
||
|
||
|
||
|
||
|
||
sentry_sdk.init(
|
||
dsn="https://697d3f2b6cb54a78202eeae0cb8c1f65@sentry.pirogov.ai/15",
|
||
# Set traces_sample_rate to 1.0 to capture 100%
|
||
# of transactions for performance monitoring.
|
||
traces_sample_rate=1.0,
|
||
)
|
||
|
||
|
||
# Настройки бота
|
||
API_TOKEN = '7414018470:AAGYF77aCd1BQpRnf38ys05ijkYJ25sNBuU'
|
||
ADMIN_ID = 5522111920 # ID администратора, которому будет приходить уведомление
|
||
ADMIN_IDS = [5522111920, 766945900] # Список ID администраторов
|
||
SPREADSHEET_ID = '1j12H6NCZec9MEWQypPbzhETNtn58fnin1HBRkkr3a1w'
|
||
SHEET_NAME_GROUPS = 'Лист2'
|
||
SHEET_NAME_EMPLOYEES = 'Лист1'
|
||
RANGE_NAME = 'Лист1!A1:O' # Диапазон данных в таблице
|
||
|
||
# Логирование
|
||
logging.basicConfig(level=logging.INFO)
|
||
|
||
bot = Bot(token=API_TOKEN, timeout=30)
|
||
# Инициализация диспетчера и хранилища состояний
|
||
storage = MemoryStorage()
|
||
dp = Dispatcher(bot, storage=storage)
|
||
|
||
# Настройки для Google Sheets API
|
||
SCOPES = ['https://www.googleapis.com/auth/spreadsheets']
|
||
SERVICE_ACCOUNT_FILE = 'botforclinic-436512-0c117dd103a8.json'
|
||
|
||
credentials = Credentials.from_service_account_file(SERVICE_ACCOUNT_FILE, scopes=SCOPES)
|
||
service = build('sheets', 'v4', credentials=credentials)
|
||
|
||
# Настройка API для работы с документами
|
||
SCOPES = ['https://www.googleapis.com/auth/documents', 'https://www.googleapis.com/auth/drive']
|
||
SERVICE_ACCOUNT_FILE = 'botforclinic-436512-0c117dd103a8.json'
|
||
|
||
credentials = service_account.Credentials.from_service_account_file(
|
||
SERVICE_ACCOUNT_FILE, scopes=SCOPES)
|
||
|
||
docs_service = build('docs', 'v1', credentials=credentials)
|
||
drive_service = build('drive', 'v3', credentials=credentials)
|
||
|
||
# Идентификатор Google Документа (шаблона трудового договора)
|
||
TEMPLATE_DOC_ID = '1xi4YiPNBEDvODS0SaVb0wgKZ_b_OBkas'
|
||
|
||
|
||
CHANGES_FILE = r"C:\Users\ilyac\PycharmProjects\Bot_for_clinic\sheets_backup\changes.json"
|
||
|
||
|
||
logging.basicConfig(level=logging.INFO)
|
||
|
||
|
||
# Google API настройки
|
||
SCOPES = ['https://www.googleapis.com/auth/spreadsheets', 'https://www.googleapis.com/auth/drive']
|
||
SERVICE_ACCOUNT_FILE = 'botforclinic-436512-0c117dd103a8.json'
|
||
|
||
credentials = Credentials.from_service_account_file(SERVICE_ACCOUNT_FILE, scopes=SCOPES)
|
||
sheets_service = build('sheets', 'v4', credentials=credentials)
|
||
|
||
SPREADSHEET_NAME = "Корпоративные группы Клиники в Телеграмм"
|
||
|
||
|
||
def load_changes():
|
||
file_path = r"C:\Users\ilyac\PycharmProjects\Bot_for_clinic\sheets_backup\changes.json" # Укажите ваш путь к JSON-файлу
|
||
if not os.path.exists(file_path):
|
||
raise FileNotFoundError(f"Файл {file_path} не найден.")
|
||
|
||
with open(file_path, 'r', encoding='utf-8') as f:
|
||
content = f.read().strip() # Читаем и удаляем лишние пробелы
|
||
if not content:
|
||
raise ValueError(f"Файл {file_path} пуст.")
|
||
|
||
try:
|
||
return json.loads(content) # Используем json.loads для строки
|
||
except json.JSONDecodeError as e:
|
||
raise ValueError(f"Ошибка декодирования JSON в файле {file_path}: {e}")
|
||
|
||
|
||
|
||
|
||
def get_sheet_data2(sheet_name):
|
||
"""Получаем данные из листа по имени."""
|
||
spreadsheet = gc.open(SPREADSHEET_NAME)
|
||
sheet = spreadsheet.worksheet(sheet_name)
|
||
return sheet.get_all_values()
|
||
|
||
|
||
def send_message_plan(sheet_data, start_date, tg_id):
|
||
"""Создаем план отправки сообщений."""
|
||
messages_to_send = []
|
||
for row in sheet_data[1:]: # Пропускаем заголовок
|
||
description = row[0]
|
||
link = row[2]
|
||
message = row[3]
|
||
days_offset = int(row[4]) # Дни с начала периода
|
||
send_time = row[5]
|
||
|
||
# Рассчитываем дату и время отправки
|
||
send_date = datetime.strptime(start_date, "%d.%m.%Y, %H:%M") + timedelta(days=days_offset)
|
||
send_datetime = datetime.strptime(f"{send_date.date()} {send_time}", "%Y-%m-%d %H:%M")
|
||
|
||
messages_to_send.append({
|
||
"datetime": send_datetime,
|
||
"tg_id": tg_id,
|
||
"message": f"{message}\n{link}",
|
||
"description": description
|
||
})
|
||
return messages_to_send
|
||
|
||
|
||
async def send_messages(bot, messages_to_send, sheet_name):
|
||
"""Отправляем сообщения и записываем время прочтения."""
|
||
spreadsheet = gc.open(SPREADSHEET_NAME)
|
||
sheet = spreadsheet.worksheet(sheet_name)
|
||
for msg in messages_to_send:
|
||
# Ожидание времени отправки
|
||
now = datetime.now()
|
||
if now < msg["datetime"]:
|
||
await asyncio.sleep((msg["datetime"] - now).total_seconds())
|
||
|
||
# Отправляем сообщение
|
||
await bot.send_message(msg["tg_id"], msg["message"])
|
||
|
||
# Записываем время прочтения
|
||
cell = sheet.find(msg["description"]) # Ищем описание в столбце A
|
||
row = cell.row
|
||
col = sheet.row_values(1).index(f"{msg['tg_id']}") + 1 # Столбец по ID
|
||
sheet.update_cell(row, col, now.strftime("%d.%m.%Y %H:%M"))
|
||
|
||
|
||
@dp.message_handler(commands=['rassil'])
|
||
async def start_handler(message: Message):
|
||
"""Обрабатываем команду /start."""
|
||
changes = load_changes2()
|
||
for sheet_name, employees in changes.items():
|
||
for employee in employees:
|
||
full_name = employee[0]
|
||
tg_id = int(employee[13]) # ID Telegram
|
||
start_date = employee[14] # Дата начала
|
||
sheet_data = get_sheet_data2(sheet_name)
|
||
|
||
# Создаем план отправки сообщений
|
||
messages_to_send = send_message_plan(sheet_data, start_date, tg_id)
|
||
|
||
# Отправляем сообщения
|
||
await send_messages(bot, messages_to_send, sheet_name)
|
||
|
||
|
||
|
||
|
||
# Известные сотрудники, чтобы избежать повторной обработки
|
||
known_employees = set()
|
||
|
||
|
||
def load_changes2():
|
||
"""Загружает изменения из файла changes.json."""
|
||
try:
|
||
with open(r'C:\Users\ilyac\PycharmProjects\Bot_for_clinic\sheets_backup\changes.json', 'r', encoding='utf-8') as file:
|
||
data = json.load(file)
|
||
return data
|
||
except Exception as e:
|
||
logging.error(f"Ошибка при чтении файла changes.json: {e}")
|
||
return {}
|
||
|
||
|
||
def find_employee_in_sheet(full_name):
|
||
"""Находит сотрудника в Google Таблице по ФИО."""
|
||
try:
|
||
result = sheets_service.spreadsheets().values().get(
|
||
spreadsheetId=SPREADSHEET_ID, range=RANGE_NAME
|
||
).execute()
|
||
values = result.get('values', [])
|
||
|
||
for row in values:
|
||
if row and row[0] == full_name:
|
||
return row # Возвращаем строку с данными сотрудника
|
||
return None
|
||
except Exception as e:
|
||
logging.error(f"Ошибка при поиске сотрудника в Google Таблице: {e}")
|
||
return None
|
||
|
||
|
||
def create_contract(employee_data):
|
||
"""Создаёт трудовой договор на основе данных сотрудника."""
|
||
try:
|
||
file_metadata = {
|
||
'name': f"ТД_{employee_data[0]}_{datetime.now().strftime('%Y%m%d%H%M%S')}",
|
||
'mimeType': 'application/vnd.google-apps.document',
|
||
'parents': ['1fyA5btnwCQC-juE40T-uLSVHVIB-I2Ny']
|
||
}
|
||
copied_file = drive_service.files().copy(
|
||
fileId=TEMPLATE_DOC_ID, body=file_metadata
|
||
).execute()
|
||
document_id = copied_file['id']
|
||
|
||
replacements = {
|
||
"{{ФИО}}": employee_data[0],
|
||
"{{Серия паспорта}}": employee_data[3],
|
||
"{{Номер паспорта}}": employee_data[4],
|
||
"{{Кем выдан}}": employee_data[5],
|
||
"{{Дата выдачи}}": employee_data[6],
|
||
"{{Адрес}}": employee_data[8],
|
||
"{{Дата рождения}}": employee_data[12],
|
||
"{{ИНН}}": employee_data[11],
|
||
"{{СНИЛС}}": employee_data[10],
|
||
}
|
||
|
||
requests = [{"replaceAllText": {
|
||
"containsText": {"text": key, "matchCase": True},
|
||
"replaceText": value
|
||
}} for key, value in replacements.items()]
|
||
|
||
docs_service.documents().batchUpdate(
|
||
documentId=document_id, body={'requests': requests}
|
||
).execute()
|
||
|
||
return f"https://docs.google.com/document/d/{document_id}/edit"
|
||
except Exception as e:
|
||
logging.error(f"Ошибка при создании трудового договора: {e}")
|
||
return None
|
||
|
||
|
||
async def process_changes():
|
||
"""Обрабатывает изменения из файла changes.json."""
|
||
print('я тут')
|
||
while True:
|
||
changes = load_changes()
|
||
for sheet_name, entries in changes.items():
|
||
for entry in entries:
|
||
full_name = entry[0]
|
||
if full_name in known_employees:
|
||
continue # Пропускаем уже обработанных сотрудников
|
||
|
||
employee_data = find_employee_in_sheet(full_name)
|
||
if employee_data:
|
||
contract_url = create_contract(employee_data)
|
||
if contract_url:
|
||
# Уведомляем администраторов
|
||
for admin_id in ADMIN_IDS:
|
||
await bot.send_message(
|
||
admin_id,
|
||
f"Новый сотрудник: {full_name}\n"
|
||
f"Трудовой договор: {contract_url}"
|
||
)
|
||
|
||
known_employees.add(full_name)
|
||
await asyncio.sleep(10) # Проверяем изменения каждые 10 секунд
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
def is_admin(user_id):
|
||
ADMIN_IDS = [5522111920, 766945900] # Список ID администраторов
|
||
return user_id in ADMIN_IDS
|
||
|
||
class GroupMessageState(StatesGroup):
|
||
waiting_for_group_names1 = State()
|
||
waiting_for_message_text = State()
|
||
waiting_for_user_name = State()
|
||
waiting_for_group_names = State()
|
||
waiting_for_user_fio = State()
|
||
|
||
|
||
|
||
|
||
# # Словарь для хранения сотрудников, которых уже обработали
|
||
# known_employees = set()
|
||
#
|
||
# def create_contract_google(employee_data):
|
||
# # Копирование шаблона
|
||
# file_metadata = {
|
||
# 'name': f"ТД_{employee_data[0]}_{datetime.now().strftime('%Y%m%d%H%M%S')}",
|
||
# 'mimeType': 'application/vnd.google-apps.document',
|
||
# 'parents': ['1fyA5btnwCQC-juE40T-uLSVHVIB-I2Ny']
|
||
# }
|
||
# copied_file = drive_service.files().copy(
|
||
# fileId=TEMPLATE_DOC_ID, body=file_metadata).execute()
|
||
# document_id = copied_file['id']
|
||
#
|
||
# # Замены текста в шаблоне
|
||
# replacements = {
|
||
# "{{ФИО}}": employee_data[0],
|
||
# "{{Серия паспорта}}": employee_data[3],
|
||
# "{{Номер паспорта}}": employee_data[4],
|
||
# "{{Кем выдан}}": employee_data[5],
|
||
# "{{Дата выдачи}}": employee_data[6],
|
||
# "{{Адрес}}": employee_data[8],
|
||
# "{{Дата рождения}}": employee_data[12],
|
||
# "{{ИНН}}": employee_data[11],
|
||
# "{{СНИЛС}}": employee_data[10],
|
||
# }
|
||
#
|
||
# requests = [{"replaceAllText": {
|
||
# "containsText": {"text": key, "matchCase": True},
|
||
# "replaceText": value
|
||
# }} for key, value in replacements.items()]
|
||
#
|
||
# docs_service.documents().batchUpdate(
|
||
# documentId=document_id, body={'requests': requests}).execute()
|
||
#
|
||
# return f"https://docs.google.com/document/d/{document_id}/edit"
|
||
|
||
|
||
|
||
|
||
#
|
||
# # Функция для отправки сообщения о новом сотруднике с клавиатурой для выбора специальности
|
||
# async def notify_admin_about_new_employee(entry):
|
||
# full_name = entry[0] if entry else "Без имени"
|
||
# new_employee_id = entry[13] if len(entry) > 13 else None
|
||
#
|
||
# if new_employee_id:
|
||
# # Отправляем уведомление админу о новом сотруднике с клавишами выбора специальности
|
||
# keyboard = create_employee_keyboard()
|
||
#
|
||
# await bot.send_message(
|
||
# ADMIN_ID,
|
||
# f"Зарегистрирован новый сотрудник: {full_name}\nTelegram ID сотрудника: {new_employee_id}",
|
||
# reply_markup=keyboard
|
||
# )
|
||
# else:
|
||
# logging.error("Не удалось найти Telegram ID сотрудника.")
|
||
#
|
||
#
|
||
# # Функция для проверки новых сотрудников
|
||
# async def check_for_new_entries():
|
||
# sheet = service.spreadsheets()
|
||
#
|
||
# while True:
|
||
# try:
|
||
# result = sheet.values().get(spreadsheetId=SPREADSHEET_ID, range=RANGE_NAME).execute()
|
||
# values = result.get('values', [])
|
||
# except Exception as e:
|
||
# logging.error(f"Ошибка при чтении таблицы: {e}")
|
||
# await asyncio.sleep(30) # Ждем перед следующей попыткой
|
||
# continue
|
||
#
|
||
# # Проверяем каждую запись на наличие нового сотрудника
|
||
# for entry in values:
|
||
# full_name = entry[0] if entry else "Без имени"
|
||
# new_employee_id = entry[13] if len(entry) > 13 else None # Telegram ID сотрудника
|
||
#
|
||
# if full_name and full_name not in known_employees:
|
||
# known_employees.add(full_name)
|
||
#
|
||
# # Генерация трудового договора
|
||
# try:
|
||
# contract_url = create_contract_google(entry)
|
||
#
|
||
# # Уведомление для администратора
|
||
# await bot.send_message(
|
||
# ADMIN_ID,
|
||
# f"Зарегистрирован новый сотрудник: {full_name}\nТрудовой договор: {contract_url}",
|
||
# )
|
||
#
|
||
# # Уведомление для сотрудника
|
||
# if new_employee_id:
|
||
# welcome_message = f"Большое спасибо! Мы получили данные для заключения трудового договора с вами."
|
||
# await bot.send_message(
|
||
# new_employee_id,
|
||
# welcome_message,
|
||
# )
|
||
#
|
||
# # Оповещаем админа и отправляем клавиатуру для выбора специальности
|
||
# await notify_admin_about_new_employee(entry)
|
||
#
|
||
# except Exception as e:
|
||
# logging.error(f"Ошибка при создании трудового договора: {e}")
|
||
# await bot.send_message(ADMIN_ID, "Произошла ошибка при создании трудового договора.")
|
||
#
|
||
# await asyncio.sleep(10) # Проверяем таблицу каждые 10 секунд
|
||
#
|
||
#
|
||
#
|
||
#
|
||
#
|
||
#
|
||
#
|
||
#
|
||
#
|
||
# # Функция отправки сообщения о прохождении инструктажа
|
||
# async def send_fire_safety_instructions(employee_id, full_name, employee_data):
|
||
# message_text = f"""
|
||
# {full_name}, добрый день. Вы приглашены на корпоративный канал Клиники в Telegram. В закрепленном сообщении на этом канале Вы сможете ознакомиться с такими разделами:
|
||
#
|
||
# 👉[Наши сотрудники](https://t.me/c/1414064442/5) - актуальный список всех сотрудников Клиники;
|
||
# 👉[Контакты сотрудников](https://t.me/c/1414064442/39) - номера телефонов сотрудников для скачивания в телефонную книгу;
|
||
# 👉[История клиники](https://t.me/c/1414064442/6)
|
||
# 👉[Структура рабочих чатов](https://t.me/c/1414064442/10)
|
||
# 👉[Инструктаж по пожарной безопасности в Клинике ухо, горло, нос](https://disk.yandex.ru/i/i1k41Z2SznfpOA)
|
||
# 👉[Обратная связь](https://forms.gle/Dfjrb8K1NzzjmW5j7) - оставить предложение, пожелание или задать вопрос руководству (анонимно).
|
||
#
|
||
# **ВАЖНО:**
|
||
# В разделе [Инструктаж по пожарной безопасности в Клинике ухо, горло, нос](https://disk.yandex.ru/i/i1k41Z2SznfpOA) Вам необходимо изучить материалы по Пожарной безопасности, уделить особое внимание теме - порядок действий и по каким номерам звонить в случае пожароопасной ситуации.
|
||
#
|
||
# После этого пройти тестирование по данной теме (21 вопрос - зачет 70% правильных ответов):
|
||
# 👉[Тестирование по пожарной безопасности](https://forms.gle/VLEx2Gf1h8grpXXu5)
|
||
#
|
||
# **Срок обучения и прохождения тестирования** - до начала выполнения трудовой функции!
|
||
#
|
||
# Спасибо!
|
||
#
|
||
# С уважением,
|
||
# Жуланова Наталья Александровна,
|
||
# помощник генерального директора ООО "Клиника ухо, горло, нос им. проф. Е.Н.Оленевой" (г. Пермь)
|
||
# 📞 +7 (902) 64-54-648
|
||
# """
|
||
# try:
|
||
# # Отправка сообщения сотруднику
|
||
# await bot.send_message(employee_id, message_text, parse_mode="Markdown")
|
||
# logging.info(f"Сообщение о пожарной безопасности отправлено {full_name}.")
|
||
#
|
||
#
|
||
# except Exception as e:
|
||
# logging.error(f"Ошибка при отправке инструктажа или документа: {e}")
|
||
# await bot.send_message(ADMIN_ID, "Произошла ошибка при отправке инструктажа или документа.")
|
||
#
|
||
#
|
||
# # Обработчик нажатия на кнопки выбора специальности
|
||
#
|
||
# @dp.message_handler(lambda message: message.text in specialty_chat_links.keys(), chat_type=types.ChatType.PRIVATE)
|
||
# async def handle_specialty_selection(message: types.Message):
|
||
# selected_specialty = message.text
|
||
# data = sheet.get_all_values()
|
||
# last_employee_row = len(data)
|
||
#
|
||
# if last_employee_row < 2:
|
||
# await message.answer("В таблице пока нет сотрудников для назначения специальности.")
|
||
# return
|
||
#
|
||
# new_employee_id = data[last_employee_row - 1][13]
|
||
# full_name = data[last_employee_row - 1][0]
|
||
#
|
||
# if not new_employee_id:
|
||
# await message.answer("Не удалось найти Telegram ID сотрудника в таблице.")
|
||
# return
|
||
#
|
||
# try:
|
||
# sheet.update_cell(last_employee_row, 16, selected_specialty)
|
||
# await message.answer(f"Специальность сотрудника обновлена на: {selected_specialty}")
|
||
#
|
||
# chat_invite_link = specialty_chat_links.get(selected_specialty)
|
||
# if chat_invite_link:
|
||
# await bot.send_message(
|
||
# new_employee_id,
|
||
# f"Поздравляем! Вы были добавлены в нашу команду как {selected_specialty}. Вот ссылка на вашу беседу: {chat_invite_link}"
|
||
# )
|
||
# await message.answer(f"Ссылка на беседу отправлена сотруднику.")
|
||
#
|
||
# # Отправка сообщения о прохождении инструктажа
|
||
# await send_fire_safety_instructions(new_employee_id, full_name, data[last_employee_row - 1])
|
||
#
|
||
# except Exception as e:
|
||
# await message.answer(f"Ошибка при обновлении таблицы: {e}")
|
||
#
|
||
#
|
||
#
|
||
# # Функция для генерации клавиатуры с кнопками типа сотрудника
|
||
# def create_employee_keyboard():
|
||
# keyboard = ReplyKeyboardMarkup(resize_keyboard=True, one_time_keyboard=False)
|
||
# button_doctors = KeyboardButton("Врачи")
|
||
# button_nurses = KeyboardButton("Медсёстры")
|
||
# button_orderlies = KeyboardButton("Санитарки")
|
||
# button_admins = KeyboardButton("Администраторы")
|
||
# button_operators = KeyboardButton("Операторы КЦ")
|
||
# button_aap = KeyboardButton("АУП")
|
||
#
|
||
# keyboard.add(button_doctors, button_nurses)
|
||
# keyboard.add(button_orderlies, button_admins)
|
||
# keyboard.add(button_operators, button_aap)
|
||
#
|
||
# return keyboard
|
||
|
||
|
||
# Обработчик команды /start
|
||
@dp.message_handler(commands=['start'], chat_type=types.ChatType.PRIVATE)
|
||
async def send_welcome(message: types.Message):
|
||
user_id = message.from_user.id
|
||
|
||
# Отправка фото
|
||
photo_path = r'start_foto.png'
|
||
await bot.send_photo(user_id, types.InputFile(photo_path))
|
||
|
||
# Отправка текста с кнопкой
|
||
await bot.send_message(
|
||
user_id,
|
||
"Добрый день, этот бот нужен Вам для начала прохождения процедуры трудоустройства в "
|
||
"Общество с ограниченной ответственностью «Клиника ухо, горло, нос имени профессора Е.Н. Оленевой».\n"
|
||
"Нажмите, пожалуйста, на команду 👉 /registration",
|
||
reply_markup=get_registration_button()
|
||
)
|
||
|
||
|
||
|
||
|
||
# Обработчик команды регистрации
|
||
@dp.message_handler(commands=['registration'], chat_type=types.ChatType.PRIVATE)
|
||
async def send_welcome(message: types.Message):
|
||
await message.answer(
|
||
"▪️В соответствии с требованиями статьи 9 Федерального закона от 27.07.2006 г. «О персональных данных» № 152-ФЗ даю согласие на обработку своих персональных данных.\n"
|
||
"▪️Наименование и адрес оператора, получающего согласие субъекта персональных данных: Общество с ограниченной ответственностью «Клиника ухо, горло, нос имени профессора Е.Н. Оленевой».\n"
|
||
"▪️Цель обработки персональных данных: организация труда и осуществление иных, связанных с этим мероприятий.",
|
||
reply_markup=get_consent_keyboard()
|
||
)
|
||
|
||
# Обработчик ответа пользователя
|
||
@dp.message_handler(lambda message: message.text in ["✅ Даю согласие на обработку своих персональных данных", "❌ Не даю согласие на обработку своих персональных данных"])
|
||
async def process_consent(message: types.Message):
|
||
user_id = message.from_user.id
|
||
|
||
if message.text == "✅ Даю согласие на обработку своих персональных данных":
|
||
form_url = f"https://tgbotkugn.pirogov.ai/?user_id={user_id}"
|
||
form_keyboard = InlineKeyboardMarkup().add(
|
||
InlineKeyboardButton(
|
||
text="Ввести персональные данные", web_app=WebAppInfo(url=form_url)
|
||
)
|
||
)
|
||
await message.answer(
|
||
"Спасибо за согласие! Нажмите кнопку ниже, чтобы ввести персональные данные:",
|
||
reply_markup=form_keyboard
|
||
)
|
||
else:
|
||
await message.answer(
|
||
"Вы не дали согласие на обработку персональных данных.\n"
|
||
"Вы не можете продолжить регистрацию.",
|
||
reply_markup=get_consent_keyboard()
|
||
)
|
||
|
||
@dp.message_handler(commands=['getchatid'], chat_type=[ChatType.GROUP, ChatType.SUPERGROUP])
|
||
async def get_chat_id(message: types.Message):
|
||
# Проверяем статус пользователя в чате
|
||
chat_member = await bot.get_chat_member(message.chat.id, message.from_user.id)
|
||
if chat_member.status not in ['administrator', 'creator']:
|
||
# Отправляем предупреждение о недостатке прав
|
||
warning_message = await message.answer("У вас нет прав на выполнение этой команды.")
|
||
|
||
# Удаляем команду /getchatid через 1 секунду
|
||
await asyncio.sleep(1)
|
||
try:
|
||
await message.delete()
|
||
except MessageCantBeDeleted:
|
||
logging.warning("Не удалось удалить сообщение с командой /getchatid")
|
||
|
||
# Удаляем предупреждение через 5 секунд
|
||
await asyncio.sleep(5)
|
||
try:
|
||
await warning_message.delete()
|
||
except MessageCantBeDeleted:
|
||
logging.warning("Не удалось удалить сообщение с предупреждением")
|
||
return
|
||
|
||
# Отправляем ID и название чата администратору бота
|
||
chat_id = message.chat.id
|
||
chat_title = message.chat.title or "Без названия"
|
||
await bot.send_message(ADMIN_ID, f"ID чата: {chat_id}\nНазвание чата: {chat_title}")
|
||
|
||
# Удаляем команду /getchatid через 1 секунду
|
||
await asyncio.sleep(1)
|
||
try:
|
||
await message.delete()
|
||
except MessageCantBeDeleted:
|
||
logging.warning("Не удалось удалить сообщение с командой /getchatid")
|
||
|
||
|
||
|
||
# Инициализация доступа к Google Sheets
|
||
gc = gspread.service_account(filename=SERVICE_ACCOUNT_FILE)
|
||
sheet = gc.open_by_key(SPREADSHEET_ID).sheet1 # Открываем первую таблицу
|
||
|
||
|
||
@dp.message_handler(commands=['kick'])
|
||
async def kick_employee(message: types.Message):
|
||
args = message.get_args()
|
||
if not args:
|
||
await message.reply("Введите ФИО сотрудника после команды /kick.")
|
||
return
|
||
|
||
full_name = args.strip()
|
||
employees_data = get_sheet_data1("Лист1")
|
||
|
||
if len(employees_data) < 2:
|
||
await message.reply("Данные о сотрудниках отсутствуют.")
|
||
return
|
||
|
||
employee_id = None
|
||
for row in employees_data[1:]: # Пропускаем заголовок
|
||
if len(row) >= 14 and row[0].strip().lower() == full_name.lower():
|
||
employee_id = row[13]
|
||
break
|
||
|
||
if not employee_id:
|
||
await message.reply("Сотрудник не найден в базе.")
|
||
return
|
||
|
||
# Получаем список ID групп из Лист2
|
||
chat_ids = get_sheet_data("Лист2")
|
||
if not chat_ids or not chat_ids[0]:
|
||
await message.reply("Список групп для удаления не найден.")
|
||
return
|
||
|
||
kicked_from = []
|
||
for chat_id in chat_ids[0]: # Берем ID групп из первой строки
|
||
try:
|
||
await bot.kick_chat_member(chat_id, employee_id)
|
||
kicked_from.append(chat_id)
|
||
except Exception as e:
|
||
print(f"Ошибка при исключении из {chat_id}: {e}")
|
||
|
||
if kicked_from:
|
||
await message.reply(f"Сотрудник {full_name} исключен из {len(kicked_from)} групп.")
|
||
else:
|
||
await message.reply("Не удалось исключить сотрудника.")
|
||
|
||
def get_registration_button():
|
||
keyboard = ReplyKeyboardMarkup(resize_keyboard=True)
|
||
registration_button = KeyboardButton("/registration")
|
||
keyboard.add(registration_button)
|
||
return keyboard
|
||
|
||
|
||
def get_consent_keyboard():
|
||
keyboard = ReplyKeyboardMarkup(resize_keyboard=True, one_time_keyboard=True)
|
||
consent_button = KeyboardButton("✅ Даю согласие на обработку своих персональных данных")
|
||
deny_button = KeyboardButton("❌ Не даю согласие на обработку своих персональных данных")
|
||
keyboard.add(consent_button, deny_button)
|
||
return keyboard
|
||
|
||
|
||
|
||
|
||
# Получение данных из Google Таблицы
|
||
def get_sheet_data(sheet_name):
|
||
range_name = f"{sheet_name}!B1:AE3" # Диапазон для строки с chat_id и группами
|
||
result = service.spreadsheets().values().get(
|
||
spreadsheetId=SPREADSHEET_ID,
|
||
range=range_name
|
||
).execute()
|
||
return result.get('values', [])
|
||
|
||
|
||
# Получение данных из Google Таблицы
|
||
def get_sheet_data1(sheet_name):
|
||
range_name = f"{sheet_name}!A1:Q200" # Диапазон для строки с chat_id и группами
|
||
result = service.spreadsheets().values().get(
|
||
spreadsheetId=SPREADSHEET_ID,
|
||
range=range_name
|
||
).execute()
|
||
return result.get('values', [])
|
||
|
||
# Команда для рассылки сообщений в группы
|
||
@dp.message_handler(commands=['send_to_groups'], chat_type=types.ChatType.PRIVATE)
|
||
async def send_to_groups(message: types.Message):
|
||
if not is_admin(message.from_user.id):
|
||
await message.reply("Эта команда доступна только администратору.")
|
||
return
|
||
|
||
groups_data = get_sheet_data("Лист2")
|
||
if len(groups_data) < 2:
|
||
await message.reply("Данные о группах отсутствуют.")
|
||
return
|
||
|
||
await message.reply("Введите текст для рассылки в группы:")
|
||
|
||
@dp.message_handler()
|
||
async def broadcast_groups(msg: types.Message):
|
||
text = msg.text
|
||
groups_data = get_sheet_data("Лист2") # Укажите название листа в Google Sheets
|
||
if not groups_data or len(groups_data) < 2: # Проверяем наличие данных
|
||
await msg.reply("Данные о группах отсутствуют или некорректны.")
|
||
return
|
||
|
||
# Первая строка — chat_id (B1, C1, ...)
|
||
chat_ids = groups_data[0]
|
||
# Вторая строка — названия групп (B2, C2, ...)
|
||
group_names = groups_data[1]
|
||
|
||
for chat_id, group_name in zip(chat_ids, group_names):
|
||
print(f"Chat ID: {chat_id}, Group: {group_name}")
|
||
try:
|
||
await bot.send_message(chat_id, text)
|
||
except Exception as e:
|
||
error_message = (
|
||
f"Не удалось отправить сообщение в группу {group_name} "
|
||
f"(Chat ID: {chat_id}): {e}"
|
||
)
|
||
print(error_message) # Логируем ошибку
|
||
await bot.send_message(ADMIN_ID, error_message)
|
||
|
||
await msg.reply("Рассылка в группы завершена.")
|
||
|
||
|
||
# Команда для рассылки сообщений сотрудникам
|
||
@dp.message_handler(commands=['send_to_employees'])
|
||
async def send_to_employees(message: types.Message):
|
||
if not is_admin(message.from_user.id):
|
||
await message.reply("Эта команда доступна только администратору.")
|
||
return
|
||
|
||
employees_data = get_sheet_data1("Лист1")
|
||
if len(employees_data) < 2:
|
||
await message.reply("Данные о сотрудниках отсутствуют.")
|
||
return
|
||
|
||
await message.reply("Введите текст для рассылки сотрудникам:")
|
||
|
||
@dp.message_handler()
|
||
async def broadcast_employees(msg: types.Message):
|
||
text = msg.text
|
||
for row in employees_data[1:]:
|
||
if len(row) >= 14: # Проверяем наличие Telegram ID
|
||
telegram_id = row[13]
|
||
print(telegram_id)
|
||
try:
|
||
await bot.send_message(telegram_id, text)
|
||
except Exception as e:
|
||
await bot.send_message(ADMIN_ID, f"Не удалось отправить сообщение сотруднику {row[0]}: {e}")
|
||
await msg.reply("Рассылка сотрудникам завершена.")
|
||
|
||
|
||
@dp.message_handler(commands=['send_by_fio'], chat_type=types.ChatType.PRIVATE)
|
||
async def send_by_fio(message: types.Message, state: FSMContext): # Добавляем state
|
||
if not is_admin(message.from_user.id):
|
||
await message.reply("Эта команда доступна только администратору.")
|
||
return
|
||
|
||
employees_data = get_sheet_data1("Лист1")
|
||
if len(employees_data) < 2:
|
||
await message.reply("Данные о сотрудниках отсутствуют.")
|
||
return
|
||
|
||
await message.reply("Введите список ФИО сотрудников через запятую (например, Иванов Иван Иванович, Петров Петр Петрович):")
|
||
await state.set_state("waiting_for_fio_list")
|
||
|
||
|
||
|
||
@dp.message_handler(state="waiting_for_fio_list")
|
||
async def process_fio_list(message: types.Message, state: FSMContext):
|
||
fio_list = [fio.strip() for fio in message.text.split(",")]
|
||
employees_data = get_sheet_data1("Лист1")
|
||
|
||
# Поиск сотрудников по ФИО
|
||
found_employees = []
|
||
not_found_employees = []
|
||
|
||
for fio in fio_list:
|
||
target_row = next((row for row in employees_data[1:] if row[0] == fio), None)
|
||
if target_row and len(target_row) >= 14: # Проверяем наличие Telegram ID
|
||
found_employees.append((fio, target_row[13])) # Добавляем (ФИО, Telegram ID)
|
||
else:
|
||
not_found_employees.append(fio)
|
||
|
||
# Сохраняем найденных сотрудников в состояние
|
||
await state.update_data(found_employees=found_employees)
|
||
if not_found_employees:
|
||
await message.reply(
|
||
f"Следующие сотрудники не найдены или у них отсутствует Telegram ID: {', '.join(not_found_employees)}"
|
||
)
|
||
await message.reply("Введите текст сообщения для отправки сотрудникам:")
|
||
await state.set_state("waiting_for_message_text")
|
||
|
||
|
||
@dp.message_handler(state="waiting_for_message_text")
|
||
async def send_messages_to_employees(message: types.Message, state: FSMContext):
|
||
user_data = await state.get_data()
|
||
found_employees = user_data.get("found_employees", [])
|
||
text = message.text.strip()
|
||
|
||
if not found_employees:
|
||
await message.reply("Нет сотрудников для отправки сообщений.")
|
||
await state.finish()
|
||
return
|
||
|
||
for fio, telegram_id in found_employees:
|
||
try:
|
||
await bot.send_message(telegram_id, text)
|
||
await message.reply(f"Сообщение успешно отправлено сотруднику: {fio}")
|
||
except Exception as e:
|
||
await message.reply(f"Не удалось отправить сообщение сотруднику {fio}: {e}")
|
||
|
||
await message.reply("Рассылка завершена.")
|
||
await state.finish()
|
||
|
||
|
||
@dp.message_handler(commands=['send_to_group'], chat_type=types.ChatType.PRIVATE)
|
||
async def send_to_group(message: types.Message, state: FSMContext):
|
||
if not is_admin(message.from_user.id):
|
||
await message.reply("Эта команда доступна только администратору.")
|
||
return
|
||
|
||
await message.reply("Введите названия групп через запятую (например, Группа1, Группа2):")
|
||
await state.set_state(GroupMessageState.waiting_for_group_names1)
|
||
|
||
|
||
@dp.message_handler(state=GroupMessageState.waiting_for_group_names1)
|
||
async def process_group_names(message: types.Message, state: FSMContext):
|
||
group_names = [group.strip() for group in message.text.split(',') if group.strip()]
|
||
|
||
# Читаем данные из Лист2
|
||
sheet_data = get_sheet_data1("Лист2") # Подключите функцию чтения данных
|
||
|
||
if len(sheet_data) < 3 or len(sheet_data[0]) != len(sheet_data[1]):
|
||
await message.reply("Ошибка в данных Google Таблицы. Проверьте формат.")
|
||
return
|
||
|
||
# Создаём словарь {название группы: chat_id}
|
||
group_dict = {sheet_data[1][i]: sheet_data[0][i] for i in range(len(sheet_data[0]))}
|
||
|
||
# Ищем chat_id для введённых названий групп
|
||
group_ids = []
|
||
invalid_groups = []
|
||
for group in group_names:
|
||
if group in group_dict:
|
||
group_ids.append(group_dict[group])
|
||
else:
|
||
invalid_groups.append(group)
|
||
|
||
if not group_ids:
|
||
await message.reply("Указанные группы не найдены в таблице.")
|
||
return
|
||
|
||
# Сохраняем список найденных chat_id в состояние
|
||
await state.update_data(group_ids=group_ids, invalid_groups=invalid_groups)
|
||
await message.reply(
|
||
f"Группы найдены: {', '.join(group_names)}.\n\n"
|
||
"Теперь введите текст сообщения для отправки:"
|
||
)
|
||
await state.set_state(GroupMessageState.waiting_for_message_text)
|
||
|
||
|
||
@dp.message_handler(state=GroupMessageState.waiting_for_message_text)
|
||
async def process_group_message(message: types.Message, state: FSMContext):
|
||
text = message.text.strip()
|
||
data = await state.get_data()
|
||
group_ids = data.get('group_ids', [])
|
||
invalid_groups = data.get('invalid_groups', [])
|
||
|
||
for chat_id in group_ids:
|
||
try:
|
||
await bot.send_message(chat_id, text)
|
||
except Exception as e:
|
||
await message.reply(f"Не удалось отправить сообщение в группу {chat_id}: {e}")
|
||
|
||
if invalid_groups:
|
||
await message.reply(f"Следующие группы не найдены в таблице: {', '.join(invalid_groups)}")
|
||
|
||
await message.reply("Сообщение успешно отправлено в найденные группы.")
|
||
await state.finish()
|
||
|
||
|
||
|
||
@dp.message_handler(commands=['add_to_groups'], chat_type=types.ChatType.PRIVATE)
|
||
async def add_to_groups(message: types.Message, state: FSMContext):
|
||
if not is_admin(message.from_user.id):
|
||
await message.reply("Эта команда доступна только администратору.")
|
||
return
|
||
|
||
await message.reply("Введите названия групп через запятую (например, Группа1, Группа2):")
|
||
await state.set_state(GroupMessageState.waiting_for_group_names)
|
||
|
||
|
||
@dp.message_handler(state=GroupMessageState.waiting_for_group_names)
|
||
async def process_group_names(message: types.Message, state: FSMContext):
|
||
group_names = [group.strip() for group in message.text.split(',') if group.strip()]
|
||
|
||
# Читаем данные из Лист2
|
||
sheet_data = get_sheet_data("Лист2")
|
||
|
||
if len(sheet_data) < 3 or len(sheet_data[0]) != len(sheet_data[1]):
|
||
await message.reply("Ошибка в данных Google Таблицы. Проверьте формат.")
|
||
return
|
||
|
||
# Создаём словарь {название группы: chat_id}
|
||
group_dict = {sheet_data[1][i]: sheet_data[0][i] for i in range(len(sheet_data[0]))}
|
||
|
||
# Ищем chat_id для введённых названий групп
|
||
group_ids = []
|
||
invalid_groups = []
|
||
for group in group_names:
|
||
if group in group_dict:
|
||
group_ids.append(group_dict[group])
|
||
else:
|
||
invalid_groups.append(group)
|
||
|
||
if not group_ids:
|
||
await message.reply("Указанные группы не найдены в таблице.")
|
||
return
|
||
|
||
# Сохраняем список найденных chat_id в состояние
|
||
await state.update_data(group_ids=group_ids, invalid_groups=invalid_groups)
|
||
await message.reply(
|
||
f"Группы найдены: {', '.join(group_names)}.\n\n"
|
||
"Теперь введите ФИО пользователя, которого нужно добавить в группы:"
|
||
)
|
||
await state.set_state(GroupMessageState.waiting_for_user_fio)
|
||
|
||
|
||
@dp.message_handler(state=GroupMessageState.waiting_for_user_fio)
|
||
async def process_user_fio(message: types.Message, state: FSMContext):
|
||
user_fio = message.text.strip()
|
||
|
||
# Читаем данные из Лист1 для поиска пользователя
|
||
user_data = get_sheet_data1("Лист1")
|
||
|
||
# Создаём словарь {ФИО: Telegram ID}
|
||
user_dict = {row[0]: row[13] for row in user_data if len(row) > 13 and row[0] and row[13]}
|
||
|
||
if user_fio not in user_dict:
|
||
await message.reply("Пользователь с указанным ФИО не найден в таблице.")
|
||
return
|
||
|
||
user_id = user_dict[user_fio]
|
||
data = await state.get_data()
|
||
group_ids = data.get('group_ids', [])
|
||
invalid_groups = data.get('invalid_groups', [])
|
||
|
||
# Добавляем пользователя в группы и отправляем ссылки на приглашение
|
||
for chat_id in group_ids:
|
||
try:
|
||
invite_link = await bot.create_chat_invite_link(chat_id)
|
||
await bot.send_message(user_id, f"Вас приглашают в группу: {invite_link.invite_link}")
|
||
except Exception as e:
|
||
await message.reply(f"Не удалось добавить пользователя в группу {chat_id}: {e}")
|
||
|
||
if invalid_groups:
|
||
await message.reply(f"Следующие группы не найдены в таблице: {', '.join(invalid_groups)}")
|
||
|
||
await message.reply("Пользователь успешно добавлен в найденные группы и получил ссылки на приглашения.")
|
||
await state.finish()
|
||
|
||
|
||
# Команда /update_sheets
|
||
@dp.message_handler(commands=["update_sheets"])
|
||
async def update_sheets_command(message: types.Message):
|
||
if not is_admin(message.from_user.id):
|
||
await message.reply("Эта команда доступна только администратору.")
|
||
return
|
||
|
||
await message.reply("Процесс обновления данных начат...")
|
||
try:
|
||
result = subprocess.run(
|
||
[
|
||
r"C:\Users\ilyac\PycharmProjects\Bot_for_clinic\.venv\Scripts\python.exe",
|
||
r"C:\Users\ilyac\PycharmProjects\Bot_for_clinic\backup_google_sheets.py"
|
||
],
|
||
capture_output=True,
|
||
text=True,
|
||
encoding="utf-8", # Указываем кодировку
|
||
check=True,
|
||
)
|
||
|
||
if result.returncode == 0:
|
||
await message.reply(f"Обновление данных завершено успешно.")
|
||
else:
|
||
await message.reply(f"Обновление завершилось с ошибками:\n{result.stderr}")
|
||
|
||
except subprocess.CalledProcessError as e:
|
||
await message.reply(f"Ошибка выполнения скрипта: {e.stderr}")
|
||
|
||
except Exception as e:
|
||
await message.reply(f"Произошла ошибка: {e}")
|
||
|
||
|
||
|
||
|
||
|
||
if __name__ == '__main__':
|
||
loop = asyncio.get_event_loop()
|
||
loop.create_task(process_changes())
|
||
from aiogram import executor
|
||
executor.start_polling(dp, skip_updates=True) |