You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

1014 lines
45 KiB

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)