leadguru-jobs 0.638.0__py3-none-any.whl → 0.639.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.
@@ -1,1136 +0,0 @@
1
- from __future__ import annotations
2
- import copy
3
- import json
4
- from abc import ABC
5
- from datetime import datetime, UTC, timedelta
6
- from typing import Optional, List
7
- from lgt_jobs.lgt_data.enums import UserRole, SourceType, FeaturesEnum, FeatureOptions, NotificationType
8
- from bson import ObjectId
9
-
10
-
11
- class DictionaryModel(ABC):
12
- @classmethod
13
- def from_dic(cls, dic: dict):
14
- if not dic:
15
- return None
16
-
17
- model = cls()
18
- for k, v in dic.items():
19
- setattr(model, k, v)
20
-
21
- if '_id' in dic:
22
- setattr(model, 'id', dic['_id'])
23
-
24
- return model
25
-
26
- def to_dic(self):
27
- result = copy.deepcopy(self.__dict__)
28
- return result
29
-
30
-
31
- class BaseModel(DictionaryModel):
32
- def __init__(self):
33
- self.id = None
34
- self.created_at = datetime.now(UTC)
35
-
36
-
37
- class Credentials(BaseModel):
38
- def __init__(self):
39
- super().__init__()
40
- self.token = None
41
- self.cookies = None
42
- self.invalid_creds = False
43
-
44
-
45
- class Source:
46
- def __init__(self):
47
- self.source_type: SourceType | None = None
48
- self.source_name = None
49
- self.source_id = None
50
-
51
- @classmethod
52
- def from_dic(cls, dic: dict):
53
- if not dic:
54
- return None
55
-
56
- model = cls()
57
- for k, v in dic.items():
58
- setattr(model, k, v)
59
-
60
- return model
61
-
62
- def to_dic(self):
63
- result = copy.deepcopy(self.__dict__)
64
- return result
65
-
66
-
67
- class BaseConfig:
68
- def __init__(self):
69
- self.owner = None
70
- self.id = None
71
-
72
- @classmethod
73
- def from_dic(cls, dic: dict):
74
- if not dic:
75
- return None
76
-
77
- model = cls()
78
- for k, v in dic.items():
79
- setattr(model, k, v)
80
-
81
- return model
82
-
83
- def to_dic(self):
84
- result = copy.deepcopy(self.__dict__)
85
- return result
86
-
87
-
88
- class Config(BaseConfig):
89
- def __init__(self):
90
- super().__init__()
91
- self.name = None
92
-
93
-
94
- class BaseBotModel(Credentials):
95
- def __init__(self):
96
- super().__init__()
97
- self.created_by = None
98
- self.user_name = None
99
- self.slack_url = None
100
- self.registration_link = None
101
- self.channels = None
102
- self.connected_channels = None
103
- self.channels_users = None
104
- self.users_count = None
105
- self.recent_messages: List[str] = []
106
- self.icon = None
107
- self.active_channels = {}
108
- self.paused_channels = []
109
- self.source: Source | None = None
110
- self.two_factor_required: bool = False
111
- self.banned: bool = False
112
- self.associated_user = None
113
- self.type: SourceType | None = None
114
- self.deleted = False
115
-
116
-
117
- class DedicatedBotModel(BaseBotModel):
118
- def __init__(self):
119
- super().__init__()
120
- self.user_id: Optional[str] = None
121
- self.updated_at: Optional[datetime] = datetime.now(UTC)
122
- self.servers: List[Server] = []
123
- self.state = 0
124
- self.source: Source | None = None
125
-
126
- def to_dic(self):
127
- result = copy.deepcopy(self.__dict__)
128
-
129
- if result.get('source'):
130
- result['source'] = Source.to_dic(result.get('source'))
131
-
132
- result['servers'] = [Server.to_dic(server) for server in self.servers]
133
- return result
134
-
135
- @classmethod
136
- def from_dic(cls, dic: dict):
137
- if not dic:
138
- return None
139
-
140
- model: DedicatedBotModel = cls()
141
- for k, v in dic.items():
142
- setattr(model, k, v)
143
-
144
- if '_id' in dic:
145
- setattr(model, 'id', dic['_id'])
146
-
147
- model.source = Source.from_dic(dic.get("source"))
148
- model.servers = [Server.from_dic(server) for server in dic.get("servers", [])]
149
- return model
150
-
151
-
152
- class Server:
153
- pass
154
-
155
- def __init__(self):
156
- self.id = None
157
- self.name = None
158
- self.channels: List[Channel] = []
159
- self.icon = None
160
- self.active = False
161
- self.deleted = False
162
- self.subscribers = 0
163
- self.approximate_member_count = 0
164
- self.messages_received: int = 0
165
- self.messages_filtered: int = 0
166
-
167
- @classmethod
168
- def from_dic(cls, dic: dict):
169
- if not dic:
170
- return None
171
-
172
- model = cls()
173
- for k, v in dic.items():
174
- if hasattr(model, k):
175
- setattr(model, k, v)
176
-
177
- model.channels = [Channel.from_dic(channel) for channel in dic.get("channels", [])]
178
- model.subscribers = dic.get('approximate_member_count')
179
- return model
180
-
181
- def to_dic(self):
182
- result = copy.deepcopy(self.__dict__)
183
- result['channels'] = [Channel.to_dic(channel) for channel in self.channels]
184
- return result
185
-
186
-
187
- class Channel:
188
- pass
189
-
190
- def __init__(self):
191
- self.id = None
192
- self.name = None
193
- self.type = None
194
- self.is_member = True
195
- self.active = True
196
- self.subscribers = 0
197
-
198
- @classmethod
199
- def from_dic(cls, dic: dict):
200
- if not dic:
201
- return None
202
-
203
- model = cls()
204
- for k, v in dic.items():
205
- if hasattr(model, k):
206
- setattr(model, k, v)
207
-
208
- return model
209
-
210
- def to_dic(self):
211
- result = copy.deepcopy(self.__dict__)
212
- return result
213
-
214
-
215
- class MessageModel:
216
- pass
217
-
218
- def __init__(self):
219
- self.message_id = None
220
- self.channel_id = None
221
- self.channel_name = None
222
- self.message = None
223
- self.name = None
224
- self.sender_id = None
225
- self.source: Source | None = None
226
- self.companies: List[str] = list()
227
- self.technologies: List[str] = list()
228
- self.locations: List[str] = list()
229
- self.configs: List[BaseConfig] = list()
230
- self.attachments: List[dict] = []
231
- self.timestamp = None
232
- self.tags: List[str] = []
233
-
234
- @classmethod
235
- def from_dic(cls, dic: dict):
236
- if not dic:
237
- return None
238
- if isinstance(dic.get('attachments'), str):
239
- dic['attachments'] = json.loads(dic['attachments'])
240
-
241
- model: MessageModel = cls()
242
- for k, v in dic.items():
243
- setattr(model, k, v)
244
-
245
- model.source = Source.from_dic(dic.get("source"))
246
- model.configs = [BaseConfig.from_dic(doc) for doc in dic.get("configs", [])]
247
- return model
248
-
249
- def to_dic(self):
250
- result = copy.deepcopy(self.__dict__)
251
-
252
- if result.get('source'):
253
- result['source'] = Source.to_dic(result.get('source'))
254
- if result.get('configs'):
255
- result['configs'] = [BaseConfig.to_dic(config) for config in result.get('configs')]
256
- return result
257
-
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.minute: int | None = None
266
- self.last_notification: datetime | None = None
267
- self.need_to_notify: bool = False
268
- self.attributes: list[str] = []
269
-
270
- @property
271
- def need_to_notify_now(self) -> bool:
272
- if not self.enabled or not self.need_to_notify:
273
- return False
274
-
275
- now = datetime.now(UTC)
276
- current_week_day = datetime.isoweekday(now)
277
- if self.last_notification:
278
- self.last_notification = self.last_notification.replace(tzinfo=UTC)
279
-
280
- if (self.type == NotificationType.UNREAD_FOR_FEW_MINUTES
281
- and self.last_notification and (now.minute - self.minute <= self.last_notification.minute)):
282
- return False
283
-
284
- if self.type == NotificationType.ONCE_A_WEEK and current_week_day != self.day:
285
- return False
286
-
287
- if ((self.type == NotificationType.ONCE_A_DAY or self.type == NotificationType.ONCE_A_WEEK)
288
- and now.hour != self.hour and now.minute < self.minute):
289
- return False
290
-
291
- if ((self.type == NotificationType.ONCE_A_DAY or self.type == NotificationType.ONCE_A_WEEK)
292
- and self.last_notification and self.last_notification > now - timedelta(hours=1)):
293
- return False
294
-
295
- return True
296
-
297
- @staticmethod
298
- def need_to_notify_week_before(date: datetime) -> bool:
299
- return datetime.now(UTC) < (date + timedelta(7))
300
-
301
-
302
- class NotificationSettings(DictionaryModel):
303
- def __init__(self):
304
- self.incoming_messages: Notification | None = None
305
- self.inbox: Notification | None = None
306
- self.source_deactivation: Notification | None = None
307
- self.billing: Notification | None = None
308
- self.bulk_replies: Notification | None = None
309
- self.bulk_reactions: Notification | None = None
310
- self.follow_ups: Notification | None = None
311
-
312
- @classmethod
313
- def from_dic(cls, dic: dict):
314
- if not dic:
315
- return None
316
-
317
- model: NotificationSettings = cls()
318
- model.incoming_messages = Notification.from_dic(dic.get('incoming_messages'))
319
- model.inbox = Notification.from_dic(dic.get('inbox'))
320
- model.source_deactivation = Notification.from_dic(dic.get('source_deactivation'))
321
- model.billing = Notification.from_dic(dic.get('billing'))
322
- model.bulk_replies = Notification.from_dic(dic.get('bulk_replies'))
323
- model.bulk_reactions = Notification.from_dic(dic.get('bulk_reactions'))
324
- model.follow_ups = Notification.from_dic(dic.get('follow_ups'))
325
- return model
326
-
327
- def to_dic(self):
328
- result = copy.deepcopy(self.__dict__)
329
-
330
- if result.get('incoming_messages'):
331
- result['incoming_messages'] = Notification.to_dic(result.get('incoming_messages'))
332
- if result.get('inbox'):
333
- result['inbox'] = Notification.to_dic(result.get('inbox'))
334
- if result.get('source_deactivation'):
335
- result['source_deactivation'] = Notification.to_dic(result.get('source_deactivation'))
336
- if result.get('billing'):
337
- result['billing'] = Notification.to_dic(result.get('billing'))
338
- if result.get('bulk_replies'):
339
- result['bulk_replies'] = Notification.to_dic(result.get('bulk_replies'))
340
- if result.get('bulk_reactions'):
341
- result['bulk_reactions'] = Notification.to_dic(result.get('bulk_reactions'))
342
- if result.get('follow_ups'):
343
- result['follow_ups'] = Notification.to_dic(result.get('follow_ups'))
344
-
345
- return result
346
-
347
-
348
- class GeneralSettings(DictionaryModel):
349
- def __init__(self):
350
- self.theme: str | None = None
351
- self.ask_pipeline_and_status: bool = False
352
- self.ask_follow_up: bool = False
353
- self.dashboard_is_starting_page: bool = False
354
-
355
-
356
- class UserModel(BaseModel):
357
- def __init__(self):
358
- super().__init__()
359
- self.email: Optional[str] = None
360
- self.password: Optional[str] = None
361
- self.roles: List[str] = []
362
- self.user_name: str = ''
363
- self.company: str = ''
364
- self.company_size: Optional[int] = None
365
- self.company_industries: Optional[List[str]] = None
366
- self.company_technologies: Optional[List[str]] = None
367
- self.company_locations: Optional[List[str]] = None
368
- self.company_web_site: str = ''
369
- self.company_description: str = ''
370
- self.position: str = ''
371
- self.leads_limit: Optional[int] = None
372
- self.leads_proceeded: Optional[int] = None
373
- self.leads_filtered: Optional[int] = None
374
- self.leads_limit_updated_at: Optional[int] = None
375
- self.paid_lead_price: int = 1
376
- self.state: int = 0
377
- self.credits_exceeded_at: Optional[datetime] = None
378
- self.unanswered_leads_period = None
379
- self.inactive = None
380
- self.slack_users: List[SlackUser] = []
381
- self.discord_users: List[DiscordUser] = []
382
- self.verified: bool = False
383
- self.subscription_id: ObjectId | None = None
384
- self.subscription_expired_at: datetime | None = None
385
- self.balance: str | None = None
386
- self.subscription_name: str | None = None
387
- self.subscription_expiration_notified = False
388
- self.subscription_expiration_warning_notified = False
389
- self.notification_settings: NotificationSettings | None = None
390
- self.general_settings: GeneralSettings | None = None
391
-
392
- @classmethod
393
- def from_dic(cls, dic: dict):
394
- if not dic:
395
- return None
396
-
397
- model: UserModel = cls()
398
- for k, v in dic.items():
399
- setattr(model, k, v)
400
-
401
- if '_id' in dic:
402
- setattr(model, 'id', dic['_id'])
403
-
404
- model.slack_profile = Profile.from_dic(dic.get('slack_profile'))
405
- model.slack_users = [SlackUser.from_dic(user) for user in dic.get('slack_users', [])]
406
- model.discord_users = [DiscordUser.from_dic(user) for user in dic.get('discord_users', [])]
407
- model.notification_settings = NotificationSettings.from_dic(dic.get('notification_settings'))
408
- model.general_settings = GeneralSettings.from_dic(dic.get('general_settings'))
409
- return model
410
-
411
- def to_dic(self):
412
- result = copy.deepcopy(self.__dict__)
413
-
414
- if result.get('slack_profile'):
415
- result['slack_profile'] = result.get('slack_profile').__dict__
416
- if result.get('notification_settings'):
417
- result['notification_settings'] = NotificationSettings.to_dic(result.get('notification_settings'))
418
- if result.get('general_settings'):
419
- result['general_settings'] = GeneralSettings.to_dic(result.get('general_settings'))
420
-
421
- return result
422
-
423
- @property
424
- def is_admin(self):
425
- return UserRole.ADMIN in self.roles
426
-
427
- @property
428
- def subscription_expired(self):
429
- return self.subscription_expired_at.replace(tzinfo=UTC) < datetime.now(UTC)
430
-
431
- def get_slack_user(self, slack_email: str) -> SlackUser:
432
- return next(filter(lambda x: slack_email == x.email, self.slack_users), None)
433
-
434
- def get_discord_user(self, login: str) -> DiscordUser:
435
- return next(filter(lambda x: login == x.login, self.discord_users), None)
436
-
437
-
438
- class DiscordUser(DictionaryModel):
439
- pass
440
-
441
- def __init__(self):
442
- super().__init__()
443
- self.created_at = datetime.now(UTC)
444
- self.login = ''
445
- self.captcha_key = ''
446
- self.status = None
447
- self.workspaces: List[UserWorkspace] = []
448
- self.deleted = False
449
- self.profile: Profile | None = None
450
-
451
- def to_dic(self):
452
- result = copy.deepcopy(self.__dict__)
453
-
454
- if result.get('workspaces'):
455
- result['workspaces'] = [ws.__dict__ for ws in result.get('workspaces')]
456
-
457
- if result.get('profile'):
458
- result['profile'] = result.get('profile').__dict__
459
-
460
- return result
461
-
462
- @classmethod
463
- def from_dic(cls, dic: dict):
464
- if not dic:
465
- return None
466
-
467
- model = cls()
468
- for k, v in dic.items():
469
- setattr(model, k, v)
470
-
471
- model.workspaces = [UserWorkspace.from_dic(ws) for ws in dic.get('workspaces', [])]
472
- model.profile = Profile.from_dic(dic.get('profile'))
473
- return model
474
-
475
-
476
- class SlackUser:
477
- pass
478
-
479
- def __init__(self):
480
- self.created_at = datetime.now(UTC)
481
- self.cookies = {}
482
- self.email = ''
483
- self.status = None
484
- self.workspaces: List[UserWorkspace] = []
485
- self.deleted = False
486
- self.profile: Profile | None = None
487
-
488
- def to_dic(self):
489
- result = copy.deepcopy(self.__dict__)
490
-
491
- if result.get('workspaces', None):
492
- result['workspaces'] = [ws.__dict__ for ws in result.get('workspaces')]
493
-
494
- if result.get('profile'):
495
- result['profile'] = result.get('profile').__dict__
496
-
497
- return result
498
-
499
- @classmethod
500
- def from_dic(cls, dic: dict):
501
- if not dic:
502
- return None
503
-
504
- model = cls()
505
- for k, v in dic.items():
506
- setattr(model, k, v)
507
-
508
- model.workspaces = [UserWorkspace.from_dic(ws) for ws in dic.get('workspaces', [])]
509
- model.profile = Profile.from_dic(dic.get('profile'))
510
- return model
511
-
512
-
513
- class UserWorkspace:
514
- pass
515
-
516
- def __init__(self):
517
- super().__init__()
518
- self.id = ''
519
- self.name = ''
520
- self.url = ''
521
- self.domain = ''
522
- self.active_users = ''
523
- self.profile_photos = []
524
- self.associated_user = ''
525
- self.magic_login_url = ''
526
- self.magic_login_code = ''
527
- self.user_email = ''
528
- self.user_type = ''
529
- self.variant = ''
530
- self.token = ''
531
- self.icon = ''
532
- self.two_factor_required = False
533
-
534
- @classmethod
535
- def from_dic(cls, dic: dict):
536
- if not dic:
537
- return None
538
-
539
- model: UserWorkspace = cls()
540
- for k, v in dic.items():
541
- setattr(model, k, v)
542
-
543
- model.icon = dic.get('icon_88', "")
544
- return model
545
-
546
-
547
- class UserResetPasswordModel(BaseModel):
548
- pass
549
-
550
- def __init__(self):
551
- super().__init__()
552
- self.email = None
553
-
554
-
555
- class LeadModel(BaseModel):
556
- def __init__(self):
557
- super().__init__()
558
- self.status = ''
559
- self.notes = ''
560
- self.archived = False
561
- self.message: Optional[MessageModel] = None
562
- self.hidden = False
563
- self.followup_date = None
564
- self.score = 0
565
- self.board_id = None
566
- self.linkedin_urls = []
567
- self.likes = 0
568
- self.reactions = 0
569
- self.replies = []
570
- self.last_action_at: Optional[datetime] = None
571
- self.slack_channel = None
572
-
573
- @classmethod
574
- def from_dic(cls, dic: dict):
575
- if not dic:
576
- return None
577
-
578
- model: LeadModel = cls()
579
- for k, v in dic.items():
580
- setattr(model, k, v)
581
-
582
- model.message = MessageModel.from_dic(dic['message'])
583
- if not model.last_action_at:
584
- model.last_action_at = model.created_at
585
-
586
- return model
587
-
588
- def to_dic(self):
589
- result = copy.deepcopy(self.__dict__)
590
- result["message"] = self.message.to_dic()
591
- result['archived'] = self.archived
592
- return result
593
-
594
-
595
- class ExtendedLeadModel(LeadModel):
596
- def __init__(self):
597
- super().__init__()
598
- self.previous_publications = []
599
- self.last_conversation: List[ChatMessage] = []
600
- self.contact: SlackMemberInformation | None = None
601
- self.deleted = False
602
- self.user_lead: UserLeadModel | None = None
603
- self.dedicated: bool = False
604
- self.bots: List[BotInfo] = []
605
- self.user_contact: UserContact | None = None
606
- self.paid: bool = False
607
- self.hidden_by_user: bool = False
608
-
609
- @classmethod
610
- def from_dic(cls, dic: dict):
611
- if not dic:
612
- return None
613
-
614
- result: ExtendedLeadModel | None = LeadModel.from_dic(dic)
615
- if not result:
616
- return None
617
-
618
- result.contact = SlackMemberInformation.from_dic(dic.get('contact'))
619
- result.user_contact = UserContact.from_dic(dic.get('user_contact'))
620
- result.previous_publications = [LeadModel.from_dic(lead) for lead in dic.get('previous_publications', [])]
621
- result.user_lead = UserLeadModel.from_dic(dic.get('user_lead'))
622
- result.last_conversation = [ChatMessage.from_dic(message) for message in dic.get('last_conversation', [])]
623
- result.bots = [BotInfo.from_dic(bot) for bot in dic.get('bots', [])]
624
- return result
625
-
626
- def to_csv(self, board_name: str) -> List[str]:
627
- return [self.message.source, self.contact.real_name, self.contact.title, self.contact.email,
628
- self.notes, board_name, self.status,
629
- self.followup_date.strftime("%d.%m.%Y %H:%M") if self.followup_date else "",
630
- self.message.message.replace('\n', ' ').strip()]
631
-
632
-
633
- class BotInfo:
634
- def __init__(self):
635
- self.id = None
636
- self.invalid_creds: bool | None = False
637
- self.source = None
638
- self.banned: bool | None = False
639
- self.user_name: str = ''
640
- self.associated_user: str | None = ''
641
- self.deleted: bool = False
642
- self.two_factor_required = False
643
-
644
- @classmethod
645
- def from_dic(cls, dic: dict):
646
- if not dic:
647
- return None
648
-
649
- model = BotInfo()
650
- for k, v in dic.items():
651
- if hasattr(model, k):
652
- setattr(model, k, v)
653
-
654
- if '_id' in dic:
655
- setattr(model, 'id', dic['_id'])
656
-
657
- return model
658
-
659
- def to_dic(self):
660
- result = copy.deepcopy(self.__dict__)
661
- return result
662
-
663
-
664
- class ChatMessage:
665
- bot_id: ObjectId
666
- user_id: ObjectId
667
- sender_id: str
668
- text: str
669
- user: str
670
- id: str
671
- viewed: bool
672
- files: list
673
- attachments: list[dict] | None
674
- created_at: datetime | None
675
- source_id: str | None
676
-
677
- class SlackFileModel:
678
- def __init__(self):
679
- self.id = None
680
- self.name = None
681
- self.title = None
682
- self.filetype = None
683
- self.size = 0
684
- self.mimetype = None
685
- self.download_url = None
686
-
687
- def to_dic(self):
688
- result = copy.deepcopy(self.__dict__)
689
- return result
690
-
691
- def __init__(self):
692
- self.viewed = False
693
- self.text: str = ''
694
- self.created_at: datetime
695
- self.user = ''
696
- self.id = ''
697
- self.files = []
698
- self.attachments = []
699
-
700
- def to_dic(self):
701
- result = copy.deepcopy(self.__dict__)
702
- result['files'] = [x.to_dic() for x in result.get('files', [])]
703
- return result
704
-
705
- @classmethod
706
- def from_dic(cls, dic: dict):
707
- if not dic:
708
- return None
709
- model = cls()
710
- for k, v in dic.items():
711
- setattr(model, k, v)
712
- return model
713
-
714
- @classmethod
715
- def from_slack_response(cls, bot: DedicatedBotModel, message_data: dict, sender_id: str):
716
- model = cls()
717
- model.sender_id = sender_id
718
- model.bot_id = bot.id
719
- model.text = message_data.get('text', '')
720
- model.user = message_data.get('user', '')
721
- model.id = message_data.get('ts', '')
722
- model.attachments = message_data.get('attachments', [])
723
- model.files = []
724
- model.user_id = bot.user_id
725
- model.source_id = bot.source.source_id
726
- if 'files' in message_data:
727
- for file in message_data.get('files'):
728
- if file.get('mode') != "tombstone" and file.get('url_private_download'):
729
- leadguru_file = LeadGuruFile()
730
- leadguru_file.id = file['id']
731
- leadguru_file.content_type = file['mimetype']
732
- leadguru_file.file_name = file['name']
733
- leadguru_file.blob_path = f'slack_files/{bot.user_name}/slack_files/{file["id"]}'
734
- model.files.append(leadguru_file)
735
-
736
- js_ticks = int(model.id.split('.')[0] + model.id.split('.')[1][3:])
737
- model.created_at = datetime.fromtimestamp(js_ticks / 1000.0)
738
- return model
739
-
740
-
741
- class ScheduledChatMessage(ChatMessage):
742
- post_at: Optional[datetime]
743
- jib: Optional[str]
744
-
745
- def __init__(self):
746
- super(ScheduledChatMessage, self).__init__()
747
-
748
- self.post_at = None
749
- self.jib = None
750
-
751
-
752
- class UserLeadModel(LeadModel):
753
- pass
754
-
755
- def __init__(self):
756
- super().__init__()
757
- self.order: int = 0
758
- self.followup_date = None
759
- self.user_id = None
760
- self.chat_viewed_at = None
761
- self.board_id = None
762
-
763
- @classmethod
764
- def from_dic(cls, dic: dict):
765
- if not dic:
766
- return None
767
-
768
- result: UserLeadModel | None = super().from_dic(dic)
769
- if not result:
770
- return None
771
-
772
- result.chat_viewed_at = dic.get('chat_viewed_at')
773
- return result
774
-
775
-
776
- class ExtendedUserLeadModel(UserLeadModel):
777
- pass
778
-
779
- def __init__(self):
780
- super().__init__()
781
- self.contact: SlackMemberInformation | None = None
782
- self.previous_publications = []
783
- self.bots: List[BotInfo] = []
784
-
785
- @classmethod
786
- def from_dic(cls, dic: dict):
787
- if not dic:
788
- return None
789
-
790
- result: ExtendedUserLeadModel | None = super().from_dic(dic)
791
- if not result:
792
- return None
793
-
794
- result.contact = SlackMemberInformation.from_dic(dic.get('contact'))
795
- result.previous_publications = [LeadModel.from_dic(lead) for lead in dic.get('previous_publications', [])]
796
- return result
797
-
798
- def to_dic(self):
799
- result = super().to_dic()
800
- result["contact"] = self.contact.to_dic()
801
- return result
802
-
803
- def to_csv(self, board_name: str) -> List[str]:
804
- return [self.message.source, self.contact.real_name, self.contact.title, self.contact.email,
805
- self.notes, board_name, self.status,
806
- self.followup_date.strftime("%d.%m.%Y %H:%M") if self.followup_date else "",
807
- self.message.message.replace('\n', ' ').strip()]
808
-
809
-
810
- class BoardModel(BaseModel):
811
- pass
812
-
813
- def __init__(self):
814
- super().__init__()
815
- self.name = None
816
- self.user_id = None
817
- self.statuses: List[BoardedStatus] = []
818
- self.is_primary = None
819
- self.default = False
820
-
821
- @classmethod
822
- def from_dic(cls, dic: dict):
823
- if not dic:
824
- return None
825
-
826
- model = BoardModel()
827
- for k, v in dic.items():
828
- setattr(model, k, v)
829
-
830
- model.id = dic.get('_id')
831
- model.statuses = [BoardedStatus.from_dic(status) for status in dic.get('statuses', [])]
832
- return model
833
-
834
- def to_dic(self):
835
- result = copy.deepcopy(self.__dict__)
836
- result["statuses"] = [BoardedStatus.to_dic(status) for status in self.statuses]
837
-
838
- for status in result['statuses']:
839
- status['board_id'] = result['id']
840
-
841
- return result
842
-
843
-
844
- class BoardedStatus:
845
- pass
846
-
847
- def __init__(self):
848
- self.id = None
849
- self.name = None
850
- self.order = 0
851
- self.is_primary = False
852
- self.default = False
853
- self.user_leads = 0
854
- self.collapsed = False
855
-
856
- def to_dic(self):
857
- self.id = self.name
858
- return copy.deepcopy(self.__dict__)
859
-
860
- @classmethod
861
- def from_dic(cls, dic: dict):
862
- if not dic:
863
- return None
864
- model = cls()
865
- for k, v in dic.items():
866
- setattr(model, k, v)
867
- return model
868
-
869
-
870
- class Profile:
871
- pass
872
-
873
- def __init__(self):
874
- self.title = ''
875
- self.phone = ''
876
- self.skype = ''
877
- self.display_name = ''
878
- self.real_name = ''
879
- self.email = ''
880
- self.photo_url = ''
881
- self.main = False
882
-
883
- def to_dic(self):
884
- return copy.deepcopy(self.__dict__)
885
-
886
- @classmethod
887
- def from_dic(cls, dic: dict):
888
- if not dic:
889
- return None
890
- model = cls()
891
- for k, v in dic.items():
892
- setattr(model, k, v)
893
- return model
894
-
895
-
896
- class SlackMemberInformation(BaseModel, Profile):
897
- workspace: str
898
- sender_id: str
899
- images: dict
900
- full_text: str
901
- deleted: bool = False
902
- is_bot: bool = False
903
- is_app_user: bool = False
904
- is_admin: bool = False
905
- is_owner: bool = False
906
- is_email_confirmed: bool = False
907
- online: Optional[str] = None
908
- online_updated_at: datetime = None
909
- timezone: SlackTimeZone = None
910
- source: Source = None
911
-
912
- @classmethod
913
- def from_dic(cls, dic: dict):
914
- model: SlackMemberInformation = cls()
915
- if not dic:
916
- return None
917
-
918
- for k, v in dic.items():
919
- setattr(model, k, v)
920
-
921
- model.online = dic.get('online', '') == "active"
922
- model: SlackMemberInformation | None = super().from_dic(dic)
923
- model.source = Source.from_dic(dic.get('source'))
924
- return model
925
-
926
- def to_dic(self):
927
- result = copy.deepcopy(self.__dict__)
928
- if result.get('source'):
929
- result['source'] = Source.to_dic(result.get('source'))
930
- return result
931
-
932
- @staticmethod
933
- def from_slack_response(slack_profile: dict, source: Source = None):
934
- member_info: SlackMemberInformation = SlackMemberInformation()
935
- member_info.source = source
936
- member_info.sender_id = slack_profile.get("id")
937
- member_info.display_name = slack_profile["profile"].get("display_name")
938
- member_info.real_name = slack_profile["profile"].get("real_name")
939
- member_info.title = slack_profile["profile"].get("title")
940
- member_info.phone = slack_profile["profile"].get("phone")
941
- member_info.skype = slack_profile["profile"].get("skype")
942
- member_info.email = slack_profile["profile"].get("email")
943
- member_info.images = {
944
- 'image_24': slack_profile.get("profile", {}).get("image_24",
945
- 'https://a.slack-edge.com/80588/img/slackbot_24.png'),
946
- 'image_32': slack_profile.get("profile", {}).get("image_32",
947
- 'https://a.slack-edge.com/80588/img/slackbot_32.png'),
948
- 'image_48': slack_profile.get("profile", {}).get("image_48",
949
- 'https://a.slack-edge.com/80588/img/slackbot_48.png'),
950
- 'image_72': slack_profile.get("profile", {}).get("image_72",
951
- 'https://a.slack-edge.com/80588/img/slackbot_72.png'),
952
- 'image_192': slack_profile.get("profile", {}).get("image_192",
953
- 'https://a.slack-edge.com/80588/img/slackbot_192.png'),
954
- 'image_512': slack_profile.get("profile", {}).get("image_512",
955
- 'https://a.slack-edge.com/80588/img/slackbot_512.png'),
956
-
957
- }
958
- member_info.timezone = {"tz": slack_profile.get("tz"), "tz_label": slack_profile.get("tz_label"),
959
- "tz_offset": slack_profile.get("tz_offset")}
960
- return member_info
961
-
962
-
963
- class UserContact(SlackMemberInformation):
964
- chat_id: str
965
- user_id: ObjectId
966
- source_id: str
967
- scheduled_messages: List[ScheduledChatMessage] = []
968
- last_message_at: Optional[datetime]
969
-
970
- @classmethod
971
- def from_dic(cls, dic: dict):
972
- if not dic:
973
- return None
974
- model: UserContact | None = super().from_dic(dic)
975
- model.chat_history = [ChatMessage.from_dic(message) for message in dic.get('chat_history', [])]
976
- model.scheduled_messages = [ScheduledChatMessage.from_dic(item) for item in dic.get("scheduled_messages", [])]
977
- return model
978
-
979
-
980
- class SlackTimeZone:
981
- tz: Optional[str]
982
- tz_label: Optional[str]
983
- tz_offset: Optional[int]
984
-
985
-
986
- class ExtendedSlackMemberInformation(SlackMemberInformation):
987
- previous_publications = []
988
- name: str = None
989
- bots: List[BotInfo] = []
990
-
991
- @classmethod
992
- def from_dic(cls, dic: dict):
993
- model: ExtendedSlackMemberInformation | None = super().from_dic(dic)
994
- if not model:
995
- return None
996
-
997
- model.previous_publications = [LeadModel.from_dic(lead) for lead in dic.get('previous_publications', [])]
998
- model.bots = [BotInfo.from_dic(bot) for bot in dic.get('bots', [])]
999
- return model
1000
-
1001
- @staticmethod
1002
- def to_lead(contact: ExtendedSlackMemberInformation) -> ExtendedUserLeadModel:
1003
- lead = ExtendedUserLeadModel()
1004
- lead.id = str(contact.id)
1005
- lead.created_at = contact.created_at
1006
- lead.notes = ""
1007
- lead.slack_channel = None
1008
- lead.hidden = True
1009
- lead.replies = []
1010
- lead.reactions = 0
1011
- lead.last_action_at = datetime.now(UTC)
1012
- lead.created_at = datetime.now(UTC)
1013
- if not hasattr(contact, "real_name"):
1014
- contact.real_name = contact.name
1015
- if not hasattr(contact, "display_name"):
1016
- contact.display_name = contact.name
1017
-
1018
- lead.linkedin_urls = [
1019
- f"https://www.linkedin.com/search/results/all/?keywords={contact.real_name}&origin=GLOBAL_SEARCH_HEADER&sid=u%40F"]
1020
- lead.message = MessageModel()
1021
- lead.message.message = contact.real_name
1022
- if contact.title:
1023
- lead.message.message += contact.title
1024
- lead.message.message_id = str(contact.id)
1025
- lead.message.name = contact.source.source_name
1026
- lead.message.source = contact.source
1027
- lead.message.sender_id = contact.sender_id
1028
- lead.message.companies = []
1029
- lead.message.technologies = []
1030
- lead.message.locations = []
1031
- lead.message.chat_history = []
1032
- lead.chat_viewed_at = datetime.now(UTC)
1033
- lead.chat_history = []
1034
- lead.previous_publications = contact.previous_publications if hasattr(contact, "previous_publications") else []
1035
- lead.bots = contact.bots if hasattr(contact, "bots") else []
1036
- lead.contact = contact
1037
- return lead
1038
-
1039
-
1040
- class UserTemplateModel(BaseModel):
1041
- text: str
1042
- subject: Optional[str]
1043
- user_id: Optional[ObjectId]
1044
-
1045
-
1046
- class CloudFileModel(BaseModel):
1047
- blob_path: str
1048
- public_url: str
1049
- file_name: str
1050
-
1051
- def __init__(self, blob_path: str, public_url: str, file_name: str):
1052
- super().__init__()
1053
- if not self.id:
1054
- self.id = str(ObjectId())
1055
- self.blob_path = blob_path
1056
- self.public_url = public_url
1057
- self.file_name = file_name
1058
-
1059
-
1060
- class GroupedMessagesModel:
1061
- messages: List[ChatMessage] = []
1062
-
1063
- @classmethod
1064
- def from_dic(cls, dic: dict):
1065
- if not dic:
1066
- return None
1067
-
1068
- model = cls()
1069
- model.messages = [ChatMessage.from_dic(message) for message in dic.get('messages', [])]
1070
- return model
1071
-
1072
-
1073
- class UserVerificationModel(DictionaryModel):
1074
- pass
1075
-
1076
- def __init__(self):
1077
- super().__init__()
1078
- self.email = None
1079
- self.created_at = datetime.now(UTC)
1080
-
1081
-
1082
- class UsersPage:
1083
- users: List[UserModel]
1084
- count: int = 0
1085
-
1086
- def __init__(self, users: List[UserModel], count: int):
1087
- self.users = users
1088
- self.count = count
1089
-
1090
- @staticmethod
1091
- def from_dic(dic: dict):
1092
- users = [UserModel.from_dic(doc) for doc in dic.get('page', [])]
1093
- count = dic.get('count', 0)
1094
- return UsersPage(users=users, count=count)
1095
-
1096
-
1097
- class Feature(DictionaryModel):
1098
- display_name: str
1099
- name: FeaturesEnum
1100
- description: str | None = None
1101
- limit: int | None = None
1102
- options: FeatureOptions | None = None
1103
-
1104
-
1105
- class Subscription(BaseModel):
1106
- features: list[Feature]
1107
- duration_days: int
1108
- name: str
1109
- price: int
1110
- limits: int
1111
- trial: bool
1112
- updated_at: datetime = None
1113
-
1114
- @classmethod
1115
- def from_dic(cls, dic: dict):
1116
- if not dic:
1117
- return None
1118
- model: Subscription | None = super().from_dic(dic)
1119
- model.features = [Feature.from_dic(feature) for feature in dic.get('features', [])]
1120
- return model
1121
-
1122
-
1123
- class LeadGuruFile(DictionaryModel):
1124
- id: str = None
1125
- blob_path: str
1126
- content_type: str
1127
- file_name: str = None
1128
-
1129
-
1130
- class ScheduledMessage(BaseModel):
1131
- post_at: datetime
1132
- bot_id: ObjectId
1133
- user_id: ObjectId
1134
- sender_id: str
1135
- text: str | None
1136
- files: list