leadguru-jobs 0.404.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.404.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.404.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 -964
  41. lgt_jobs/lgt_data/mongo_repository.py +0 -980
  42. {leadguru_jobs-0.404.0.dist-info → leadguru_jobs-0.405.0.dist-info}/WHEEL +0 -0
  43. {leadguru_jobs-0.404.0.dist-info → leadguru_jobs-0.405.0.dist-info}/top_level.txt +0 -0
@@ -1,964 +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
- self.bots: List[BotInfo] = []
688
-
689
- @classmethod
690
- def from_dic(cls, dic: dict):
691
- if not dic:
692
- return None
693
-
694
- result: ExtendedUserLeadModel | None = super().from_dic(dic)
695
- if not result:
696
- return None
697
-
698
- result.contact = SlackMemberInformation.from_dic(dic.get('contact'))
699
- result.previous_publications = [LeadModel.from_dic(lead) for lead in dic.get('previous_publications', [])]
700
- return result
701
-
702
- def to_dic(self):
703
- result = super().to_dic()
704
- result["contact"] = self.contact.to_dic()
705
- return result
706
-
707
- def to_csv(self, board_name: str) -> List[str]:
708
- return [self.message.source, self.contact.real_name, self.contact.title, self.contact.email,
709
- self.notes, board_name, self.status,
710
- self.followup_date.strftime("%d.%m.%Y %H:%M") if self.followup_date else "",
711
- self.message.message.replace('\n', ' ').strip()]
712
-
713
-
714
- class BoardModel(BaseModel):
715
- pass
716
-
717
- def __init__(self):
718
- super().__init__()
719
- self.name = None
720
- self.user_id = None
721
- self.statuses = list()
722
- self.is_primary = None
723
- self.default = False
724
-
725
- @classmethod
726
- def from_dic(cls, dic: dict):
727
- if not dic:
728
- return None
729
-
730
- model = BoardModel()
731
- for k, v in dic.items():
732
- setattr(model, k, v)
733
-
734
- model.id = dic.get('_id')
735
- model.statuses = [BoardedStatus.from_dic(status) for status in dic.get('statuses', [])]
736
- return model
737
-
738
- def to_dic(self):
739
- result = copy.deepcopy(self.__dict__)
740
- result["statuses"] = [BoardedStatus.to_dic(status) for status in self.statuses]
741
-
742
- for status in result['statuses']:
743
- status['board_id'] = result['id']
744
-
745
- return result
746
-
747
-
748
- class BoardedStatus:
749
- pass
750
-
751
- def __init__(self):
752
- self.id = None
753
- self.name = None
754
- self.order = 0
755
- self.is_primary = False
756
- self.default = False
757
-
758
- def to_dic(self):
759
- self.id = self.name
760
- return copy.deepcopy(self.__dict__)
761
-
762
- @classmethod
763
- def from_dic(cls, dic: dict):
764
- if not dic:
765
- return None
766
- model = cls()
767
- for k, v in dic.items():
768
- setattr(model, k, v)
769
- return model
770
-
771
-
772
- class SlackProfile:
773
- pass
774
-
775
- def __init__(self):
776
- self.title = ''
777
- self.phone = ''
778
- self.skype = ''
779
- self.display_name = ''
780
- self.real_name = ''
781
- self.email = ''
782
-
783
- def to_dic(self):
784
- return copy.deepcopy(self.__dict__)
785
-
786
- @classmethod
787
- def from_dic(cls, dic: dict):
788
- if not dic:
789
- return None
790
- model = cls()
791
- for k, v in dic.items():
792
- setattr(model, k, v)
793
- return model
794
-
795
-
796
- class SlackMemberInformation(BaseModel, SlackProfile):
797
- workspace: str
798
- sender_id: str
799
- images: dict
800
- full_text: str
801
- deleted: bool = False
802
- is_bot: bool = False
803
- is_app_user: bool = False
804
- is_admin: bool = False
805
- is_owner: bool = False
806
- is_email_confirmed: bool = False
807
- online: Optional[str] = None
808
- online_updated_at: datetime = None
809
- timezone: SlackTimeZone = None
810
- source: Source = None
811
-
812
- @classmethod
813
- def from_dic(cls, dic: dict):
814
- model: SlackMemberInformation = cls()
815
- if not dic:
816
- return None
817
-
818
- for k, v in dic.items():
819
- setattr(model, k, v)
820
-
821
- model.online = dic.get('online', '') == "active"
822
- model: SlackMemberInformation | None = super().from_dic(dic)
823
- model.source = Source.from_dic(dic.get('source'))
824
- return model
825
-
826
- def to_dic(self):
827
- result = copy.deepcopy(self.__dict__)
828
- if result.get('source'):
829
- result['source'] = Source.to_dic(result.get('source'))
830
- return result
831
-
832
- @staticmethod
833
- def from_slack_response(slack_profile: dict, source: Source = None):
834
- member_info: SlackMemberInformation = SlackMemberInformation()
835
- member_info.source = source
836
- member_info.sender_id = slack_profile.get("id")
837
- member_info.display_name = slack_profile["profile"].get("display_name")
838
- member_info.real_name = slack_profile["profile"].get("real_name")
839
- member_info.title = slack_profile["profile"].get("title")
840
- member_info.phone = slack_profile["profile"].get("phone")
841
- member_info.skype = slack_profile["profile"].get("skype")
842
- member_info.email = slack_profile["profile"].get("email")
843
- member_info.images = {
844
- 'image_24': slack_profile.get("profile", {}).get("image_24",
845
- 'https://a.slack-edge.com/80588/img/slackbot_24.png'),
846
- 'image_32': slack_profile.get("profile", {}).get("image_32",
847
- 'https://a.slack-edge.com/80588/img/slackbot_32.png'),
848
- 'image_48': slack_profile.get("profile", {}).get("image_48",
849
- 'https://a.slack-edge.com/80588/img/slackbot_48.png'),
850
- 'image_72': slack_profile.get("profile", {}).get("image_72",
851
- 'https://a.slack-edge.com/80588/img/slackbot_72.png'),
852
- 'image_192': slack_profile.get("profile", {}).get("image_192",
853
- 'https://a.slack-edge.com/80588/img/slackbot_192.png'),
854
- 'image_512': slack_profile.get("profile", {}).get("image_512",
855
- 'https://a.slack-edge.com/80588/img/slackbot_512.png'),
856
-
857
- }
858
- member_info.timezone = {"tz": slack_profile.get("tz"), "tz_label": slack_profile.get("tz_label"),
859
- "tz_offset": slack_profile.get("tz_offset")}
860
- return member_info
861
-
862
-
863
- class UserContact(SlackMemberInformation):
864
- chat_history: List[SlackHistoryMessageModel] = []
865
- chat_id: str
866
- source_id: str
867
- scheduled_messages: List[SlackScheduledMessageModel] = []
868
-
869
- @classmethod
870
- def from_dic(cls, dic: dict):
871
- model: UserContact | None = super().from_dic(dic)
872
- model.chat_history = [SlackHistoryMessageModel.from_dic(message) for message in dic.get('chat_history', [])]
873
- model.scheduled_messages = [SlackScheduledMessageModel.from_dic(item) for item in
874
- dic.get("scheduled_messages", [])]
875
- return model
876
-
877
-
878
- class SlackTimeZone:
879
- tz: Optional[str]
880
- tz_label: Optional[str]
881
- tz_offset: Optional[int]
882
-
883
-
884
- class ExtendedSlackMemberInformation(SlackMemberInformation):
885
- previous_publications = []
886
- name: str = None
887
- bots: List[BotInfo] = []
888
-
889
- @classmethod
890
- def from_dic(cls, dic: dict):
891
- model: ExtendedSlackMemberInformation | None = super().from_dic(dic)
892
- if not model:
893
- return None
894
-
895
- model.previous_publications = [LeadModel.from_dic(lead) for lead in dic.get('previous_publications', [])]
896
- model.bots = [BotInfo.from_dic(bot) for bot in dic.get('bots', [])]
897
- return model
898
-
899
- @staticmethod
900
- def to_lead(contact: ExtendedSlackMemberInformation, linkedin_contacts: Dict[str, LinkedinContact] = None) \
901
- -> ExtendedUserLeadModel:
902
- lead = ExtendedUserLeadModel()
903
- lead.id = str(contact.id)
904
- lead.created_at = contact.created_at
905
- lead.notes = ""
906
- lead.slack_channel = None
907
- lead.hidden = True
908
- lead.replies = []
909
- lead.reactions = 0
910
- lead.last_action_at = datetime.now(UTC)
911
- lead.created_at = datetime.now(UTC)
912
- if not hasattr(contact, "real_name"):
913
- contact.real_name = contact.name
914
- if not hasattr(contact, "display_name"):
915
- contact.display_name = contact.name
916
-
917
- lead.linkedin_urls = [linkedin_contacts[contact.sender_id].urls[0].get("url")] \
918
- if linkedin_contacts and contact.sender_id in linkedin_contacts \
919
- else [get_linkedin_search_contact(contact.real_name)]
920
- lead.message = MessageModel()
921
- lead.message.message = contact.real_name
922
- if contact.title:
923
- lead.message.message += contact.title
924
- lead.message.message_id = str(contact.id)
925
- lead.message.name = contact.source.source_name
926
- lead.message.source = contact.source
927
- lead.message.sender_id = contact.sender_id
928
- lead.message.companies = []
929
- lead.message.technologies = []
930
- lead.message.locations = []
931
- lead.message.chat_history = []
932
- lead.chat_viewed_at = datetime.now(UTC)
933
- lead.chat_history = []
934
- lead.previous_publications = contact.previous_publications if hasattr(contact, "previous_publications") else []
935
- lead.bots = contact.bots if hasattr(contact, "bots") else []
936
- lead.contact = contact
937
- return lead
938
-
939
-
940
- class UserTemplateModel(BaseModel):
941
- text: str
942
- subject: Optional[str]
943
- user_id: Optional[ObjectId]
944
-
945
-
946
- class LinkedinContact(BaseModel):
947
- full_name: str
948
- slack_user: str
949
- title: str
950
- urls: List[dict]
951
-
952
-
953
- class CloudFileModel(BaseModel):
954
- blob_path: str
955
- public_url: str
956
- file_name: str
957
-
958
- def __init__(self, blob_path: str, public_url: str, file_name: str):
959
- super().__init__()
960
- if not self.id:
961
- self.id = str(ObjectId())
962
- self.blob_path = blob_path
963
- self.public_url = public_url
964
- self.file_name = file_name