smart-bot-factory 1.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- smart_bot_factory/__init__.py +3 -0
- smart_bot_factory/admin/__init__.py +18 -0
- smart_bot_factory/admin/admin_events.py +1223 -0
- smart_bot_factory/admin/admin_logic.py +553 -0
- smart_bot_factory/admin/admin_manager.py +156 -0
- smart_bot_factory/admin/admin_tester.py +157 -0
- smart_bot_factory/admin/timeout_checker.py +547 -0
- smart_bot_factory/aiogram_calendar/__init__.py +14 -0
- smart_bot_factory/aiogram_calendar/common.py +64 -0
- smart_bot_factory/aiogram_calendar/dialog_calendar.py +259 -0
- smart_bot_factory/aiogram_calendar/schemas.py +99 -0
- smart_bot_factory/aiogram_calendar/simple_calendar.py +224 -0
- smart_bot_factory/analytics/analytics_manager.py +414 -0
- smart_bot_factory/cli.py +806 -0
- smart_bot_factory/config.py +258 -0
- smart_bot_factory/configs/growthmed-october-24/prompts/1sales_context.txt +16 -0
- smart_bot_factory/configs/growthmed-october-24/prompts/2product_info.txt +582 -0
- smart_bot_factory/configs/growthmed-october-24/prompts/3objection_handling.txt +66 -0
- smart_bot_factory/configs/growthmed-october-24/prompts/final_instructions.txt +212 -0
- smart_bot_factory/configs/growthmed-october-24/prompts/help_message.txt +28 -0
- smart_bot_factory/configs/growthmed-october-24/prompts/welcome_message.txt +8 -0
- smart_bot_factory/configs/growthmed-october-24/reports/test_20250924_064229.txt +818 -0
- smart_bot_factory/configs/growthmed-october-24/reports/test_20250924_064335.txt +32 -0
- smart_bot_factory/configs/growthmed-october-24/reports/test_20250924_064638.txt +35 -0
- smart_bot_factory/configs/growthmed-october-24/tests/quick_scenarios.yaml +133 -0
- smart_bot_factory/configs/growthmed-october-24/tests/realistic_scenarios.yaml +108 -0
- smart_bot_factory/configs/growthmed-october-24/tests/scenario_examples.yaml +46 -0
- smart_bot_factory/configs/growthmed-october-24/welcome_file/welcome_file_msg.txt +16 -0
- smart_bot_factory/configs/growthmed-october-24/welcome_file//342/225/250/320/267/342/225/250/342/225/241/342/225/250/342/225/221 /342/225/250/342/225/227/342/225/250/342/225/225/342/225/244/320/221/342/225/244/320/222 /342/225/250/342/224/220/342/225/250/342/225/233 152/342/225/250/320/264/342/225/250/320/247 /342/225/250/342/225/225 323/342/225/250/320/264/342/225/250/320/247 /342/225/250/342/224/244/342/225/250/342/225/227/342/225/244/320/237 /342/225/250/342/225/235/342/225/250/342/225/241/342/225/250/342/224/244/342/225/250/342/225/225/342/225/244/320/226/342/225/250/342/225/225/342/225/250/342/225/234/342/225/244/320/233.pdf +0 -0
- smart_bot_factory/core/bot_utils.py +1108 -0
- smart_bot_factory/core/conversation_manager.py +653 -0
- smart_bot_factory/core/decorators.py +2464 -0
- smart_bot_factory/core/message_sender.py +729 -0
- smart_bot_factory/core/router.py +347 -0
- smart_bot_factory/core/router_manager.py +218 -0
- smart_bot_factory/core/states.py +27 -0
- smart_bot_factory/creation/__init__.py +7 -0
- smart_bot_factory/creation/bot_builder.py +1093 -0
- smart_bot_factory/creation/bot_testing.py +1122 -0
- smart_bot_factory/dashboard/__init__.py +3 -0
- smart_bot_factory/event/__init__.py +7 -0
- smart_bot_factory/handlers/handlers.py +2013 -0
- smart_bot_factory/integrations/langchain_openai.py +542 -0
- smart_bot_factory/integrations/openai_client.py +513 -0
- smart_bot_factory/integrations/supabase_client.py +1678 -0
- smart_bot_factory/memory/__init__.py +8 -0
- smart_bot_factory/memory/memory_manager.py +299 -0
- smart_bot_factory/memory/static_memory.py +214 -0
- smart_bot_factory/message/__init__.py +56 -0
- smart_bot_factory/rag/__init__.py +5 -0
- smart_bot_factory/rag/decorators.py +29 -0
- smart_bot_factory/rag/router.py +54 -0
- smart_bot_factory/rag/templates/__init__.py +3 -0
- smart_bot_factory/rag/templates/create_table.sql +7 -0
- smart_bot_factory/rag/templates/create_table_and_function_template.py +94 -0
- smart_bot_factory/rag/templates/match_function.sql +61 -0
- smart_bot_factory/rag/templates/match_services_template.py +82 -0
- smart_bot_factory/rag/vectorstore.py +449 -0
- smart_bot_factory/router/__init__.py +10 -0
- smart_bot_factory/setup_checker.py +512 -0
- smart_bot_factory/supabase/__init__.py +7 -0
- smart_bot_factory/supabase/client.py +631 -0
- smart_bot_factory/utils/__init__.py +11 -0
- smart_bot_factory/utils/debug_routing.py +114 -0
- smart_bot_factory/utils/prompt_loader.py +529 -0
- smart_bot_factory/utils/tool_router.py +68 -0
- smart_bot_factory/utils/user_prompt_loader.py +55 -0
- smart_bot_factory/utm_link_generator.py +123 -0
- smart_bot_factory-1.1.1.dist-info/METADATA +1135 -0
- smart_bot_factory-1.1.1.dist-info/RECORD +73 -0
- smart_bot_factory-1.1.1.dist-info/WHEEL +4 -0
- smart_bot_factory-1.1.1.dist-info/entry_points.txt +2 -0
- smart_bot_factory-1.1.1.dist-info/licenses/LICENSE +24 -0
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import calendar
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
|
|
4
|
+
from aiogram.types import (CallbackQuery, InlineKeyboardButton,
|
|
5
|
+
InlineKeyboardMarkup)
|
|
6
|
+
|
|
7
|
+
from .common import GenericCalendar
|
|
8
|
+
from .schemas import (DialogCalAct, DialogCalendarCallback, highlight,
|
|
9
|
+
superscript)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class DialogCalendar(GenericCalendar):
|
|
13
|
+
|
|
14
|
+
ignore_callback = DialogCalendarCallback(
|
|
15
|
+
act=DialogCalAct.ignore
|
|
16
|
+
).pack() # placeholder for no answer buttons
|
|
17
|
+
|
|
18
|
+
async def _get_month_kb(self, year: int):
|
|
19
|
+
"""Creates an inline keyboard with months for specified year"""
|
|
20
|
+
|
|
21
|
+
today = datetime.now()
|
|
22
|
+
now_month, now_year = today.month, today.year
|
|
23
|
+
now_year = today.year
|
|
24
|
+
|
|
25
|
+
kb = []
|
|
26
|
+
# first row with year button
|
|
27
|
+
years_row = []
|
|
28
|
+
years_row.append(
|
|
29
|
+
InlineKeyboardButton(
|
|
30
|
+
text=self._labels.cancel_caption,
|
|
31
|
+
callback_data=DialogCalendarCallback(
|
|
32
|
+
act=DialogCalAct.cancel, year=year, month=1, day=1
|
|
33
|
+
).pack(),
|
|
34
|
+
)
|
|
35
|
+
)
|
|
36
|
+
years_row.append(
|
|
37
|
+
InlineKeyboardButton(
|
|
38
|
+
text=str(year) if year != today.year else highlight(year),
|
|
39
|
+
callback_data=DialogCalendarCallback(
|
|
40
|
+
act=DialogCalAct.start, year=year, month=-1, day=-1
|
|
41
|
+
).pack(),
|
|
42
|
+
)
|
|
43
|
+
)
|
|
44
|
+
years_row.append(
|
|
45
|
+
InlineKeyboardButton(text=" ", callback_data=self.ignore_callback)
|
|
46
|
+
)
|
|
47
|
+
kb.append(years_row)
|
|
48
|
+
# two rows with 6 months buttons
|
|
49
|
+
month6_row = []
|
|
50
|
+
|
|
51
|
+
def highlight_month():
|
|
52
|
+
month_str = self._labels.months[month - 1]
|
|
53
|
+
if now_month == month and now_year == year:
|
|
54
|
+
return highlight(month_str)
|
|
55
|
+
return month_str
|
|
56
|
+
|
|
57
|
+
for month in range(1, 7):
|
|
58
|
+
month6_row.append(
|
|
59
|
+
InlineKeyboardButton(
|
|
60
|
+
text=highlight_month(),
|
|
61
|
+
callback_data=DialogCalendarCallback(
|
|
62
|
+
act=DialogCalAct.set_m, year=year, month=month, day=-1
|
|
63
|
+
).pack(),
|
|
64
|
+
)
|
|
65
|
+
)
|
|
66
|
+
month12_row = []
|
|
67
|
+
|
|
68
|
+
for month in range(7, 13):
|
|
69
|
+
month12_row.append(
|
|
70
|
+
InlineKeyboardButton(
|
|
71
|
+
text=highlight_month(),
|
|
72
|
+
callback_data=DialogCalendarCallback(
|
|
73
|
+
act=DialogCalAct.set_m, year=year, month=month, day=-1
|
|
74
|
+
).pack(),
|
|
75
|
+
)
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
kb.append(month6_row)
|
|
79
|
+
kb.append(month12_row)
|
|
80
|
+
return InlineKeyboardMarkup(row_width=6, inline_keyboard=kb)
|
|
81
|
+
|
|
82
|
+
async def _get_days_kb(self, year: int, month: int):
|
|
83
|
+
"""Creates an inline keyboard with calendar days of month for specified year and month"""
|
|
84
|
+
|
|
85
|
+
today = datetime.now()
|
|
86
|
+
now_weekday = self._labels.days_of_week[today.weekday()]
|
|
87
|
+
now_month, now_year, now_day = today.month, today.year, today.day
|
|
88
|
+
|
|
89
|
+
def highlight_month():
|
|
90
|
+
month_str = self._labels.months[month - 1]
|
|
91
|
+
if now_month == month and now_year == year:
|
|
92
|
+
return highlight(month_str)
|
|
93
|
+
return month_str
|
|
94
|
+
|
|
95
|
+
def highlight_weekday():
|
|
96
|
+
if now_month == month and now_year == year and now_weekday == weekday:
|
|
97
|
+
return highlight(weekday)
|
|
98
|
+
return weekday
|
|
99
|
+
|
|
100
|
+
def format_day_string():
|
|
101
|
+
date_to_check = datetime(year, month, day)
|
|
102
|
+
if self.min_date and date_to_check < self.min_date:
|
|
103
|
+
return superscript(str(day))
|
|
104
|
+
elif self.max_date and date_to_check > self.max_date:
|
|
105
|
+
return superscript(str(day))
|
|
106
|
+
return str(day)
|
|
107
|
+
|
|
108
|
+
def highlight_day():
|
|
109
|
+
day_string = format_day_string()
|
|
110
|
+
if now_month == month and now_year == year and now_day == day:
|
|
111
|
+
return highlight(day_string)
|
|
112
|
+
return day_string
|
|
113
|
+
|
|
114
|
+
kb = []
|
|
115
|
+
nav_row = []
|
|
116
|
+
nav_row.append(
|
|
117
|
+
InlineKeyboardButton(
|
|
118
|
+
text=self._labels.cancel_caption,
|
|
119
|
+
callback_data=DialogCalendarCallback(
|
|
120
|
+
act=DialogCalAct.cancel, year=year, month=1, day=1
|
|
121
|
+
).pack(),
|
|
122
|
+
)
|
|
123
|
+
)
|
|
124
|
+
nav_row.append(
|
|
125
|
+
InlineKeyboardButton(
|
|
126
|
+
text=str(year) if year != now_year else highlight(year),
|
|
127
|
+
callback_data=DialogCalendarCallback(
|
|
128
|
+
act=DialogCalAct.start, year=year, month=-1, day=-1
|
|
129
|
+
).pack(),
|
|
130
|
+
)
|
|
131
|
+
)
|
|
132
|
+
nav_row.append(
|
|
133
|
+
InlineKeyboardButton(
|
|
134
|
+
text=highlight_month(),
|
|
135
|
+
callback_data=DialogCalendarCallback(
|
|
136
|
+
act=DialogCalAct.set_y, year=year, month=-1, day=-1
|
|
137
|
+
).pack(),
|
|
138
|
+
)
|
|
139
|
+
)
|
|
140
|
+
kb.append(nav_row)
|
|
141
|
+
|
|
142
|
+
week_days_labels_row = []
|
|
143
|
+
for weekday in self._labels.days_of_week:
|
|
144
|
+
week_days_labels_row.append(
|
|
145
|
+
InlineKeyboardButton(
|
|
146
|
+
text=highlight_weekday(), callback_data=self.ignore_callback
|
|
147
|
+
)
|
|
148
|
+
)
|
|
149
|
+
kb.append(week_days_labels_row)
|
|
150
|
+
|
|
151
|
+
month_calendar = calendar.monthcalendar(year, month)
|
|
152
|
+
|
|
153
|
+
for week in month_calendar:
|
|
154
|
+
days_row = []
|
|
155
|
+
for day in week:
|
|
156
|
+
if day == 0:
|
|
157
|
+
days_row.append(
|
|
158
|
+
InlineKeyboardButton(
|
|
159
|
+
text=" ", callback_data=self.ignore_callback
|
|
160
|
+
)
|
|
161
|
+
)
|
|
162
|
+
continue
|
|
163
|
+
days_row.append(
|
|
164
|
+
InlineKeyboardButton(
|
|
165
|
+
text=highlight_day(),
|
|
166
|
+
callback_data=DialogCalendarCallback(
|
|
167
|
+
act=DialogCalAct.day, year=year, month=month, day=day
|
|
168
|
+
).pack(),
|
|
169
|
+
)
|
|
170
|
+
)
|
|
171
|
+
kb.append(days_row)
|
|
172
|
+
return InlineKeyboardMarkup(row_width=7, inline_keyboard=kb)
|
|
173
|
+
|
|
174
|
+
async def start_calendar(
|
|
175
|
+
self, year: int = datetime.now().year, month: int = None
|
|
176
|
+
) -> InlineKeyboardMarkup:
|
|
177
|
+
today = datetime.now()
|
|
178
|
+
now_year = today.year
|
|
179
|
+
|
|
180
|
+
if month:
|
|
181
|
+
return await self._get_days_kb(year, month)
|
|
182
|
+
kb = []
|
|
183
|
+
# inline_kb = InlineKeyboardMarkup(row_width=5)
|
|
184
|
+
# first row - years
|
|
185
|
+
years_row = []
|
|
186
|
+
for value in range(year - 2, year + 3):
|
|
187
|
+
years_row.append(
|
|
188
|
+
InlineKeyboardButton(
|
|
189
|
+
text=str(value) if value != now_year else highlight(value),
|
|
190
|
+
callback_data=DialogCalendarCallback(
|
|
191
|
+
act=DialogCalAct.set_y, year=value, month=-1, day=-1
|
|
192
|
+
).pack(),
|
|
193
|
+
)
|
|
194
|
+
)
|
|
195
|
+
kb.append(years_row)
|
|
196
|
+
# nav buttons
|
|
197
|
+
nav_row = []
|
|
198
|
+
nav_row.append(
|
|
199
|
+
InlineKeyboardButton(
|
|
200
|
+
text="<<",
|
|
201
|
+
callback_data=DialogCalendarCallback(
|
|
202
|
+
act=DialogCalAct.prev_y, year=year, month=-1, day=-1
|
|
203
|
+
).pack(),
|
|
204
|
+
)
|
|
205
|
+
)
|
|
206
|
+
nav_row.append(
|
|
207
|
+
InlineKeyboardButton(
|
|
208
|
+
text=self._labels.cancel_caption,
|
|
209
|
+
callback_data=DialogCalendarCallback(
|
|
210
|
+
act=DialogCalAct.cancel, year=year, month=1, day=1
|
|
211
|
+
).pack(),
|
|
212
|
+
)
|
|
213
|
+
)
|
|
214
|
+
nav_row.append(
|
|
215
|
+
InlineKeyboardButton(
|
|
216
|
+
text=">>",
|
|
217
|
+
callback_data=DialogCalendarCallback(
|
|
218
|
+
act=DialogCalAct.next_y, year=year, month=1, day=1
|
|
219
|
+
).pack(),
|
|
220
|
+
)
|
|
221
|
+
)
|
|
222
|
+
kb.append(nav_row)
|
|
223
|
+
return InlineKeyboardMarkup(row_width=5, inline_keyboard=kb)
|
|
224
|
+
|
|
225
|
+
async def process_selection(
|
|
226
|
+
self, query: CallbackQuery, data: DialogCalendarCallback
|
|
227
|
+
) -> tuple:
|
|
228
|
+
return_data = (False, None)
|
|
229
|
+
if data.act == DialogCalAct.ignore:
|
|
230
|
+
await query.answer(cache_time=60)
|
|
231
|
+
if data.act == DialogCalAct.set_y:
|
|
232
|
+
await query.message.edit_reply_markup(
|
|
233
|
+
reply_markup=await self._get_month_kb(int(data.year))
|
|
234
|
+
)
|
|
235
|
+
if data.act == DialogCalAct.prev_y:
|
|
236
|
+
new_year = int(data.year) - 5
|
|
237
|
+
await query.message.edit_reply_markup(
|
|
238
|
+
reply_markup=await self.start_calendar(year=new_year)
|
|
239
|
+
)
|
|
240
|
+
if data.act == DialogCalAct.next_y:
|
|
241
|
+
new_year = int(data.year) + 5
|
|
242
|
+
await query.message.edit_reply_markup(
|
|
243
|
+
reply_markup=await self.start_calendar(year=new_year)
|
|
244
|
+
)
|
|
245
|
+
if data.act == DialogCalAct.start:
|
|
246
|
+
await query.message.edit_reply_markup(
|
|
247
|
+
reply_markup=await self.start_calendar(int(data.year))
|
|
248
|
+
)
|
|
249
|
+
if data.act == DialogCalAct.set_m:
|
|
250
|
+
await query.message.edit_reply_markup(
|
|
251
|
+
reply_markup=await self._get_days_kb(int(data.year), int(data.month))
|
|
252
|
+
)
|
|
253
|
+
if data.act == DialogCalAct.day:
|
|
254
|
+
|
|
255
|
+
return await self.process_day_select(data, query)
|
|
256
|
+
|
|
257
|
+
if data.act == DialogCalAct.cancel:
|
|
258
|
+
await query.message.delete_reply_markup()
|
|
259
|
+
return return_data
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
from aiogram.filters.callback_data import CallbackData
|
|
5
|
+
from pydantic import BaseModel, Field, conlist
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class SimpleCalAct(str, Enum):
|
|
9
|
+
ignore = "IGNORE"
|
|
10
|
+
prev_y = "PREV-YEAR"
|
|
11
|
+
next_y = "NEXT-YEAR"
|
|
12
|
+
prev_m = "PREV-MONTH"
|
|
13
|
+
next_m = "NEXT-MONTH"
|
|
14
|
+
cancel = "CANCEL"
|
|
15
|
+
today = "TODAY"
|
|
16
|
+
day = "DAY"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class DialogCalAct(str, Enum):
|
|
20
|
+
ignore = "IGNORE"
|
|
21
|
+
set_y = "SET-YEAR"
|
|
22
|
+
set_m = "SET-MONTH"
|
|
23
|
+
prev_y = "PREV-YEAR"
|
|
24
|
+
next_y = "NEXT-YEAR"
|
|
25
|
+
cancel = "CANCEL"
|
|
26
|
+
start = "START"
|
|
27
|
+
day = "SET-DAY"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class CalendarCallback(CallbackData, prefix="calendar"):
|
|
31
|
+
act: str
|
|
32
|
+
year: Optional[int] = None
|
|
33
|
+
month: Optional[int] = None
|
|
34
|
+
day: Optional[int] = None
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class SimpleCalendarCallback(CalendarCallback, prefix="simple_calendar"):
|
|
38
|
+
act: SimpleCalAct
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class DialogCalendarCallback(CalendarCallback, prefix="dialog_calendar"):
|
|
42
|
+
act: DialogCalAct
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class CalendarLabels(BaseModel):
|
|
46
|
+
"Schema to pass labels for calendar. Can be used to put in different languages"
|
|
47
|
+
|
|
48
|
+
days_of_week: conlist(str, max_length=7, min_length=7) = [
|
|
49
|
+
"Пн",
|
|
50
|
+
"Вт",
|
|
51
|
+
"Ср",
|
|
52
|
+
"Чт",
|
|
53
|
+
"Пт",
|
|
54
|
+
"Сб",
|
|
55
|
+
"Вс",
|
|
56
|
+
]
|
|
57
|
+
months: conlist(str, max_length=12, min_length=12) = [
|
|
58
|
+
"Янв",
|
|
59
|
+
"Фев",
|
|
60
|
+
"Мар",
|
|
61
|
+
"Апр",
|
|
62
|
+
"Май",
|
|
63
|
+
"Июн",
|
|
64
|
+
"Июл",
|
|
65
|
+
"Авг",
|
|
66
|
+
"Сен",
|
|
67
|
+
"Окт",
|
|
68
|
+
"Ноя",
|
|
69
|
+
"Дек",
|
|
70
|
+
]
|
|
71
|
+
cancel_caption: str = Field(
|
|
72
|
+
default="Отмена", description="Caprion for Cancel button"
|
|
73
|
+
)
|
|
74
|
+
today_caption: str = Field(default="Сегодня", description="Caprion for Today button")
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
HIGHLIGHT_FORMAT = "[{}]"
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def highlight(text):
|
|
81
|
+
return HIGHLIGHT_FORMAT.format(text)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def superscript(text):
|
|
85
|
+
normal = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-=()"
|
|
86
|
+
super_s = "ᴬᴮᶜᴰᴱᶠᴳᴴᴵᴶᴷᴸᴹᴺᴼᴾQᴿˢᵀᵁⱽᵂˣʸᶻᵃᵇᶜᵈᵉᶠᵍʰᶦʲᵏˡᵐⁿᵒᵖ۹ʳˢᵗᵘᵛʷˣʸᶻ⁰¹²³⁴⁵⁶⁷⁸⁹⁺⁻⁼⁽⁾"
|
|
87
|
+
output = ""
|
|
88
|
+
for i in text:
|
|
89
|
+
output += super_s[normal.index(i)] if i in normal else i
|
|
90
|
+
return output
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def subscript(text):
|
|
94
|
+
normal = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-=()"
|
|
95
|
+
sub_s = "ₐ₈CDₑբGₕᵢⱼₖₗₘₙₒₚQᵣₛₜᵤᵥwₓᵧZₐ♭꜀ᑯₑբ₉ₕᵢⱼₖₗₘₙₒₚ૧ᵣₛₜᵤᵥwₓᵧ₂₀₁₂₃₄₅₆₇₈₉₊₋₌₍₎"
|
|
96
|
+
output = ""
|
|
97
|
+
for i in text:
|
|
98
|
+
output += sub_s[normal.index(i)] if i in normal else i
|
|
99
|
+
return output
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import calendar
|
|
2
|
+
from datetime import datetime, timedelta
|
|
3
|
+
|
|
4
|
+
from aiogram.types import (CallbackQuery, InlineKeyboardButton,
|
|
5
|
+
InlineKeyboardMarkup)
|
|
6
|
+
|
|
7
|
+
from .common import GenericCalendar
|
|
8
|
+
from .schemas import (SimpleCalAct, SimpleCalendarCallback, highlight,
|
|
9
|
+
superscript)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class SimpleCalendar(GenericCalendar):
|
|
13
|
+
|
|
14
|
+
ignore_callback = SimpleCalendarCallback(
|
|
15
|
+
act=SimpleCalAct.ignore
|
|
16
|
+
).pack() # placeholder for no answer buttons
|
|
17
|
+
|
|
18
|
+
async def start_calendar(
|
|
19
|
+
self, year: int = datetime.now().year, month: int = datetime.now().month
|
|
20
|
+
) -> InlineKeyboardMarkup:
|
|
21
|
+
"""
|
|
22
|
+
Creates an inline keyboard with the provided year and month
|
|
23
|
+
:param int year: Year to use in the calendar, if None the current year is used.
|
|
24
|
+
:param int month: Month to use in the calendar, if None the current month is used.
|
|
25
|
+
:return: Returns InlineKeyboardMarkup object with the calendar.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
today = datetime.now()
|
|
29
|
+
now_weekday = self._labels.days_of_week[today.weekday()]
|
|
30
|
+
now_month, now_year, now_day = today.month, today.year, today.day
|
|
31
|
+
|
|
32
|
+
def highlight_month():
|
|
33
|
+
month_str = self._labels.months[month - 1]
|
|
34
|
+
if now_month == month and now_year == year:
|
|
35
|
+
return highlight(month_str)
|
|
36
|
+
return month_str
|
|
37
|
+
|
|
38
|
+
def highlight_weekday():
|
|
39
|
+
if now_month == month and now_year == year and now_weekday == weekday:
|
|
40
|
+
return highlight(weekday)
|
|
41
|
+
return weekday
|
|
42
|
+
|
|
43
|
+
def format_day_string():
|
|
44
|
+
date_to_check = datetime(year, month, day)
|
|
45
|
+
if self.min_date and date_to_check < self.min_date:
|
|
46
|
+
return superscript(str(day))
|
|
47
|
+
elif self.max_date and date_to_check > self.max_date:
|
|
48
|
+
return superscript(str(day))
|
|
49
|
+
return str(day)
|
|
50
|
+
|
|
51
|
+
def highlight_day():
|
|
52
|
+
day_string = format_day_string()
|
|
53
|
+
if now_month == month and now_year == year and now_day == day:
|
|
54
|
+
return highlight(day_string)
|
|
55
|
+
return day_string
|
|
56
|
+
|
|
57
|
+
# building a calendar keyboard
|
|
58
|
+
kb = []
|
|
59
|
+
|
|
60
|
+
# inline_kb = InlineKeyboardMarkup(row_width=7)
|
|
61
|
+
# First row - Year
|
|
62
|
+
years_row = []
|
|
63
|
+
years_row.append(
|
|
64
|
+
InlineKeyboardButton(
|
|
65
|
+
text="<<",
|
|
66
|
+
callback_data=SimpleCalendarCallback(
|
|
67
|
+
act=SimpleCalAct.prev_y, year=year, month=month, day=1
|
|
68
|
+
).pack(),
|
|
69
|
+
)
|
|
70
|
+
)
|
|
71
|
+
years_row.append(
|
|
72
|
+
InlineKeyboardButton(
|
|
73
|
+
text=str(year) if year != now_year else highlight(year),
|
|
74
|
+
callback_data=self.ignore_callback,
|
|
75
|
+
)
|
|
76
|
+
)
|
|
77
|
+
years_row.append(
|
|
78
|
+
InlineKeyboardButton(
|
|
79
|
+
text=">>",
|
|
80
|
+
callback_data=SimpleCalendarCallback(
|
|
81
|
+
act=SimpleCalAct.next_y, year=year, month=month, day=1
|
|
82
|
+
).pack(),
|
|
83
|
+
)
|
|
84
|
+
)
|
|
85
|
+
kb.append(years_row)
|
|
86
|
+
|
|
87
|
+
# Month nav Buttons
|
|
88
|
+
month_row = []
|
|
89
|
+
month_row.append(
|
|
90
|
+
InlineKeyboardButton(
|
|
91
|
+
text="<",
|
|
92
|
+
callback_data=SimpleCalendarCallback(
|
|
93
|
+
act=SimpleCalAct.prev_m, year=year, month=month, day=1
|
|
94
|
+
).pack(),
|
|
95
|
+
)
|
|
96
|
+
)
|
|
97
|
+
month_row.append(
|
|
98
|
+
InlineKeyboardButton(
|
|
99
|
+
text=highlight_month(), callback_data=self.ignore_callback
|
|
100
|
+
)
|
|
101
|
+
)
|
|
102
|
+
month_row.append(
|
|
103
|
+
InlineKeyboardButton(
|
|
104
|
+
text=">",
|
|
105
|
+
callback_data=SimpleCalendarCallback(
|
|
106
|
+
act=SimpleCalAct.next_m, year=year, month=month, day=1
|
|
107
|
+
).pack(),
|
|
108
|
+
)
|
|
109
|
+
)
|
|
110
|
+
kb.append(month_row)
|
|
111
|
+
|
|
112
|
+
# Week Days
|
|
113
|
+
week_days_labels_row = []
|
|
114
|
+
for weekday in self._labels.days_of_week:
|
|
115
|
+
week_days_labels_row.append(
|
|
116
|
+
InlineKeyboardButton(
|
|
117
|
+
text=highlight_weekday(), callback_data=self.ignore_callback
|
|
118
|
+
)
|
|
119
|
+
)
|
|
120
|
+
kb.append(week_days_labels_row)
|
|
121
|
+
|
|
122
|
+
# Calendar rows - Days of month
|
|
123
|
+
month_calendar = calendar.monthcalendar(year, month)
|
|
124
|
+
|
|
125
|
+
for week in month_calendar:
|
|
126
|
+
days_row = []
|
|
127
|
+
for day in week:
|
|
128
|
+
if day == 0:
|
|
129
|
+
days_row.append(
|
|
130
|
+
InlineKeyboardButton(
|
|
131
|
+
text=" ", callback_data=self.ignore_callback
|
|
132
|
+
)
|
|
133
|
+
)
|
|
134
|
+
continue
|
|
135
|
+
days_row.append(
|
|
136
|
+
InlineKeyboardButton(
|
|
137
|
+
text=highlight_day(),
|
|
138
|
+
callback_data=SimpleCalendarCallback(
|
|
139
|
+
act=SimpleCalAct.day, year=year, month=month, day=day
|
|
140
|
+
).pack(),
|
|
141
|
+
)
|
|
142
|
+
)
|
|
143
|
+
kb.append(days_row)
|
|
144
|
+
|
|
145
|
+
# nav today & cancel button
|
|
146
|
+
cancel_row = []
|
|
147
|
+
cancel_row.append(
|
|
148
|
+
InlineKeyboardButton(
|
|
149
|
+
text=self._labels.cancel_caption,
|
|
150
|
+
callback_data=SimpleCalendarCallback(
|
|
151
|
+
act=SimpleCalAct.cancel, year=year, month=month, day=day
|
|
152
|
+
).pack(),
|
|
153
|
+
)
|
|
154
|
+
)
|
|
155
|
+
cancel_row.append(
|
|
156
|
+
InlineKeyboardButton(text=" ", callback_data=self.ignore_callback)
|
|
157
|
+
)
|
|
158
|
+
cancel_row.append(
|
|
159
|
+
InlineKeyboardButton(
|
|
160
|
+
text=self._labels.today_caption,
|
|
161
|
+
callback_data=SimpleCalendarCallback(
|
|
162
|
+
act=SimpleCalAct.today, year=year, month=month, day=day
|
|
163
|
+
).pack(),
|
|
164
|
+
)
|
|
165
|
+
)
|
|
166
|
+
kb.append(cancel_row)
|
|
167
|
+
return InlineKeyboardMarkup(row_width=7, inline_keyboard=kb)
|
|
168
|
+
|
|
169
|
+
async def _update_calendar(self, query: CallbackQuery, with_date: datetime):
|
|
170
|
+
await query.message.edit_reply_markup(
|
|
171
|
+
reply_markup=await self.start_calendar(
|
|
172
|
+
int(with_date.year), int(with_date.month)
|
|
173
|
+
)
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
async def process_selection(
|
|
177
|
+
self, query: CallbackQuery, data: SimpleCalendarCallback
|
|
178
|
+
) -> tuple:
|
|
179
|
+
"""
|
|
180
|
+
Process the callback_query. This method generates a new calendar if forward or
|
|
181
|
+
backward is pressed. This method should be called inside a CallbackQueryHandler.
|
|
182
|
+
:param query: callback_query, as provided by the CallbackQueryHandler
|
|
183
|
+
:param data: callback_data, dictionary, set by calendar_callback
|
|
184
|
+
:return: Returns a tuple (Boolean,datetime), indicating if a date is selected
|
|
185
|
+
and returning the date if so.
|
|
186
|
+
"""
|
|
187
|
+
return_data = (False, None)
|
|
188
|
+
|
|
189
|
+
# processing empty buttons, answering with no action
|
|
190
|
+
if data.act == SimpleCalAct.ignore:
|
|
191
|
+
await query.answer(cache_time=60)
|
|
192
|
+
return return_data
|
|
193
|
+
|
|
194
|
+
temp_date = datetime(int(data.year), int(data.month), 1)
|
|
195
|
+
|
|
196
|
+
# user picked a day button, return date
|
|
197
|
+
if data.act == SimpleCalAct.day:
|
|
198
|
+
return await self.process_day_select(data, query)
|
|
199
|
+
|
|
200
|
+
# user navigates to previous year, editing message with new calendar
|
|
201
|
+
if data.act == SimpleCalAct.prev_y:
|
|
202
|
+
prev_date = datetime(int(data.year) - 1, int(data.month), 1)
|
|
203
|
+
await self._update_calendar(query, prev_date)
|
|
204
|
+
# user navigates to next year, editing message with new calendar
|
|
205
|
+
if data.act == SimpleCalAct.next_y:
|
|
206
|
+
next_date = datetime(int(data.year) + 1, int(data.month), 1)
|
|
207
|
+
await self._update_calendar(query, next_date)
|
|
208
|
+
# user navigates to previous month, editing message with new calendar
|
|
209
|
+
if data.act == SimpleCalAct.prev_m:
|
|
210
|
+
prev_date = temp_date - timedelta(days=1)
|
|
211
|
+
await self._update_calendar(query, prev_date)
|
|
212
|
+
# user navigates to next month, editing message with new calendar
|
|
213
|
+
if data.act == SimpleCalAct.next_m:
|
|
214
|
+
next_date = temp_date + timedelta(days=31)
|
|
215
|
+
await self._update_calendar(query, next_date)
|
|
216
|
+
if data.act == SimpleCalAct.today:
|
|
217
|
+
# Возвращаем сегодняшнюю дату
|
|
218
|
+
await query.answer()
|
|
219
|
+
return (True, datetime.now())
|
|
220
|
+
if data.act == SimpleCalAct.cancel:
|
|
221
|
+
await query.message.delete_reply_markup()
|
|
222
|
+
return ("cancel", None)
|
|
223
|
+
# at some point user clicks DAY button, returning date
|
|
224
|
+
return return_data
|