leadguru-jobs 0.613.0__tar.gz → 0.614.0__tar.gz

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.
Files changed (61) hide show
  1. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/PKG-INFO +1 -1
  2. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/leadguru_jobs.egg-info/PKG-INFO +1 -1
  3. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/jobs/chat_history.py +31 -11
  4. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/lgt_data/enums.py +6 -7
  5. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/lgt_data/model.py +6 -98
  6. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/lgt_data/mongo_repository.py +0 -5
  7. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/MANIFEST.in +0 -0
  8. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/README.md +0 -0
  9. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/leadguru_jobs.egg-info/SOURCES.txt +0 -0
  10. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/leadguru_jobs.egg-info/dependency_links.txt +0 -0
  11. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/leadguru_jobs.egg-info/not-zip-safe +0 -0
  12. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/leadguru_jobs.egg-info/requires.txt +0 -0
  13. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/leadguru_jobs.egg-info/top_level.txt +0 -0
  14. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/__init__.py +0 -0
  15. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/assets/images/arrow.png +0 -0
  16. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/assets/images/firework.png +0 -0
  17. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/assets/images/lock.png +0 -0
  18. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/assets/images/logo.png +0 -0
  19. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/assets/images/mail.png +0 -0
  20. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/basejobs.py +0 -0
  21. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/env.py +0 -0
  22. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/jobs/__init__.py +0 -0
  23. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/jobs/analytics.py +0 -0
  24. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/jobs/bot_stats_update.py +0 -0
  25. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/jobs/connect_sources.py +0 -0
  26. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/jobs/inbox_leads.py +0 -0
  27. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/jobs/load_slack_people.py +0 -0
  28. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/jobs/mass_message.py +0 -0
  29. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/jobs/send_code.py +0 -0
  30. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/jobs/send_slack_message.py +0 -0
  31. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/jobs/update_slack_profile.py +0 -0
  32. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/jobs/user_balance_update.py +0 -0
  33. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/jobs/workspace_connect.py +0 -0
  34. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/lgt_common/__init__.py +0 -0
  35. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/lgt_common/discord_client/__init__.py +0 -0
  36. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/lgt_common/discord_client/discord_client.py +0 -0
  37. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/lgt_common/discord_client/methods.py +0 -0
  38. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/lgt_common/enums/__init__.py +0 -0
  39. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/lgt_common/enums/slack_errors.py +0 -0
  40. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/lgt_common/helpers.py +0 -0
  41. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/lgt_common/lgt_logging.py +0 -0
  42. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/lgt_common/pubsub/__init__.py +0 -0
  43. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/lgt_common/pubsub/messages.py +0 -0
  44. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/lgt_common/pubsub/pubsubfactory.py +0 -0
  45. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/lgt_common/slack_client/__init__.py +0 -0
  46. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/lgt_common/slack_client/methods.py +0 -0
  47. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/lgt_common/slack_client/slack_client.py +0 -0
  48. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/lgt_common/slack_client/web_client.py +0 -0
  49. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/lgt_data/__init__.py +0 -0
  50. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/lgt_data/analytics.py +0 -0
  51. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/lgt_data/engine.py +0 -0
  52. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/lgt_data/helpers.py +0 -0
  53. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/main.py +0 -0
  54. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/runner.py +0 -0
  55. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/services/__init__.py +0 -0
  56. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/services/web_client.py +0 -0
  57. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/simple_job.py +0 -0
  58. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/smtp.py +0 -0
  59. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/lgt_jobs/templates/new_message.html +0 -0
  60. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/setup.cfg +0 -0
  61. {leadguru_jobs-0.613.0 → leadguru_jobs-0.614.0}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: leadguru_jobs
3
- Version: 0.613.0
3
+ Version: 0.614.0
4
4
  Summary: LGT jobs builds
5
5
  Author-email: developer@leadguru.co
6
6
  Classifier: Development Status :: 5 - Production/Stable
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: leadguru_jobs
3
- Version: 0.613.0
3
+ Version: 0.614.0
4
4
  Summary: LGT jobs builds
5
5
  Author-email: developer@leadguru.co
6
6
  Classifier: Development Status :: 5 - Production/Stable
@@ -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.model import ChatMessage, UserContact, DedicatedBotModel
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
- last_notification = user.notification_settings.incoming_messages.last_notification
66
- has_to_be_notified = (not last_notification or (last_message and last_message.created_at > last_notification))
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.notification_settings.incoming_messages.need_to_notify = True
69
- user.notification_settings.incoming_messages.attributes = [last_message_contact.sender_id,
70
- last_message_bot.source.source_id]
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))
@@ -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'
@@ -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, timedelta
5
+ from datetime import datetime, UTC
6
6
  from typing import Optional, List
7
- from lgt_jobs.lgt_data.enums import UserRole, SourceType, FeaturesEnum, FeatureOptions, NotificationType
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
- # self.new_message_notified_at: Optional[datetime] = None # TODO: Move to settings
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