leadguru-jobs 0.403.0__py3-none-any.whl → 0.405.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.
Files changed (43) hide show
  1. {leadguru_jobs-0.403.0.dist-info → leadguru_jobs-0.405.0.dist-info}/METADATA +5 -10
  2. leadguru_jobs-0.405.0.dist-info/RECORD +26 -0
  3. lgt_jobs/__init__.py +4 -4
  4. lgt_jobs/jobs/analytics.py +1 -1
  5. lgt_jobs/jobs/archive_leads.py +2 -2
  6. lgt_jobs/jobs/bot_stats_update.py +11 -10
  7. lgt_jobs/jobs/chat_history.py +6 -6
  8. lgt_jobs/jobs/inbox_leads.py +6 -5
  9. lgt_jobs/jobs/mass_message.py +2 -2
  10. lgt_jobs/jobs/send_code.py +1 -1
  11. lgt_jobs/jobs/send_slack_message.py +5 -5
  12. lgt_jobs/jobs/update_slack_profile.py +14 -12
  13. lgt_jobs/jobs/user_balance_update.py +5 -5
  14. lgt_jobs/jobs/workspace_connect.py +5 -7
  15. lgt_jobs/main.py +11 -9
  16. lgt_jobs/runner.py +9 -6
  17. lgt_jobs/smtp.py +1 -1
  18. leadguru_jobs-0.403.0.dist-info/RECORD +0 -49
  19. lgt_jobs/lgt_common/__init__.py +0 -0
  20. lgt_jobs/lgt_common/discord_client/__init__.py +0 -0
  21. lgt_jobs/lgt_common/discord_client/discord_client.py +0 -58
  22. lgt_jobs/lgt_common/discord_client/methods.py +0 -15
  23. lgt_jobs/lgt_common/enums/__init__.py +0 -0
  24. lgt_jobs/lgt_common/enums/slack_errors.py +0 -6
  25. lgt_jobs/lgt_common/helpers.py +0 -18
  26. lgt_jobs/lgt_common/lgt_logging.py +0 -15
  27. lgt_jobs/lgt_common/pubsub/__init__.py +0 -0
  28. lgt_jobs/lgt_common/pubsub/command.py +0 -14
  29. lgt_jobs/lgt_common/pubsub/messages.py +0 -37
  30. lgt_jobs/lgt_common/pubsub/pubsubfactory.py +0 -51
  31. lgt_jobs/lgt_common/slack_client/__init__.py +0 -0
  32. lgt_jobs/lgt_common/slack_client/methods.py +0 -46
  33. lgt_jobs/lgt_common/slack_client/slack_client.py +0 -392
  34. lgt_jobs/lgt_common/slack_client/web_client.py +0 -164
  35. lgt_jobs/lgt_data/__init__.py +0 -0
  36. lgt_jobs/lgt_data/analytics.py +0 -723
  37. lgt_jobs/lgt_data/engine.py +0 -223
  38. lgt_jobs/lgt_data/enums.py +0 -68
  39. lgt_jobs/lgt_data/helpers.py +0 -2
  40. lgt_jobs/lgt_data/model.py +0 -960
  41. lgt_jobs/lgt_data/mongo_repository.py +0 -980
  42. {leadguru_jobs-0.403.0.dist-info → leadguru_jobs-0.405.0.dist-info}/WHEEL +0 -0
  43. {leadguru_jobs-0.403.0.dist-info → leadguru_jobs-0.405.0.dist-info}/top_level.txt +0 -0
