-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
235 lines (182 loc) · 8.79 KB
/
Copy pathmain.py
File metadata and controls
235 lines (182 loc) · 8.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
import json
import os
import pickle
import sys
import logging
import traceback
from functools import wraps
from dotenv import load_dotenv
from vk_maria.dispatcher import Dispatcher
from vk_maria import Vk, types
from vk_maria.dispatcher.fsm import StatesGroup, State, PickleStorage, FSMContext
from vk_maria.types import KeyboardMarkup, Button, Color
load_dotenv()
load_dotenv("./api_key.env")
load_dotenv("/run/secrets/vk_token")
logging.basicConfig(format="%(asctime)s [%(levelname)s] %(message)s", datefmt="%m/%d/%Y %H:%M:%S", level=logging.INFO)
if __name__ == "__main__":
logger = logging.getLogger(__name__)
else:
logging.error("Данный файл нельзя импортировать как библиотеку")
sys.exit(100)
try:
access_token = os.getenv("VK_API_KEY")
delay_in_seconds = os.getenv("DELAY")
talk_id = int(os.getenv("VK_TALK_ID"))
debug_mode = bool(os.getenv("DEBUG"))
if access_token is None or delay_in_seconds is None:
raise Exception()
except Exception:
logger.error("Не все нужные для работы данные были указаны(VK_API_KEY, VK_TALK_ID, DELAY)")
sys.exit(1)
try:
vk = Vk(access_token=access_token)
storage = PickleStorage("state/state.pck")
dp = Dispatcher(vk, storage)
except Exception as e:
logger.error("Инициализация не удалась!")
logger.exception(e)
sys.exit(2)
class Form(StatesGroup):
waiting_for_name: State
waiting_for_phone: State
waiting_for_text: State
waiting_for_photos: State
default_markup = KeyboardMarkup(one_time=False, inline=True)
default_markup.add_button(Button.Text(Color.PRIMARY, "Создать обращение"))
def log_exception(func):
@wraps(func)
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception as e:
logger.error("".join(traceback.format_exception(type(e), e, e.__traceback__.tb_next, limit=3)))
logger.error(f"Переменные при ошибки, позиционные: {args}; именованные: {kwargs}")
return wrapper
@dp.message_handler(state=Form.waiting_for_name)
@log_exception
def process_name(event: types.Message, state: FSMContext):
details = event.message.text.split()
name = " ".join(details)
if len(event.message.text) < 4 or len(details) < 2:
event.answer("Должно быть введено корректное имя и фамилия!")
return
if len(name) > 36:
event.answer("Ваши данные слишком длинные, укажите только имя и фамилию, если ошибка остаётся, сократите фамилию или/и имя")
return
state.update_data(name=name)
event.reply("Ваш телефон:")
Form.next()
@dp.message_handler(state=Form.waiting_for_phone)
@log_exception
def process_phone(event: types.Message, state: FSMContext):
phone = event.message.text.replace(" ", "")
if not 10 < len(phone) < 20 or not all([x in "0123456789()+-" for x in phone]):
if len(phone) <= 10:
event.answer("Телефон слишком короткий")
elif len(phone) >= 20:
event.answer("Телефон слишком длинный")
else:
event.answer("Телефон не может содержать букв")
return
state.update_data(phone=phone)
event.reply("Отправьте ваше обращение к администрации:")
Form.next()
@dp.message_handler(state=Form.waiting_for_text)
@log_exception
def process_text(event: types.Message, state: FSMContext):
if len(event.message.text) > 3500:
event.answer("Текст слишком большой(Максимум: 3500 символов)!")
return
state.update_data(text=event.message.text)
markup = KeyboardMarkup(one_time=False, inline=True)
markup.add_button(Button.Text(Color.SECONDARY, "Фото нет"))
event.reply(
"Отправьте фотографии, которые вы хотите прикрепить к обращению, если фотографий несколько, отправьте их одним сообщением(До 10 штук)",
keyboard=markup,
)
Form.next()
@dp.message_handler(state=Form.waiting_for_photos)
@log_exception
def process_callback(event: types.Message, state: FSMContext):
if event.message.text != "Фото нет" and len(event.message.attachments) == 0:
event.answer("Если фото нет, необходимо нажать на кнопку выше")
return
photos = []
for x in event.message.attachments:
if x.type != "photo":
event.answer(
"Обнаружены недопустимые вложения, пожалуйста используйте только фотографии, отправленные при помощи быстрой отправки"
)
return
photos.append(f"photo{x.photo.owner_id}_{x.photo.id}_{x.photo.access_key}")
user_data = state.get_data()
vk.messages_send(
peer_id=2000000000 + talk_id,
message=f"Обращение от: {user_data['name']}\nС телефоном: {user_data['phone']}\nhttps://vk.com/id{event.message.from_id}\n{'-' * 15}\n{user_data['text']}",
attachment=",".join(photos),
content_source=json.dumps(
{
"type": "message",
# от чьего имени указан peer_id. т.е. вы можете использовать контент из сообщения другой группы.
"owner_id": event.message.from_id,
# id диалога
"peer_id": event.message.peer_id,
# id сообщения в беседе. Не путать с message.id профиля
"conversation_message_id": event.message.conversation_message_id,
}
),
)
event.answer(
"Принято! Ваше обращение было сформировано и отправлено администрации техникума. Если хотите отправить новое обращение, нажмите на кнопку ниже",
keyboard=default_markup,
)
state.finish(with_data=True)
Form.finish()
logger.info(f"Обращение успешно отправлено от {user_data['name']} в беседу предложки!")
@dp.message_handler(text="Начать")
@log_exception
def welcome(event):
event.answer(
"""Добро пожаловать в чат-бот для обращений.
Сюда вы можете написать свои вопросы, жалобы, или предложения администрации ОГБПОУ «ТТИТ»""",
keyboard=default_markup,
)
@dp.message_handler(text="Создать обращение")
@log_exception
def start_send_process(event):
event.reply("Введите своё фамилию и имя для обращения:", keyboard=KeyboardMarkup())
Form.waiting_for_name.set()
@dp.message_handler()
@log_exception
def echo(event: types.Message):
event.reply("Для отправки обращения нажмите на кнопку и заполните анкету", keyboard=default_markup)
logger.info("Начинаю работу")
@log_exception
def poll():
return dp.start_polling(debug=debug_mode)
def notify_users_of_outage():
dialogs = vk.messages_get_conversations(filter="unread")
for chat in dialogs.items:
peer_id = chat['conversation']['peer']['id']
if peer_id > 2000000000:
continue
vk.messages_mark_as_read(peer_id=peer_id)
if storage.get_state(chat=peer_id, user=peer_id) is None:
vk.messages_send(
peer_id=peer_id,
message="Извините, запутался в сообщениях, если хотели сформировать и отправить сообщение, пожалуйста нажмите на кнопку ниже",
keyboard=default_markup
)
else:
vk.messages_send(
peer_id=peer_id,
message="Я был занят и не мог продолжить заполнение с Вами, пожалуйста повторите ваш ввод",
)
logger.info(f"Отправлено оповещение об отсутствии связи пользователю c id{peer_id}")
notify_users_of_outage()
try:
latest_event = poll()
except KeyboardInterrupt:
logger.info("Сохранение состояния на диск")
logger.info("Процесс завершился")