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.
- {leadguru_jobs-0.404.0.dist-info → leadguru_jobs-0.405.0.dist-info}/METADATA +5 -10
- leadguru_jobs-0.405.0.dist-info/RECORD +26 -0
- lgt_jobs/__init__.py +4 -4
- lgt_jobs/jobs/analytics.py +1 -1
- lgt_jobs/jobs/archive_leads.py +2 -2
- lgt_jobs/jobs/bot_stats_update.py +11 -10
- lgt_jobs/jobs/chat_history.py +6 -6
- lgt_jobs/jobs/inbox_leads.py +6 -5
- lgt_jobs/jobs/mass_message.py +2 -2
- lgt_jobs/jobs/send_code.py +1 -1
- lgt_jobs/jobs/send_slack_message.py +5 -5
- lgt_jobs/jobs/update_slack_profile.py +14 -12
- lgt_jobs/jobs/user_balance_update.py +5 -5
- lgt_jobs/jobs/workspace_connect.py +5 -7
- lgt_jobs/main.py +11 -9
- lgt_jobs/runner.py +9 -6
- lgt_jobs/smtp.py +1 -1
- leadguru_jobs-0.404.0.dist-info/RECORD +0 -49
- lgt_jobs/lgt_common/__init__.py +0 -0
- lgt_jobs/lgt_common/discord_client/__init__.py +0 -0
- lgt_jobs/lgt_common/discord_client/discord_client.py +0 -58
- lgt_jobs/lgt_common/discord_client/methods.py +0 -15
- lgt_jobs/lgt_common/enums/__init__.py +0 -0
- lgt_jobs/lgt_common/enums/slack_errors.py +0 -6
- lgt_jobs/lgt_common/helpers.py +0 -18
- lgt_jobs/lgt_common/lgt_logging.py +0 -15
- lgt_jobs/lgt_common/pubsub/__init__.py +0 -0
- lgt_jobs/lgt_common/pubsub/command.py +0 -14
- lgt_jobs/lgt_common/pubsub/messages.py +0 -37
- lgt_jobs/lgt_common/pubsub/pubsubfactory.py +0 -51
- lgt_jobs/lgt_common/slack_client/__init__.py +0 -0
- lgt_jobs/lgt_common/slack_client/methods.py +0 -46
- lgt_jobs/lgt_common/slack_client/slack_client.py +0 -392
- lgt_jobs/lgt_common/slack_client/web_client.py +0 -164
- lgt_jobs/lgt_data/__init__.py +0 -0
- lgt_jobs/lgt_data/analytics.py +0 -723
- lgt_jobs/lgt_data/engine.py +0 -223
- lgt_jobs/lgt_data/enums.py +0 -68
- lgt_jobs/lgt_data/helpers.py +0 -2
- lgt_jobs/lgt_data/model.py +0 -964
- lgt_jobs/lgt_data/mongo_repository.py +0 -980
- {leadguru_jobs-0.404.0.dist-info → leadguru_jobs-0.405.0.dist-info}/WHEEL +0 -0
- {leadguru_jobs-0.404.0.dist-info → leadguru_jobs-0.405.0.dist-info}/top_level.txt +0 -0
lgt_jobs/lgt_data/model.py
DELETED
@@ -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
|