@@ -1,960 +0,0 @@
1
- from __future__ import annotations
2
- import copy
3
- import json
4
- import re
5
- from abc import ABC
6
- from datetime import datetime, UTC
7
- from typing import Optional, List, Dict
8
- from .enums import UserRole, SourceType
9
- from .helpers import get_linkedin_search_contact
10
- from bson import ObjectId
11
-
12
-
13
- class BaseModel(ABC):
14
- def __init__(self):
15
- self.id = None
16
- self.created_at = datetime.now(UTC)
17
-
18
- @classmethod
19
- def from_dic(cls, dic: dict):
20
- if not dic:
21
- return None
22
-
23
- model = cls()
24
- for k, v in dic.items():
25
- setattr(model, k, v)
26
-
27
- if '_id' in dic:
28
- setattr(model, 'id', dic['_id'])
29
-
30
- return model
31
-
32
- def to_dic(self):
33
- result = copy.deepcopy(self.__dict__)
34
- return result
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.messages_received: int = 0
106
- self.messages_filtered: int = 0
107
- self.recent_messages: List[str] = []
108
- self.icon = None
109
- self.active_channels = {}
110
- self.paused_channels = []
111
- self.source: Source | None = None
112
- self.two_factor_required: bool = False
113
- self.banned: bool = False
114
- self.associated_user = None
115
- self.type: SourceType | None = None
116
-
117
-
118
- class DedicatedBotModel(BaseBotModel):
119
- def __init__(self):
120
- super().__init__()
121
- self.user_id: Optional[str] = None
122
- self.updated_at: Optional[datetime] = datetime.now(UTC)
123
- self.servers: List[Server] = []
124
- self.state = 0
125
- self.source: Source | None = None
126
-
127
- def to_dic(self):
128
- result = copy.deepcopy(self.__dict__)
129
-
130
- if result.get('source'):
131
- result['source'] = Source.to_dic(result.get('source'))
132
-
133
- result['servers'] = [Server.to_dic(server) for server in self.servers]
134
- return result
135
-
136
- @classmethod
137
- def from_dic(cls, dic: dict):
138
- if not dic:
139
- return None
140
-
141
- model: DedicatedBotModel = cls()
142
- for k, v in dic.items():
143
- setattr(model, k, v)
144
-
145
- if '_id' in dic:
146
- setattr(model, 'id', dic['_id'])
147
-
148
- model.source = Source.from_dic(dic.get("source"))
149
- model.servers = [Server.from_dic(server) for server in dic.get("servers", [])]
150
- return model
151
-
152
-
153
- class Server:
154
- pass
155
-
156
- def __init__(self):
157
- self.id = None
158
- self.name = None
159
- self.channels: List[Channel] = []
160
- self.icon = None
161
- self.active = False
162
- self.deleted = False
163
- self.subscribers = 0
164
-
165
- @classmethod
166
- def from_dic(cls, dic: dict):
167
- if not dic:
168
- return None
169
-
170
- model = cls()
171
- for k, v in dic.items():
172
- if hasattr(model, k):
173
- setattr(model, k, v)
174
-
175
- model.channels = [Channel.from_dic(channel) for channel in dic.get("channels", [])]
176
- return model
177
-
178
- def to_dic(self):
179
- result = copy.deepcopy(self.__dict__)
180
- result['channels'] = [Channel.to_dic(channel) for channel in self.channels]
181
- return result
182
-
183
-
184
- class Channel:
185
- pass
186
-
187
- def __init__(self):
188
- self.id = None
189
- self.name = None
190
- self.type = None
191
- self.is_member = True
192
- self.active = False
193
- self.subscribers = 0
194
-
195
- @classmethod
196
- def from_dic(cls, dic: dict):
197
- if not dic:
198
- return None
199
-
200
- model = cls()
201
- for k, v in dic.items():
202
- if hasattr(model, k):
203
- setattr(model, k, v)
204
-
205
- return model
206
-
207
- def to_dic(self):
208
- result = copy.deepcopy(self.__dict__)
209
- return result
210
-
211
-
212
- class MessageModel:
213
- pass
214
-
215
- def __init__(self):
216
- self.message_id = None
217
- self.channel_id = None
218
- self.channel_name = None
219
- self.message = None
220
- self.name = None
221
- self.sender_id = None
222
- self.source: Source | None = None
223
- self.companies: List[str] = list()
224
- self.technologies: List[str] = list()
225
- self.locations: List[str] = list()
226
- self.configs: List[BaseConfig] = list()
227
- self.attachments: List[dict] = []
228
- self.timestamp = None
229
- self.tags: List[str] = []
230
-
231
- @classmethod
232
- def from_dic(cls, dic: dict):
233
- if not dic:
234
- return None
235
- if isinstance(dic.get('attachments'), str):
236
- dic['attachments'] = json.loads(dic['attachments'])
237
-
238
- model: MessageModel = cls()
239
- for k, v in dic.items():
240
- setattr(model, k, v)
241
-
242
- model.source = Source.from_dic(dic.get("source"))
243
- model.configs = [BaseConfig.from_dic(doc) for doc in dic.get("configs", [])]
244
- return model
245
-
246
- def to_dic(self):
247
- result = copy.deepcopy(self.__dict__)
248
-
249
- if result.get('source'):
250
- result['source'] = Source.to_dic(result.get('source'))
251
- if result.get('configs'):
252
- result['configs'] = [BaseConfig.to_dic(config) for config in result.get('configs')]
253
- return result
254
-
255
- @property
256
- def urls_in_message(self) -> List[str]:
257
- url_pattern = r"(?i)\b((?:https?://|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|" \
258
- r"(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'\".,<>?«»“”‘’]))"
259
- return re.findall(url_pattern, self.message)
260
-
261
-
262
- class UserModel(BaseModel):
263
- def __init__(self):
264
- super().__init__()
265
- self.email: Optional[str] = None
266
- self.password: Optional[str] = None
267
- self.roles: List[str] = []
268
- self.user_name: str = ''
269
- self.company: str = ''
270
- self.company_size: Optional[int] = None
271
- self.company_industries: Optional[List[str]] = None
272
- self.company_technologies: Optional[List[str]] = None
273
- self.company_locations: Optional[List[str]] = None
274
- self.company_web_site: str = ''
275
- self.company_description: str = ''
276
- self.position: str = ''
277
- self.new_message_notified_at: Optional[datetime] = None
278
- self.photo_url: str = ''
279
- self.slack_profile = SlackProfile()
280
- self.leads_limit: Optional[int] = None
281
- self.leads_proceeded: Optional[int] = None
282
- self.leads_filtered: Optional[int] = None
283
- self.leads_limit_updated_at: Optional[int] = None
284
- self.excluded_channels: Optional[Dict[str, List[str]]] = None
285
- self.excluded_workspaces: Optional[List[str]] = []
286
- self.keywords: Optional[List[str]] = None
287
- self.block_words: Optional[List[str]] = None
288
- self.paid_lead_price: int = 1
289
- self.state: int = 0
290
- self.credits_exceeded_at: Optional[datetime] = None
291
- self.unanswered_leads_period = None
292
- self.inactive = None
293
- self.slack_users: List[SlackUser] = []
294
- self.discord_users: List[DiscordUser] = []
295
-
296
- @classmethod
297
- def from_dic(cls, dic: dict):
298
- if not dic:
299
- return None
300
-
301
- model: UserModel = cls()
302
- for k, v in dic.items():
303
- setattr(model, k, v)
304
-
305
- if '_id' in dic:
306
- setattr(model, 'id', dic['_id'])
307
-
308
- model.slack_profile = SlackProfile.from_dic(dic.get('slack_profile'))
309
- model.slack_users = [SlackUser.from_dic(user) for user in dic.get('slack_users', [])]
310
- model.discord_users = [DiscordUser.from_dic(user) for user in dic.get('discord_users', [])]
311
- return model
312
-
313
- def to_dic(self):
314
- result = copy.deepcopy(self.__dict__)
315
-
316
- if result.get('slack_profile', None):
317
- result['slack_profile'] = result.get('slack_profile').__dict__
318
-
319
- return result
320
-
321
- @property
322
- def is_admin(self):
323
- return UserRole.ADMIN in self.roles
324
-
325
- def get_slack_user(self, slack_email: str):
326
- return next(filter(lambda x: slack_email == x.email, self.slack_users), None)
327
-
328
- def get_discord_user(self, login: str):
329
- return next(filter(lambda x: login == x.login, self.discord_users), None)
330
-
331
-
332
- class DiscordUser(BaseModel):
333
- pass
334
-
335
- def __init__(self):
336
- super().__init__()
337
- self.created_at = datetime.now(UTC)
338
- self.login = ''
339
- self.captcha_key = ''
340
- self.status = None
341
- self.workspaces: List[UserWorkspace] = []
342
-
343
- def to_dic(self):
344
- result = copy.deepcopy(self.__dict__)
345
-
346
- if result.get('workspaces', None):
347
- result['workspaces'] = [ws.__dict__ for ws in result.get('workspaces')]
348
-
349
- return result
350
-
351
- @classmethod
352
- def from_dic(cls, dic: dict):
353
- if not dic:
354
- return None
355
-
356
- model = cls()
357
- for k, v in dic.items():
358
- setattr(model, k, v)
359
-
360
- model.workspaces = [UserWorkspace.from_dic(ws) for ws in dic.get('workspaces', [])]
361
- return model
362
-
363
-
364
- class SlackUser:
365
- pass
366
-
367
- def __init__(self):
368
- self.created_at = datetime.now(UTC)
369
- self.cookies = {}
370
- self.email = ''
371
- self.status = None
372
- self.workspaces: List[UserWorkspace] = []
373
-
374
- def to_dic(self):
375
- result = copy.deepcopy(self.__dict__)
376
-
377
- if result.get('workspaces', None):
378
- result['workspaces'] = [ws.__dict__ for ws in result.get('workspaces')]
379
-
380
- return result
381
-
382
- @classmethod
383
- def from_dic(cls, dic: dict):
384
- if not dic:
385
- return None
386
-
387
- model = cls()
388
- for k, v in dic.items():
389
- setattr(model, k, v)
390
-
391
- model.workspaces = [UserWorkspace.from_dic(ws) for ws in dic.get('workspaces', [])]
392
- return model
393
-
394
-
395
- class UserWorkspace:
396
- pass
397
-
398
- def __init__(self):
399
- super().__init__()
400
- self.id = ''
401
- self.name = ''
402
- self.url = ''
403
- self.domain = ''
404
- self.active_users = ''
405
- self.profile_photos = []
406
- self.associated_user = ''
407
- self.magic_login_url = ''
408
- self.magic_login_code = ''
409
- self.user_email = ''
410
- self.user_type = ''
411
- self.variant = ''
412
- self.token = ''
413
- self.icon = ''
414
- self.two_factor_required = False
415
-
416
- @classmethod
417
- def from_dic(cls, dic: dict):
418
- if not dic:
419
- return None
420
-
421
- model: UserWorkspace = cls()
422
- for k, v in dic.items():
423
- setattr(model, k, v)
424
-
425
- model.icon = dic.get('icon_88', "")
426
- return model
427
-
428
-
429
- class UserResetPasswordModel(BaseModel):
430
- pass
431
-
432
- def __init__(self):
433
- super().__init__()
434
- self.email = None
435
-
436
-
437
- class LeadModel(BaseModel):
438
- def __init__(self):
439
- super().__init__()
440
- self.status = ''
441
- self.notes = ''
442
- self.archived = False
443
- self.message: Optional[MessageModel] = None
444
- self.hidden = False
445
- self.followup_date = None
446
- self.score = 0
447
- self.board_id = None
448
- self.linkedin_urls = []
449
- self.likes = 0
450
- self.reactions = 0
451
- self.replies = []
452
- self.last_action_at: Optional[datetime] = None
453
- self.scheduled_messages: List[SlackScheduledMessageModel] = []
454
- self.slack_channel = None
455
-
456
- def is_dedicated_lead(self) -> bool:
457
- return self.message and \
458
- hasattr(self.message, "dedicated_slack_options") and \
459
- self.message.dedicated_slack_options
460
-
461
- @classmethod
462
- def from_dic(cls, dic: dict):
463
- if not dic:
464
- return None
465
-
466
- model: LeadModel = cls()
467
- for k, v in dic.items():
468
- setattr(model, k, v)
469
-
470
- model.message = MessageModel.from_dic(dic['message'])
471
- model.scheduled_messages = [SlackScheduledMessageModel.from_dic(item) for item in
472
- dic.get("scheduled_messages", [])]
473
-
474
- if not model.last_action_at:
475
- model.last_action_at = model.created_at
476
-
477
- return model
478
-
479
- def to_dic(self):
480
- result = copy.deepcopy(self.__dict__)
481
- result["message"] = self.message.to_dic()
482
- result['archived'] = self.archived
483
- return result
484
-
485
-
486
- class ExtendedLeadModel(LeadModel):
487
- def __init__(self):
488
- super().__init__()
489
- self.previous_publications = []
490
- self.last_conversation: List[SlackHistoryMessageModel] = []
491
- self.contact: SlackMemberInformation | None = None
492
- self.deleted = False
493
- self.user_lead: UserLeadModel | None = None
494
- self.dedicated: bool = False
495
- self.bots: List[BotInfo] = []
496
-
497
- @classmethod
498
- def from_dic(cls, dic: dict):
499
- if not dic:
500
- return None
501
-
502
- result: ExtendedLeadModel | None = LeadModel.from_dic(dic)
503
- if not result:
504
- return None
505
-
506
- result.contact = SlackMemberInformation.from_dic(dic.get('contact'))
507
- result.previous_publications = [LeadModel.from_dic(lead) for lead in dic.get('previous_publications', [])]
508
- result.user_lead = UserLeadModel.from_dic(dic.get('user_lead'))
509
- result.last_conversation = [SlackHistoryMessageModel.from_dic(message)
510
- for message in dic.get('last_conversation', [])]
511
- result.bots = [BotInfo.from_dic(bot) for bot in dic.get('bots', [])]
512
- return result
513
-
514
- def to_csv(self, board_name: str) -> List[str]:
515
- return [self.message.source, self.contact.real_name, self.contact.title, self.contact.email,
516
- self.notes, board_name, self.status,
517
- self.followup_date.strftime("%d.%m.%Y %H:%M") if self.followup_date else "",
518
- self.message.message.replace('\n', ' ').strip()]
519
-
520
-
521
- class BotInfo:
522
- def __init__(self):
523
- self.id = None
524
- self.invalid_creds: bool | None = False
525
- self.source = None
526
- self.banned: bool | None = False
527
- self.user_name: str = ''
528
- self.associated_user: str | None = ''
529
-
530
- @classmethod
531
- def from_dic(cls, dic: dict):
532
- if not dic:
533
- return None
534
-
535
- model = BotInfo()
536
- for k, v in dic.items():
537
- if hasattr(model, k):
538
- setattr(model, k, v)
539
-
540
- if '_id' in dic:
541
- setattr(model, 'id', dic['_id'])
542
-
543
- return model
544
-
545
- def to_dic(self):
546
- result = copy.deepcopy(self.__dict__)
547
- return result
548
-
549
-
550
- class SlackReplyModel(BaseModel):
551
- def __init__(self):
552
- super().__init__()
553
- self.type = None
554
- self.user = None
555
- self.username = None
556
- self.text = None
557
- self.thread_ts = None
558
- self.parent_user_id = None
559
- self.ts = None
560
- self.files = []
561
- self.attachments = []
562
-
563
- @classmethod
564
- def from_slack_response(cls, dic: dict):
565
- if not dic:
566
- return None
567
-
568
- model = cls()
569
- for k, v in dic.items():
570
- setattr(model, k, v)
571
-
572
- js_ticks = int(model.ts.split('.')[0] + model.ts.split('.')[1][3:])
573
- model.created_at = datetime.fromtimestamp(js_ticks / 1000.0)
574
-
575
- if model.files:
576
- model.files = [{"url_private_download": file.get("url_private_download")} for file in model.files]
577
-
578
- return model
579
-
580
-
581
- class SlackHistoryMessageModel:
582
- text: str
583
- created_at: datetime
584
- user: str
585
- type: str
586
- ts: str
587
- files: list
588
- attachments: list
589
-
590
- class SlackFileModel:
591
- def __init__(self):
592
- self.id = None
593
- self.name = None
594
- self.title = None
595
- self.filetype = None
596
- self.size = 0
597
- self.mimetype = None
598
- self.download_url = None
599
-
600
- def to_dic(self):
601
- result = copy.deepcopy(self.__dict__)
602
- return result
603
-
604
- def __init__(self):
605
- self.text: str = ''
606
- self.created_at: datetime
607
- self.user = ''
608
- self.type = ''
609
- self.ts = ''
610
- self.files = []
611
- self.attachments = []
612
-
613
- def to_dic(self):
614
- result = copy.deepcopy(self.__dict__)
615
- if self.files and 'files' in result:
616
- result['files'] = [x.to_dic() if isinstance(x, SlackHistoryMessageModel.SlackFileModel) else x
617
- for x in self.files]
618
-
619
- return result
620
-
621
- @classmethod
622
- def from_dic(cls, dic: dict):
623
- if not dic:
624
- return None
625
- model = cls()
626
- for k, v in dic.items():
627
- setattr(model, k, v)
628
- return model
629
-
630
-
631
- class SlackScheduledMessageModel(SlackHistoryMessageModel):
632
- post_at: Optional[datetime]
633
- jib: Optional[str]
634
-
635
- def __init__(self):
636
- super(SlackScheduledMessageModel, self).__init__()
637
-
638
- self.post_at = None
639
- self.jib = None
640
-
641
-
642
- class UserLeadModel(LeadModel):
643
- pass
644
-
645
- def __init__(self):
646
- super().__init__()
647
- self.order: int = 0
648
- self.followup_date = None
649
- self.user_id = None
650
- self.chat_viewed_at = None
651
- self.chat_history: List[SlackHistoryMessageModel] = []
652
- self.board_id = None
653
-
654
- @classmethod
655
- def from_dic(cls, dic: dict):
656
- if not dic:
657
- return None
658
-
659
- result: UserLeadModel | None = super().from_dic(dic)
660
- if not result:
661
- return None
662
-
663
- result.chat_history = [SlackHistoryMessageModel.from_dic(message) for message in dic.get('chat_history', [])]
664
- result.chat_viewed_at = dic.get('chat_viewed_at')
665
- result.chat_history = sorted(result.chat_history, key=lambda x: x.created_at)
666
- return result
667
-
668
- @staticmethod
669
- def from_route(lead: LeadModel):
670
- model_dict = lead.to_dic()
671
- result = UserLeadModel.from_dic(model_dict)
672
- result.order = 0
673
-
674
- result.message = MessageModel.from_dic(model_dict['message'])
675
- result.chat_history = []
676
- result.chat_viewed_at = None
677
- return result
678
-
679
-
680
- class ExtendedUserLeadModel(UserLeadModel):
681
- pass
682
-
683
- def __init__(self):
684
- super().__init__()
685
- self.contact: SlackMemberInformation | None = None
686
- self.previous_publications = []
687
-
688
- @classmethod
689
- def from_dic(cls, dic: dict):
690
- if not dic:
691
- return None
692
-
693
- result: ExtendedUserLeadModel | None = super().from_dic(dic)
694
- if not result:
695
- return None
696
-
697
- result.contact = SlackMemberInformation.from_dic(dic.get('contact'))
698
- result.previous_publications = [LeadModel.from_dic(lead) for lead in dic.get('previous_publications', [])]
699
- return result
700
-
701
- def to_dic(self):
702
- result = super().to_dic()
703
- result["contact"] = self.contact.to_dic()
704
- return result
705
-
706
- def to_csv(self, board_name: str) -> List[str]:
707
- return [self.message.source, self.contact.real_name, self.contact.title, self.contact.email,
708
- self.notes, board_name, self.status,
709
- self.followup_date.strftime("%d.%m.%Y %H:%M") if self.followup_date else "",
710
- self.message.message.replace('\n', ' ').strip()]
711
-
712
-
713
- class BoardModel(BaseModel):
714
- pass
715
-
716
- def __init__(self):
717
- super().__init__()
718
- self.name = None
719
- self.user_id = None
720
- self.statuses = list()
721
- self.is_primary = None
722
- self.default = False
723
-
724
- @classmethod
725
- def from_dic(cls, dic: dict):
726
- if not dic:
727
- return None
728
-
729
- model = BoardModel()
730
- for k, v in dic.items():
731
- setattr(model, k, v)
732
-
733
- model.id = dic.get('_id')
734
- model.statuses = [BoardedStatus.from_dic(status) for status in dic.get('statuses', [])]
735
- return model
736
-
737
- def to_dic(self):
738
- result = copy.deepcopy(self.__dict__)
739
- result["statuses"] = [BoardedStatus.to_dic(status) for status in self.statuses]
740
-
741
- for status in result['statuses']:
742
- status['board_id'] = result['id']
743
-
744
- return result
745
-
746
-
747
- class BoardedStatus:
748
- pass
749
-
750
- def __init__(self):
751
- self.id = None
752
- self.name = None
753
- self.order = 0
754
- self.is_primary = False
755
- self.default = False
756
-
757
- def to_dic(self):
758
- self.id = self.name
759
- return copy.deepcopy(self.__dict__)
760
-
761
- @classmethod
762
- def from_dic(cls, dic: dict):
763
- if not dic:
764
- return None
765
- model = cls()
766
- for k, v in dic.items():
767
- setattr(model, k, v)
768
- return model
769
-
770
-
771
- class SlackProfile:
772
- pass
773
-
774
- def __init__(self):
775
- self.title = ''
776
- self.phone = ''
777
- self.skype = ''
778
- self.display_name = ''
779
- self.real_name = ''
780
- self.email = ''
781
-
782
- def to_dic(self):
783
- return copy.deepcopy(self.__dict__)
784
-
785
- @classmethod
786
- def from_dic(cls, dic: dict):
787
- if not dic:
788
- return None
789
- model = cls()
790
- for k, v in dic.items():
791
- setattr(model, k, v)
792
- return model
793
-
794
-
795
- class SlackMemberInformation(BaseModel, SlackProfile):
796
- workspace: str
797
- sender_id: str
798
- images: dict
799
- full_text: str
800
- deleted: bool = False
801
- is_bot: bool = False
802
- is_app_user: bool = False
803
- is_admin: bool = False
804
- is_owner: bool = False
805
- is_email_confirmed: bool = False
806
- online: Optional[str] = None
807
- online_updated_at: datetime = None
808
- timezone: SlackTimeZone = None
809
- source: Source = None
810
-
811
- @classmethod
812
- def from_dic(cls, dic: dict):
813
- model: SlackMemberInformation = cls()
814
- if not dic:
815
- return None
816
-
817
- for k, v in dic.items():
818
- setattr(model, k, v)
819
-
820
- model.online = dic.get('online', '') == "active"
821
- model: SlackMemberInformation | None = super().from_dic(dic)
822
- model.source = Source.from_dic(dic.get('source'))
823
- return model
824
-
825
- def to_dic(self):
826
- result = copy.deepcopy(self.__dict__)
827
- if result.get('source'):
828
- result['source'] = Source.to_dic(result.get('source'))
829
- return result
830
-
831
- @staticmethod
832
- def from_slack_response(slack_profile: dict, source: Source = None):
833
- member_info: SlackMemberInformation = SlackMemberInformation()
834
- member_info.source = source
835
- member_info.sender_id = slack_profile.get("id")
836
- member_info.display_name = slack_profile["profile"].get("display_name")
837
- member_info.real_name = slack_profile["profile"].get("real_name")
838
- member_info.title = slack_profile["profile"].get("title")
839
- member_info.phone = slack_profile["profile"].get("phone")
840
- member_info.skype = slack_profile["profile"].get("skype")
841
- member_info.email = slack_profile["profile"].get("email")
842
- member_info.images = {
843
- 'image_24': slack_profile.get("profile", {}).get("image_24",
844
- 'https://a.slack-edge.com/80588/img/slackbot_24.png'),
845
- 'image_32': slack_profile.get("profile", {}).get("image_32",
846
- 'https://a.slack-edge.com/80588/img/slackbot_32.png'),
847
- 'image_48': slack_profile.get("profile", {}).get("image_48",
848
- 'https://a.slack-edge.com/80588/img/slackbot_48.png'),
849
- 'image_72': slack_profile.get("profile", {}).get("image_72",
850
- 'https://a.slack-edge.com/80588/img/slackbot_72.png'),
851
- 'image_192': slack_profile.get("profile", {}).get("image_192",
852
- 'https://a.slack-edge.com/80588/img/slackbot_192.png'),
853
- 'image_512': slack_profile.get("profile", {}).get("image_512",
854
- 'https://a.slack-edge.com/80588/img/slackbot_512.png'),
855
-
856
- }
857
- member_info.timezone = {"tz": slack_profile.get("tz"), "tz_label": slack_profile.get("tz_label"),
858
- "tz_offset": slack_profile.get("tz_offset")}
859
- return member_info
860
-
861
-
862
- class UserContact(SlackMemberInformation):
863
- chat_history: List[SlackHistoryMessageModel] = []
864
- chat_id: str
865
- source_id: str
866
- scheduled_messages: List[SlackScheduledMessageModel] = []
867
-
868
- @classmethod
869
- def from_dic(cls, dic: dict):
870
- model: UserContact | None = super().from_dic(dic)
871
- model.chat_history = [SlackHistoryMessageModel.from_dic(message) for message in dic.get('chat_history', [])]
872
- model.scheduled_messages = [SlackScheduledMessageModel.from_dic(item) for item in
873
- dic.get("scheduled_messages", [])]
874
- return model
875
-
876
-
877
- class SlackTimeZone:
878
- tz: Optional[str]
879
- tz_label: Optional[str]
880
- tz_offset: Optional[int]
881
-
882
-
883
- class ExtendedSlackMemberInformation(SlackMemberInformation):
884
- previous_publications = []
885
- name: str = None
886
-
887
- @classmethod
888
- def from_dic(cls, dic: dict):
889
- model: ExtendedSlackMemberInformation | None = super().from_dic(dic)
890
- if not model:
891
- return None
892
-
893
- model.previous_publications = [LeadModel.from_dic(lead) for lead in dic.get('previous_publications', [])]
894
- return model
895
-
896
- @staticmethod
897
- def to_lead(contact: ExtendedSlackMemberInformation, linkedin_contacts: Dict[str, LinkedinContact] = None) \
898
- -> ExtendedUserLeadModel:
899
- lead = ExtendedUserLeadModel()
900
- lead.id = str(contact.id)
901
- lead.created_at = contact.created_at
902
- lead.notes = ""
903
- lead.slack_channel = None
904
- lead.hidden = True
905
- lead.replies = []
906
- lead.reactions = 0
907
- lead.last_action_at = datetime.now(UTC)
908
- lead.created_at = datetime.now(UTC)
909
- if not hasattr(contact, "real_name"):
910
- contact.real_name = contact.name
911
- if not hasattr(contact, "display_name"):
912
- contact.display_name = contact.name
913
-
914
- lead.linkedin_urls = [linkedin_contacts[contact.sender_id].urls[0].get("url")] \
915
- if linkedin_contacts and contact.sender_id in linkedin_contacts \
916
- else [get_linkedin_search_contact(contact.real_name)]
917
- lead.message = MessageModel()
918
- lead.message.message = contact.real_name
919
- if contact.title:
920
- lead.message.message += contact.title
921
- lead.message.message_id = str(contact.id)
922
- lead.message.name = contact.source.source_name
923
- lead.message.source = contact.source
924
- lead.message.sender_id = contact.sender_id
925
- lead.message.companies = []
926
- lead.message.technologies = []
927
- lead.message.locations = []
928
- lead.message.chat_history = []
929
- lead.chat_viewed_at = datetime.now(UTC)
930
- lead.chat_history = []
931
- lead.previous_publications = contact.previous_publications if hasattr(contact, "previous_publications") else []
932
- lead.contact = contact
933
- return lead
934
-
935
-
936
- class UserTemplateModel(BaseModel):
937
- text: str
938
- subject: Optional[str]
939
- user_id: Optional[ObjectId]
940
-
941
-
942
- class LinkedinContact(BaseModel):
943
- full_name: str
944
- slack_user: str
945
- title: str
946
- urls: List[dict]
947
-
948
-
949
- class CloudFileModel(BaseModel):
950
- blob_path: str
951
- public_url: str
952
- file_name: str
953
-
954
- def __init__(self, blob_path: str, public_url: str, file_name: str):
955
- super().__init__()
956
- if not self.id:
957
- self.id = str(ObjectId())
958
- self.blob_path = blob_path
959
- self.public_url = public_url
960
- self.file_name = file_name