leadguru-jobs 0.613.0__py3-none-any.whl → 0.614.0__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.
- {leadguru_jobs-0.613.0.dist-info → leadguru_jobs-0.614.0.dist-info}/METADATA +1 -1
- {leadguru_jobs-0.613.0.dist-info → leadguru_jobs-0.614.0.dist-info}/RECORD +8 -8
- {leadguru_jobs-0.613.0.dist-info → leadguru_jobs-0.614.0.dist-info}/WHEEL +1 -1
- lgt_jobs/jobs/chat_history.py +31 -11
- lgt_jobs/lgt_data/enums.py +6 -7
- lgt_jobs/lgt_data/model.py +6 -98
- lgt_jobs/lgt_data/mongo_repository.py +0 -5
- {leadguru_jobs-0.613.0.dist-info → leadguru_jobs-0.614.0.dist-info}/top_level.txt +0 -0
@@ -13,7 +13,7 @@ lgt_jobs/assets/images/mail.png,sha256=eORzQcAMkFr7DjgtABVhJ_zFuXgO7OXv78lLF4b39
|
|
13
13
|
lgt_jobs/jobs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
14
14
|
lgt_jobs/jobs/analytics.py,sha256=IIsWt4A1qUw3w-S-8W16uKY1FRVWfXXA41_mu4uCNiM,979
|
15
15
|
lgt_jobs/jobs/bot_stats_update.py,sha256=JRPPyqFvGHWuH_NQJNzIDy3DC0Joz14goNExqp-YUrw,8683
|
16
|
-
lgt_jobs/jobs/chat_history.py,sha256=
|
16
|
+
lgt_jobs/jobs/chat_history.py,sha256=KiwiPl8FuI7FphfZT5VgKSTiO_W9j7tNyiSbnSe5GAs,6106
|
17
17
|
lgt_jobs/jobs/connect_sources.py,sha256=_eA86KnS3AC6YCI1xk7VCV7lFmPRxta-wUStfdmajQU,4790
|
18
18
|
lgt_jobs/jobs/inbox_leads.py,sha256=OSX-FNx27gWEKNBBc-hyq2odCxXytz7WHtQJajtz274,5670
|
19
19
|
lgt_jobs/jobs/load_slack_people.py,sha256=az3Pl8_0nUXizShpCksH6XMHFALLkta4QpMr_MkM9Io,3199
|
@@ -41,14 +41,14 @@ lgt_jobs/lgt_common/slack_client/web_client.py,sha256=6ybC5v-oK-Kuat8qM2fskS6nCn
|
|
41
41
|
lgt_jobs/lgt_data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
42
42
|
lgt_jobs/lgt_data/analytics.py,sha256=fiN88zcIxs_bRMmXL7ftp4FvBeJ5I7QBPE4tbwKJ39E,21689
|
43
43
|
lgt_jobs/lgt_data/engine.py,sha256=Rsbz-CApAo_TVDssdjBkv8v_fVOZm_Uh1S6W4fnaEWo,7728
|
44
|
-
lgt_jobs/lgt_data/enums.py,sha256=
|
44
|
+
lgt_jobs/lgt_data/enums.py,sha256=jBH5WGBtDAvFrh4iiPIzlQ-XrImMpuwqstuasG55mJ0,2209
|
45
45
|
lgt_jobs/lgt_data/helpers.py,sha256=NDa-V5EYaJfkGoWsmQSwSe6N_jxNxs8tHRQzW1iST6k,480
|
46
|
-
lgt_jobs/lgt_data/model.py,sha256=
|
47
|
-
lgt_jobs/lgt_data/mongo_repository.py,sha256=
|
46
|
+
lgt_jobs/lgt_data/model.py,sha256=7a10N7WAqX2ZdstHTQrvp87rtYB9UPhWLidOYQJnKXM,29040
|
47
|
+
lgt_jobs/lgt_data/mongo_repository.py,sha256=m1UHtBwiZ8JGIMZ6ud2PZadm8sgq4InG0glR7-sBLwE,46849
|
48
48
|
lgt_jobs/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
49
49
|
lgt_jobs/services/web_client.py,sha256=GLWsJkIC8rv6xLFaLwcMm4EwBlVDu0njORwkZqBJaE4,2086
|
50
50
|
lgt_jobs/templates/new_message.html,sha256=dZl8UmdAOOMq4nidvAgMFroSrTV7Pw0RWt2yLp_2idg,6989
|
51
|
-
leadguru_jobs-0.
|
52
|
-
leadguru_jobs-0.
|
53
|
-
leadguru_jobs-0.
|
54
|
-
leadguru_jobs-0.
|
51
|
+
leadguru_jobs-0.614.0.dist-info/METADATA,sha256=0-McWeTe_tD4ftBoxZKKzosIGfXv4hU7Gbv5H3ljqJM,1319
|
52
|
+
leadguru_jobs-0.614.0.dist-info/WHEEL,sha256=YiKiUUeZQGmGJoR_0N1Y933DOBowq4AIvDe2-UIy8E4,91
|
53
|
+
leadguru_jobs-0.614.0.dist-info/top_level.txt,sha256=rIuw1DqwbnZyeoarBSC-bYeGOhv9mZBs7_afl9q4_JI,9
|
54
|
+
leadguru_jobs-0.614.0.dist-info/RECORD,,
|
lgt_jobs/jobs/chat_history.py
CHANGED
@@ -3,12 +3,15 @@ from abc import ABC
|
|
3
3
|
from typing import Optional, List
|
4
4
|
import logging as log
|
5
5
|
from lgt_jobs.lgt_common.slack_client.web_client import SlackWebClient, SlackMessageConvertService
|
6
|
-
from lgt_jobs.lgt_data.
|
6
|
+
from lgt_jobs.lgt_data.helpers import get_help_text
|
7
|
+
from lgt_jobs.lgt_data.model import ChatMessage, UserModel, UserContact, DedicatedBotModel
|
7
8
|
from lgt_jobs.lgt_data.mongo_repository import UserMongoRepository, DedicatedBotRepository, UserContactsRepository, \
|
8
9
|
ChatRepository
|
9
10
|
from pydantic import BaseModel
|
10
|
-
from lgt_jobs.lgt_data.enums import SourceType
|
11
|
-
from lgt_jobs.runner import BaseBackgroundJob, BaseBackgroundJobData
|
11
|
+
from lgt_jobs.lgt_data.enums import SourceType, ImageName
|
12
|
+
from lgt_jobs.runner import BaseBackgroundJob, BaseBackgroundJobData, BackgroundJobRunner
|
13
|
+
from lgt_jobs.env import portal_url
|
14
|
+
from lgt_jobs.smtp import SendMailJobData, SendMailJob
|
12
15
|
|
13
16
|
"""
|
14
17
|
Load slack chat history
|
@@ -47,7 +50,7 @@ class LoadChatHistoryJob(BaseBackgroundJob, ABC):
|
|
47
50
|
|
48
51
|
log.info(f"[LoadChatHistoryJob]: processing {len(contacts)} contacts for user: {user.email}")
|
49
52
|
for contact in contacts:
|
50
|
-
message = self._update_history(contact=contact, bot=bot)
|
53
|
+
message = self._update_history(user=user, contact=contact, bot=bot)
|
51
54
|
|
52
55
|
if not message:
|
53
56
|
continue
|
@@ -62,13 +65,13 @@ class LoadChatHistoryJob(BaseBackgroundJob, ABC):
|
|
62
65
|
last_message = message
|
63
66
|
last_message_contact = contact
|
64
67
|
|
65
|
-
|
66
|
-
|
68
|
+
has_to_be_notified = (not user.new_message_notified_at or
|
69
|
+
(last_message and last_message.created_at > user.new_message_notified_at))
|
70
|
+
|
67
71
|
if last_message and has_to_be_notified and last_message.user == last_message_contact.sender_id:
|
68
|
-
user
|
69
|
-
|
70
|
-
|
71
|
-
UserMongoRepository().set(data.user_id, notification_settings=user.notification_settings.to_dic())
|
72
|
+
LoadChatHistoryJob._notify_about_new_messages(user, last_message_contact, last_message_bot,
|
73
|
+
data.template_path)
|
74
|
+
UserMongoRepository().set(data.user_id, new_message_notified_at=datetime.now(UTC))
|
72
75
|
|
73
76
|
def _get_new_messages(self, contact: UserContact, bot: DedicatedBotModel, slack_chat: List[ChatMessage]):
|
74
77
|
messages = self.chat_repo.get_list(sender_id=contact.sender_id, bot_id=bot.id)
|
@@ -79,7 +82,7 @@ class LoadChatHistoryJob(BaseBackgroundJob, ABC):
|
|
79
82
|
new_messages.append(message)
|
80
83
|
return new_messages
|
81
84
|
|
82
|
-
def _update_history(self, contact: UserContact, bot: DedicatedBotModel) -> Optional[ChatMessage]:
|
85
|
+
def _update_history(self, user: UserModel, contact: UserContact, bot: DedicatedBotModel) -> Optional[ChatMessage]:
|
83
86
|
slack_client = SlackWebClient(bot.token, bot.cookies)
|
84
87
|
try:
|
85
88
|
chat_id = slack_client.im_open(contact.sender_id).get('channel', {}).get('id')
|
@@ -108,3 +111,20 @@ class LoadChatHistoryJob(BaseBackgroundJob, ABC):
|
|
108
111
|
return new_messages[-1]
|
109
112
|
|
110
113
|
return None
|
114
|
+
|
115
|
+
@staticmethod
|
116
|
+
def _notify_about_new_messages(user: UserModel, contact: UserContact, bot: DedicatedBotModel, template_path: str):
|
117
|
+
with open(template_path, mode='r') as template_file:
|
118
|
+
html = template_file.read()
|
119
|
+
chat_url = f'{portal_url}/feed?senderId={contact.sender_id}&sourceId={bot.source.source_id}'
|
120
|
+
html = html.replace("$$USER_NAME$$", contact.name if hasattr(contact, 'name') else contact.real_name)
|
121
|
+
html = html.replace("$$PORTAL_LINK$$", chat_url)
|
122
|
+
html = html.replace("$$HELP_TEXT$$", get_help_text(user))
|
123
|
+
message_data = {
|
124
|
+
"html": html,
|
125
|
+
"subject": 'New message(s) on Leadguru',
|
126
|
+
"recipient": user.email,
|
127
|
+
"images": [ImageName.LOGO, ImageName.ARROW, ImageName.MAIL]
|
128
|
+
}
|
129
|
+
|
130
|
+
BackgroundJobRunner.submit(SendMailJob, SendMailJobData(**message_data))
|
lgt_jobs/lgt_data/enums.py
CHANGED
@@ -54,6 +54,12 @@ class DefaultBoards(str, Enum):
|
|
54
54
|
Primary = 'Primary board'
|
55
55
|
|
56
56
|
|
57
|
+
class BotEventType(str, Enum):
|
58
|
+
CREATE = 'BotAdded'
|
59
|
+
UPDATE = 'BotUpdated'
|
60
|
+
DELETE = 'BotDeleted'
|
61
|
+
|
62
|
+
|
57
63
|
class ImageName(str, Enum):
|
58
64
|
ARROW = 'arrow.png'
|
59
65
|
CRY = 'cry.png'
|
@@ -90,10 +96,3 @@ class FeaturesEnum(str, Enum):
|
|
90
96
|
class FeatureOptions(str, Enum):
|
91
97
|
BASIC = 'basic'
|
92
98
|
ADVANCED = 'advanced'
|
93
|
-
|
94
|
-
|
95
|
-
class NotificationType(str, Enum):
|
96
|
-
INSTANTLY = 'instantly'
|
97
|
-
ONCE_A_DAY = 'once_a_day'
|
98
|
-
ONCE_A_WEEK = 'once_a_week'
|
99
|
-
WEEK_BEFORE = 'week_before'
|
lgt_jobs/lgt_data/model.py
CHANGED
@@ -2,9 +2,9 @@ from __future__ import annotations
|
|
2
2
|
import copy
|
3
3
|
import json
|
4
4
|
from abc import ABC
|
5
|
-
from datetime import datetime, UTC
|
5
|
+
from datetime import datetime, UTC
|
6
6
|
from typing import Optional, List
|
7
|
-
from
|
7
|
+
from .enums import UserRole, SourceType, FeaturesEnum, FeatureOptions
|
8
8
|
from bson import ObjectId
|
9
9
|
|
10
10
|
|
@@ -256,95 +256,6 @@ class MessageModel:
|
|
256
256
|
return result
|
257
257
|
|
258
258
|
|
259
|
-
class Notification(DictionaryModel):
|
260
|
-
def __init__(self):
|
261
|
-
self.enabled: bool = True
|
262
|
-
self.type: NotificationType = NotificationType.INSTANTLY
|
263
|
-
self.day: int | None = None
|
264
|
-
self.hour: int | None = None
|
265
|
-
self.last_notification: datetime | None = None
|
266
|
-
self.need_to_notify: bool = False
|
267
|
-
self.attributes: list[str] = []
|
268
|
-
|
269
|
-
@property
|
270
|
-
def need_to_notify_now(self) -> bool:
|
271
|
-
if not self.enabled or not self.need_to_notify:
|
272
|
-
return False
|
273
|
-
|
274
|
-
now = datetime.now(UTC)
|
275
|
-
current_week_day = datetime.isoweekday(now)
|
276
|
-
if self.type == NotificationType.ONCE_A_WEEK and current_week_day != self.day:
|
277
|
-
return False
|
278
|
-
|
279
|
-
if ((self.type == NotificationType.ONCE_A_DAY or self.type == NotificationType.ONCE_A_WEEK)
|
280
|
-
and now.hour != self.hour):
|
281
|
-
return False
|
282
|
-
|
283
|
-
if ((self.type == NotificationType.ONCE_A_DAY or self.type == NotificationType.ONCE_A_WEEK)
|
284
|
-
and self.last_notification and self.last_notification > now - timedelta(hours=1)):
|
285
|
-
return False
|
286
|
-
|
287
|
-
return True
|
288
|
-
|
289
|
-
@staticmethod
|
290
|
-
def need_to_notify_week_before(date: datetime) -> bool:
|
291
|
-
return datetime.now(UTC) < (date + timedelta(7))
|
292
|
-
|
293
|
-
|
294
|
-
class NotificationSettings(DictionaryModel):
|
295
|
-
def __init__(self):
|
296
|
-
self.incoming_messages: Notification | None = None
|
297
|
-
self.inbox: Notification | None = None
|
298
|
-
self.source_deactivation: Notification | None = None
|
299
|
-
self.billing: Notification | None = None
|
300
|
-
self.bulk_replies: Notification | None = None
|
301
|
-
self.bulk_reactions: Notification | None = None
|
302
|
-
self.follow_ups: Notification | None = None
|
303
|
-
|
304
|
-
@classmethod
|
305
|
-
def from_dic(cls, dic: dict):
|
306
|
-
if not dic:
|
307
|
-
return None
|
308
|
-
|
309
|
-
model: NotificationSettings = cls()
|
310
|
-
model.incoming_messages = Notification.from_dic(dic.get('incoming_messages'))
|
311
|
-
model.inbox = Notification.from_dic(dic.get('inbox'))
|
312
|
-
model.source_deactivation = Notification.from_dic(dic.get('source_deactivation'))
|
313
|
-
model.billing = Notification.from_dic(dic.get('billing'))
|
314
|
-
model.bulk_replies = Notification.from_dic(dic.get('bulk_replies'))
|
315
|
-
model.bulk_reactions = Notification.from_dic(dic.get('bulk_reactions'))
|
316
|
-
model.follow_ups = Notification.from_dic(dic.get('follow_ups'))
|
317
|
-
return model
|
318
|
-
|
319
|
-
def to_dic(self):
|
320
|
-
result = copy.deepcopy(self.__dict__)
|
321
|
-
|
322
|
-
if result.get('incoming_messages'):
|
323
|
-
result['incoming_messages'] = Notification.to_dic(result.get('incoming_messages'))
|
324
|
-
if result.get('inbox'):
|
325
|
-
result['inbox'] = Notification.to_dic(result.get('inbox'))
|
326
|
-
if result.get('source_deactivation'):
|
327
|
-
result['source_deactivation'] = Notification.to_dic(result.get('source_deactivation'))
|
328
|
-
if result.get('billing'):
|
329
|
-
result['billing'] = Notification.to_dic(result.get('billing'))
|
330
|
-
if result.get('bulk_replies'):
|
331
|
-
result['bulk_replies'] = Notification.to_dic(result.get('bulk_replies'))
|
332
|
-
if result.get('bulk_reactions'):
|
333
|
-
result['bulk_reactions'] = Notification.to_dic(result.get('bulk_reactions'))
|
334
|
-
if result.get('follow_ups'):
|
335
|
-
result['follow_ups'] = Notification.to_dic(result.get('follow_ups'))
|
336
|
-
|
337
|
-
return result
|
338
|
-
|
339
|
-
|
340
|
-
class GeneralSettings(DictionaryModel):
|
341
|
-
def __init__(self):
|
342
|
-
self.theme: str | None = None
|
343
|
-
self.ask_pipeline_and_status: bool = False
|
344
|
-
self.ask_follow_up: bool = False
|
345
|
-
self.dashboard_is_starting_page: bool = False
|
346
|
-
|
347
|
-
|
348
259
|
class UserModel(BaseModel):
|
349
260
|
def __init__(self):
|
350
261
|
super().__init__()
|
@@ -360,11 +271,13 @@ class UserModel(BaseModel):
|
|
360
271
|
self.company_web_site: str = ''
|
361
272
|
self.company_description: str = ''
|
362
273
|
self.position: str = ''
|
363
|
-
|
274
|
+
self.new_message_notified_at: Optional[datetime] = None
|
364
275
|
self.leads_limit: Optional[int] = None
|
365
276
|
self.leads_proceeded: Optional[int] = None
|
366
277
|
self.leads_filtered: Optional[int] = None
|
367
278
|
self.leads_limit_updated_at: Optional[int] = None
|
279
|
+
self.keywords: Optional[List[str]] = None
|
280
|
+
self.block_words: Optional[List[str]] = None
|
368
281
|
self.paid_lead_price: int = 1
|
369
282
|
self.state: int = 0
|
370
283
|
self.credits_exceeded_at: Optional[datetime] = None
|
@@ -379,8 +292,6 @@ class UserModel(BaseModel):
|
|
379
292
|
self.subscription_name: str | None = None
|
380
293
|
self.subscription_expiration_notified = False
|
381
294
|
self.subscription_expiration_warning_notified = False
|
382
|
-
self.notification_settings: NotificationSettings | None = None
|
383
|
-
self.general_setting: GeneralSettings | None = None
|
384
295
|
|
385
296
|
@classmethod
|
386
297
|
def from_dic(cls, dic: dict):
|
@@ -397,16 +308,13 @@ class UserModel(BaseModel):
|
|
397
308
|
model.slack_profile = Profile.from_dic(dic.get('slack_profile'))
|
398
309
|
model.slack_users = [SlackUser.from_dic(user) for user in dic.get('slack_users', [])]
|
399
310
|
model.discord_users = [DiscordUser.from_dic(user) for user in dic.get('discord_users', [])]
|
400
|
-
model.notification_settings = NotificationSettings.from_dic(dic.get('notification_settings'))
|
401
311
|
return model
|
402
312
|
|
403
313
|
def to_dic(self):
|
404
314
|
result = copy.deepcopy(self.__dict__)
|
405
315
|
|
406
|
-
if result.get('slack_profile'):
|
316
|
+
if result.get('slack_profile', None):
|
407
317
|
result['slack_profile'] = result.get('slack_profile').__dict__
|
408
|
-
if result.get('notification_settings'):
|
409
|
-
result['notification_settings'] = NotificationSettings.to_dic(result.get('notification_settings'))
|
410
318
|
|
411
319
|
return result
|
412
320
|
|
@@ -71,7 +71,6 @@ class UserMongoRepository(BaseMongoRepository):
|
|
71
71
|
subscription_expired = kwargs.get('subscription_expired')
|
72
72
|
connected_slack_email = kwargs.get('connected_slack_email')
|
73
73
|
soon_subscription_expiration = kwargs.get('soon_subscription_expiration')
|
74
|
-
has_new_message = kwargs.get('has_new_message')
|
75
74
|
min_days = kwargs.get('min_days_soon_subscription_expiration', 3)
|
76
75
|
|
77
76
|
if subscription_expired:
|
@@ -82,10 +81,6 @@ class UserMongoRepository(BaseMongoRepository):
|
|
82
81
|
pipeline['subscription_expired_at'] = {'$lte': datetime.now(UTC) + timedelta(min_days)}
|
83
82
|
pipeline['subscription_expiration_warning_notified'] = False
|
84
83
|
|
85
|
-
if has_new_message:
|
86
|
-
pipeline['notification_settings.incoming_messages.enabled'] = True
|
87
|
-
pipeline['notification_settings.incoming_messages.need_to_notify'] = True
|
88
|
-
|
89
84
|
if connected_slack_email:
|
90
85
|
pipeline['slack_users.email'] = connected_slack_email
|
91
86
|
|
File without changes
|