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,980 +0,0 @@
1
- import os
2
- import re
3
- from typing import List, Optional
4
- import pymongo
5
- from dateutil import tz
6
- from pymongo import MongoClient
7
- from bson.objectid import ObjectId
8
- from .enums import SourceType
9
- from .model import (LeadModel, BaseModel, UserModel, UserResetPasswordModel, BoardModel, BoardedStatus,
10
- DedicatedBotModel, SlackMemberInformation, UserTemplateModel, LinkedinContact,
11
- ExtendedUserLeadModel, UserLeadModel, ExtendedLeadModel, UserContact)
12
- from datetime import datetime, UTC
13
- from collections import OrderedDict
14
-
15
- client = MongoClient(os.environ.get('MONGO_CONNECTION_STRING', 'mongodb://127.0.0.1:27017/'))
16
-
17
-
18
- def to_object_id(oid):
19
- if isinstance(oid, ObjectId):
20
- return oid
21
- return ObjectId(oid)
22
-
23
-
24
- class BaseMongoRepository:
25
- collection_name = ''
26
- database_name = 'lgt_admin'
27
- model: BaseModel = BaseModel()
28
-
29
- def collection(self):
30
- return client[self.database_name][self.collection_name]
31
-
32
- def _collection(self, collection_name):
33
- return client[self.database_name][collection_name]
34
-
35
- def insert_many(self, items):
36
- insert_items = map(lambda x: x.to_dic(), items)
37
- self.collection().insert_many(insert_items)
38
-
39
- def insert(self, item: BaseModel):
40
- return self.collection().insert_one(item.to_dic())
41
-
42
- def add(self, item: BaseModel):
43
- return self.insert(item)
44
-
45
- def delete(self, id):
46
- res = self.collection().delete_one({'_id': to_object_id(id)})
47
- return res
48
-
49
-
50
- class UserMongoRepository(BaseMongoRepository):
51
- collection_name = 'users'
52
- model = UserModel
53
-
54
- def get(self, _id):
55
- return UserModel.from_dic(self.collection().find_one({'_id': to_object_id(_id)}))
56
-
57
- def get_by_email(self, email: str):
58
- pipeline = {'email': email}
59
- doc = self.collection().find_one(pipeline)
60
- return UserModel.from_dic(doc)
61
-
62
- def set(self, _id, **kwargs):
63
- update_dict = {k: v for k, v in kwargs.items() if v is not None}
64
- self.collection().update_one({'_id': to_object_id(_id)}, {'$set': update_dict})
65
-
66
- def get_users(self, users_ids=None, include_inactive=False):
67
- pipeline = {}
68
-
69
- if users_ids is not None:
70
- pipeline['_id'] = {'$in': [to_object_id(_id) for _id in users_ids]}
71
-
72
- if not include_inactive:
73
- pipeline['inactive'] = False
74
-
75
- return [UserModel.from_dic(doc) for doc in self.collection().find(pipeline)]
76
-
77
-
78
- class UserLeadMongoRepository(BaseMongoRepository):
79
- collection_name = 'user_leads'
80
- model = ExtendedUserLeadModel
81
-
82
- def get_count(self, user_id, **kwargs):
83
- pipeline = self.__create_leads_filter(user_id, **kwargs)
84
- return self.collection().count_documents(pipeline)
85
-
86
- def get_many(self, ids: list, user_id):
87
- docs = self.collection().find({"id": {'$in': ids}, 'user_id': to_object_id(user_id)})
88
- leads = [ExtendedUserLeadModel.from_dic(lead) for lead in docs]
89
- senders = [lead.message.sender_id for lead in leads]
90
- contacts = SlackContactUserRepository().find(user_id, users=senders)
91
- for lead in leads:
92
- lead.contact = next(filter(lambda x: x.sender_id == lead.message.sender_id, contacts), None)
93
-
94
- return leads
95
-
96
- def get_leads(self, user_id, skip: int, limit: int, **kwargs) -> List[ExtendedUserLeadModel]:
97
- pipeline = self.__create_leads_filter(user_id, **kwargs)
98
- sort_field = kwargs.get('sort_field', 'last_action_at')
99
- sort_direction = kwargs.get('sort_direction', 'ASCENDING')
100
- sort_direction = pymongo.ASCENDING if sort_direction == 'ASCENDING' else pymongo.DESCENDING
101
- docs = list(self.collection().find(pipeline).sort([(sort_field, sort_direction)]).skip(skip).limit(limit))
102
- leads = [ExtendedUserLeadModel.from_dic(x) for x in docs]
103
- senders = [lead.message.sender_id for lead in leads]
104
- contacts = SlackContactUserRepository().find(users=senders)
105
- for lead in leads:
106
- lead.contact = next(filter(lambda x: x.sender_id == lead.message.sender_id, contacts), None)
107
- return leads
108
-
109
- @staticmethod
110
- def __create_leads_filter(user_id, **kwargs):
111
- pipeline: dict = {'user_id': to_object_id(user_id)}
112
-
113
- if kwargs.get('status') is not None:
114
- pipeline['status'] = kwargs.get('status', '')
115
-
116
- if kwargs.get('board_id'):
117
- pipeline['board_id'] = to_object_id(kwargs.get('board_id'))
118
- elif kwargs.get('board_id') is not None:
119
- pipeline['$or'] = [{'board_id': ''}, {'board_id': None}]
120
-
121
- archived = kwargs.get('archived')
122
- from_date = kwargs.get('from_date')
123
- to_date = kwargs.get('to_date')
124
- has_followup = kwargs.get('has_followup', None)
125
- followup_to = kwargs.get('followup_to_date', None)
126
- followup_from = kwargs.get('followup_from_date', None)
127
- created_to = kwargs.get('created_to_date', None)
128
- created_from = kwargs.get('created_from_date', None)
129
- sender_ids = kwargs.get('sender_ids', None)
130
- text = kwargs.get('text', None)
131
- stop_words = kwargs.get('stop_words', None)
132
- tags = kwargs.get('tags', None)
133
- configs = kwargs.get('config', None)
134
- bots_names = kwargs.get('bots_names', None)
135
- locations = kwargs.get('locations', None)
136
- dedicated_bots_ids = kwargs.get('dedicated_bots_ids', None)
137
- with_chat = kwargs.get('with_chat', None)
138
- leads_ids = kwargs.get('leads_ids', None)
139
- exclude_leads = kwargs.get('exclude_leads', None)
140
- exclude_senders = kwargs.get('exclude_senders', None)
141
-
142
- pipeline['message.profile.display_name'] = {
143
- "$ne": "Slackbot"
144
- }
145
-
146
- if leads_ids is not None:
147
- pipeline["id"] = {'$in': leads_ids}
148
-
149
- if exclude_leads:
150
- pipeline['id'] = {'$nin': exclude_leads}
151
-
152
- if exclude_senders:
153
- pipeline['message.sender_id'] = {'$nin': exclude_senders}
154
-
155
- if archived is not None:
156
- pipeline['archived'] = archived
157
-
158
- if with_chat is not None:
159
- pipeline['chat_history'] = {'$exists': True, '$ne': []}
160
-
161
- if has_followup is not None:
162
- pipeline['followup_date'] = {'$ne': None} if has_followup else {'$eq': None}
163
-
164
- if from_date or to_date:
165
- pipeline['last_action_at'] = {}
166
-
167
- if from_date:
168
- start = datetime(from_date.year, from_date.month, from_date.day, tzinfo=tz.tzutc())
169
- pipeline['last_action_at']['$gte'] = start
170
-
171
- if to_date:
172
- end = datetime(to_date.year, to_date.month, to_date.day, 23, 59, 59, tzinfo=tz.tzutc())
173
- pipeline['last_action_at']['$lte'] = end
174
-
175
- if locations and len(locations) > 0:
176
- pipeline['message.locations'] = {"$in": locations}
177
-
178
- if sender_ids:
179
- pipeline['message.sender_id'] = {'$in': sender_ids}
180
-
181
- if followup_from or followup_to:
182
- pipeline['followup_date'] = {}
183
-
184
- if followup_from:
185
- followup_from = datetime(followup_from.year, followup_from.month, followup_from.day, tzinfo=tz.tzutc())
186
- pipeline['followup_date']['$gte'] = followup_from
187
-
188
- if followup_to:
189
- followup_to = datetime(followup_to.year, followup_to.month, followup_to.day, 23, 59, 59, tzinfo=tz.tzutc())
190
- pipeline['followup_date']['$lte'] = followup_to
191
-
192
- if created_to or created_from:
193
- pipeline['created_at'] = {}
194
-
195
- if created_to:
196
- created_to = datetime(created_to.year, created_to.month, created_to.day, 23, 59, 59, tzinfo=tz.tzutc())
197
- pipeline['created_at']['$lte'] = created_to
198
-
199
- if created_from:
200
- created_from = datetime(created_from.year, created_from.month, created_from.day, tzinfo=tz.tzutc())
201
- pipeline['created_at']['$gte'] = created_from
202
-
203
- if stop_words:
204
- pipeline['full_message_text'] = {'$regex': f'^(?!.*({stop_words})).*$', '$options': 'i'}
205
- elif text:
206
- pipeline['$text'] = {'$search': text}
207
-
208
- if tags:
209
- pipeline["tags"] = {"$elemMatch": {"$in": tags}}
210
-
211
- if configs:
212
- pipeline["message.configs.id"] = {"$in": configs}
213
-
214
- if bots_names is not None:
215
- pipeline['message.name'] = {'$in': bots_names}
216
-
217
- if dedicated_bots_ids is not None:
218
- pipeline["message.dedicated_slack_options.bot_id"] = {"$in": dedicated_bots_ids}
219
- return pipeline
220
-
221
- def get_unanswered_leads_ids(self, user_id: str, from_date=None):
222
- pipeline = [
223
- {
224
- '$match':
225
- {
226
- 'user_id': to_object_id(user_id)
227
- }
228
- },
229
- {
230
- '$match':
231
- {
232
- 'chat_history': {'$exists': True, '$not': {'$size': 0}}
233
- }
234
- },
235
- {
236
- '$project':
237
- {
238
- 'id': 1,
239
- 'sender_id': "$message.sender_id",
240
- 'last_message': {'$arrayElemAt': ['$chat_history', 0]}
241
- }
242
- },
243
- {
244
- '$addFields':
245
- {
246
- 'is_user_message': {'$ne': ['$sender_id', '$last_message.user']}
247
- }
248
- },
249
- {
250
- '$match':
251
- {
252
- '$and':
253
- [
254
- {'last_message.text': {'$regex': '\\?'}},
255
- {'is_user_message': False}
256
- ]
257
- }
258
- }
259
- ]
260
-
261
- if from_date:
262
- beginning_of_the_day = datetime(from_date.year, from_date.month, from_date.day, 0, 0, 0, 0)
263
- pipeline.insert(0, {"$match": {"created_at": {"$gte": beginning_of_the_day}}})
264
-
265
- data = list(self.collection().aggregate(pipeline))
266
- leads_ids = [item['id'] for item in data]
267
- return leads_ids
268
-
269
- def get_daily_analytics_by_workspace(self, user_configs: list,
270
- dedicated_only: bool | None,
271
- from_date: datetime,
272
- to_date: datetime,
273
- user_id: str):
274
- pipeline = [
275
- {
276
- '$addFields': {
277
- 'dedicated': {
278
- '$anyElementTrue': {
279
- '$map': {
280
- 'input': "$message.configs.id",
281
- 'as': "config",
282
- 'in': {'$in': ["$$config", user_configs]}
283
- }
284
- }
285
- }
286
- }
287
- },
288
- {
289
- '$project': {
290
- 'created_at': {
291
- '$dateToString': {
292
- 'format': '%Y-%m-%d',
293
- 'date': '$created_at'
294
- }
295
- },
296
- 'id': '$id'
297
- }
298
- },
299
- {
300
- '$group': {
301
- '_id': '$created_at',
302
- 'data': {'$push': '$id'}
303
- }
304
- },
305
- {
306
- '$sort': {'_id': 1}
307
- }
308
- ]
309
-
310
- if dedicated_only:
311
- pipeline.insert(1, {"$match": {'dedicated': True}})
312
- elif dedicated_only is False:
313
- pipeline.insert(1, {"$match": {'dedicated': False}})
314
-
315
- if from_date:
316
- beginning_of_the_day = datetime(from_date.year, from_date.month, from_date.day, 0, 0, 0, 0)
317
- pipeline.insert(0, {"$match": {"created_at": {"$gte": beginning_of_the_day}}})
318
-
319
- if to_date:
320
- end_of_the_day = datetime(to_date.year, to_date.month, to_date.day, 23, 59, 59, 999)
321
- pipeline.insert(0, {"$match": {"created_at": {"$lte": end_of_the_day}}})
322
-
323
- if user_id:
324
- pipeline.insert(0, {"$match": {'user_id': to_object_id(user_id)}})
325
-
326
- saved_messages = list(self.collection().aggregate(pipeline))
327
- saved_messages_dic = OrderedDict()
328
-
329
- for item in saved_messages:
330
- saved_messages_dic[item["_id"]] = item["data"]
331
-
332
- return saved_messages_dic
333
-
334
- def get_leads_after(self, action_after: datetime) -> [ExtendedUserLeadModel]:
335
- leads = self.collection().find({'last_action_at': {'$gte': action_after}})
336
- return [ExtendedUserLeadModel.from_dic(lead) for lead in leads]
337
-
338
- def add_lead(self, user_id, lead: UserLeadModel) -> None:
339
- if not lead.created_at:
340
- lead.created_at = datetime.now(UTC)
341
-
342
- if hasattr(lead, "_id"):
343
- lead._id = ObjectId()
344
-
345
- lead.user_id = user_id
346
- self.insert(lead)
347
-
348
- def update_lead(self, user_id, lead_id: str, **kwargs):
349
- pipeline = {'user_id': to_object_id(user_id), 'id': lead_id}
350
- update_dict = {k: v for k, v in kwargs.items() if v is not None}
351
- if 'board_id' in update_dict:
352
- update_dict['board_id'] = to_object_id(update_dict['board_id']) if len(update_dict['board_id']) == 24 \
353
- else update_dict['board_id']
354
- self.collection().update_one(pipeline, {'$set': update_dict})
355
-
356
- return ExtendedUserLeadModel.from_dic(self.collection().find_one(pipeline))
357
-
358
- def update_same_leads(self, user_id, sender_id: str, **kwargs):
359
- pipeline = {'user_id': to_object_id(user_id), 'message.sender_id': sender_id}
360
- update_dict = {k: v for k, v in kwargs.items() if v is not None}
361
- self.collection().update_many(pipeline, {'$set': update_dict})
362
-
363
- def update_leads_order(self, user_id, lead_ids: [str]):
364
- pipeline = {'user_id': to_object_id(user_id), 'id': {'$in': lead_ids}}
365
- docs = list(self.collection().find(pipeline))
366
-
367
- order = 0
368
- for lead_id in lead_ids:
369
- for doc in docs:
370
- if doc['id'] == lead_id:
371
- self.collection().update_one({'id': lead_id}, {'$set': {'order': order}}, upsert=False)
372
- order = order + 1
373
-
374
- def delete_lead(self, user_id, lead_id: str):
375
- """
376
-
377
- :param user_id:
378
- :param lead_id:
379
- :return: UserLeadModel
380
- """
381
-
382
- pipeline = {'user_id': to_object_id(user_id), 'id': lead_id}
383
- self.collection().delete_one(pipeline)
384
-
385
- def get_lead(self, user_id, message_id: str = None, lead_id: str = None, **kwargs):
386
- """
387
-
388
- :param user_id:
389
- :param message_id:
390
- :param lead_id:
391
- :return: UserLeadModel
392
- """
393
-
394
- pipeline = {'user_id': to_object_id(user_id)}
395
- sender_id = kwargs.get('sender_id')
396
-
397
- if message_id:
398
- pipeline['message.message_id'] = message_id
399
-
400
- if lead_id:
401
- pipeline['id'] = lead_id
402
-
403
- if sender_id:
404
- pipeline['message.sender_id'] = sender_id
405
-
406
- return ExtendedUserLeadModel.from_dic(self.collection().find_one(pipeline))
407
-
408
-
409
- class UserResetPasswordMongoRepository(BaseMongoRepository):
410
- pass
411
-
412
- collection_name = 'user_reset_passwords'
413
- model = UserResetPasswordModel
414
-
415
- def get(self, _id):
416
- return UserResetPasswordModel.from_dic(self.collection().find_one({'_id': to_object_id(_id)}))
417
-
418
- def delete(self, email):
419
- self.collection().delete_many({'email': email})
420
-
421
- def add(self, email) -> str:
422
- model = UserResetPasswordModel()
423
- model.email = email
424
- return self.collection().insert_one({'email': email}).inserted_id
425
-
426
-
427
- class LeadMongoRepository(BaseMongoRepository):
428
- pass
429
-
430
- database_name = 'lgt_admin'
431
- collection_name = 'general_leads'
432
- model = LeadModel
433
-
434
- def delete(self, _id):
435
- res = self.collection().delete_one({'id': _id})
436
- return res
437
-
438
- def get(self, _id=None, **kwargs):
439
- pipeline = {}
440
- timestamp = kwargs.get("timestamp")
441
- message_id = kwargs.get("message_id")
442
- channel_id = kwargs.get("channel_id")
443
- if _id:
444
- pipeline['id'] = _id
445
- if message_id:
446
- pipeline['message.message_id'] = message_id
447
- if channel_id:
448
- pipeline['message.channel_id'] = channel_id
449
- if timestamp:
450
- pipeline['message.timestamp'] = timestamp
451
- result: dict = self.collection().find_one(pipeline)
452
- if not result:
453
- return None
454
-
455
- return LeadModel.from_dic(result)
456
-
457
- def get_many(self, ids: list):
458
- docs = self.collection().find({"id": {'$in': ids}})
459
- leads = [ExtendedLeadModel.from_dic(lead) for lead in docs]
460
- senders = [lead.message.sender_id for lead in leads]
461
- contacts = SlackContactUserRepository().find(users=senders)
462
- for lead in leads:
463
- lead.contact = next(filter(lambda x: x.sender_id == lead.message.sender_id, contacts), None)
464
-
465
- return leads
466
-
467
- def get_by_sender_id(self, sender_id, exclude_leads: [str], skip: int, limit: int):
468
- pipeline = {'message.sender_id': sender_id, 'id': {'$nin': exclude_leads}}
469
- leads = self.collection().find(pipeline).sort([('created_at', pymongo.DESCENDING)]).skip(skip).limit(limit)
470
- return [LeadModel.from_dic(lead) for lead in leads]
471
-
472
- def get_by_message_id(self, message_id):
473
- """
474
-
475
- :rtype: LeadModel
476
- :param message_id:
477
- """
478
- doc: dict = self.collection().find_one({'message.message_id': message_id})
479
- if not doc:
480
- return None
481
-
482
- return LeadModel.from_dic(doc)
483
-
484
- def update(self, _id: str, **kwargs):
485
- update_dict = {k: v for k, v in kwargs.items() if v is not None}
486
- self.collection().update_one({'id': _id}, {'$set': update_dict}, upsert=False)
487
-
488
- def get_count(self, **kwargs):
489
- pipeline = self.__create_leads_filter(**kwargs)
490
- return self.collection().count_documents(pipeline)
491
-
492
- def get_list(self, skip, limit, **kwargs):
493
- pipeline = self.__create_leads_filter(**kwargs)
494
-
495
- sort_field = kwargs.get('sort_field', 'created_at')
496
- sort_direction = kwargs.get('sort_direction', 'ASCENDING')
497
- sort_direction = pymongo.ASCENDING if sort_direction == 'ASCENDING' else pymongo.DESCENDING
498
-
499
- leads = self.collection().find(pipeline).sort([(sort_field, sort_direction)]).skip(skip).limit(limit)
500
- return [LeadModel.from_dic(lead) for lead in leads]
501
-
502
- def __create_leads_filter(self, **kwargs):
503
- pipeline: dict = {"hidden": False}
504
-
505
- from_date: datetime = kwargs.get('from_date')
506
- to_date: datetime = kwargs.get('to_date')
507
-
508
- country = kwargs.get('country', None)
509
- user_id = kwargs.get('user_id', None)
510
- tags = kwargs.get('tags', None)
511
- text = kwargs.get('text', None)
512
- stop_words = kwargs.get('stop_words', None)
513
- exclude_leads = kwargs.get('exclude_leads', None)
514
- exclude_senders = kwargs.get('exclude_senders', None)
515
- excluded_channels = kwargs.get('excluded_channels', None)
516
- sender_ids = kwargs.get('sender_ids', None)
517
- configs = kwargs.get('config', None)
518
- bots_names = kwargs.get('bots_names', None)
519
- locations = kwargs.get('locations', None)
520
- publication_text = kwargs.get('publication_text')
521
-
522
- pipeline['message.profile.display_name'] = {
523
- "$ne": "Slackbot"
524
- }
525
-
526
- if from_date or to_date:
527
- pipeline['created_at'] = {}
528
-
529
- if from_date:
530
- start = from_date.astimezone(tz.tzutc())
531
- pipeline['created_at']['$gte'] = start
532
-
533
- if to_date:
534
- end = to_date.astimezone(tz.tzutc())
535
- pipeline['created_at']['$lte'] = end
536
-
537
- if locations and len(locations) > 0:
538
- pipeline['message.locations'] = {"$in": locations}
539
-
540
- if country:
541
- pipeline["message.slack_options.country"] = re.compile(country, re.IGNORECASE)
542
-
543
- if user_id:
544
- pipeline["$or"] = [
545
- {"message.dedicated_slack_options": {"$exists": False}},
546
- {"message.dedicated_slack_options": None},
547
- {"message.dedicated_slack_options.user_id": f"{user_id}"},
548
- ]
549
- else:
550
- pipeline["user_id"] = {'$exists': False}
551
-
552
- if stop_words:
553
- pipeline['full_message_text'] = {'$regex': f'^(?!.*({stop_words})).*$', '$options': 'i'}
554
- elif text:
555
- pipeline['$text'] = {'$search': text}
556
-
557
- if publication_text:
558
- pipeline['message.message'] = {'$regex': publication_text, '$options': 'i'}
559
-
560
- if tags:
561
- pipeline["tags"] = {"$elemMatch": {"$in": tags}}
562
-
563
- if exclude_leads:
564
- pipeline['id'] = {'$nin': exclude_leads}
565
-
566
- if exclude_senders:
567
- pipeline['message.sender_id'] = {'$nin': exclude_senders}
568
-
569
- if excluded_channels:
570
- pipeline['$and'] = []
571
- for ws, channels in excluded_channels.items():
572
- if channels is not None:
573
- pipeline['$and'].append(
574
- {'$or': [{'message.name': {'$ne': ws}}, {'message.channel_id': {'$nin': channels}}]})
575
-
576
- if sender_ids:
577
- pipeline['message.sender_id'] = {'$in': sender_ids}
578
-
579
- if configs:
580
- pipeline["message.configs.id"] = {"$not": {"$elemMatch": {"$nin": configs}}}
581
-
582
- if bots_names is not None:
583
- pipeline['message.name'] = {'$in': bots_names}
584
-
585
- pipeline['message.profile.real_name'] = {'$ne': 'Slackbot'}
586
-
587
- return pipeline
588
-
589
- def get_per_day(self, date: datetime):
590
- start_day = datetime(date.year, date.month, date.day, 0, 0, 0, tzinfo=tz.tzutc())
591
- end_day = datetime(date.year, date.month, date.day, 23, 59, 59, tzinfo=tz.tzutc())
592
- docs = self.collection().find({'created_at': {'$gte': start_day, '$lte': end_day}}).sort('created_at', 1)
593
- return [LeadModel.from_dic(x) for x in docs]
594
-
595
-
596
- class SpamLeadsMongoRepository(LeadMongoRepository):
597
- pass
598
-
599
- def __init__(self):
600
- self.collection_name = 'spam_leads'
601
-
602
- def get_list(self, skip, limit, sort_field: str = 'created_at', sort_direction: str = 'ASCENDING', **kwargs):
603
- pipeline = self.__create_leads_filter(**kwargs)
604
- sort_direction = pymongo.ASCENDING if sort_direction == 'ASCENDING' else pymongo.DESCENDING
605
- docs = self.collection().find(pipeline).sort([(sort_field, sort_direction)]).skip(skip).limit(limit)
606
- leads = [ExtendedLeadModel.from_dic(doc) for doc in docs]
607
- senders = [lead.message.sender_id for lead in leads]
608
- contacts = SlackContactUserRepository().find(users=senders)
609
- for lead in leads:
610
- lead.contact = next(filter(lambda x: x.sender_id == lead.message.sender_id, contacts), None)
611
- return leads
612
-
613
- def get_count(self, **kwargs):
614
- pipeline = self.__create_leads_filter(**kwargs)
615
- return self.collection().count_documents(pipeline)
616
-
617
- def __create_leads_filter(self, **kwargs):
618
- pipeline = {"user_id": {'$exists': False}}
619
- text = kwargs.get('text', None)
620
- stop_words = kwargs.get('stop_words', None)
621
- if stop_words:
622
- pipeline['full_message_text'] = {'$regex': f'^(?!.*({stop_words})).*$', '$options': 'i'}
623
- elif text:
624
- pipeline['$text'] = {'$search': text}
625
-
626
- return pipeline
627
-
628
-
629
- class GarbageLeadsMongoRepository(SpamLeadsMongoRepository):
630
- pass
631
-
632
- def __init__(self):
633
- super().__init__()
634
- self.collection_name = 'garbage_leads'
635
-
636
-
637
- class GarbageUserLeadsMongoRepository(UserLeadMongoRepository):
638
- pass
639
-
640
- def __init__(self):
641
- self.database_name = 'lgt_admin'
642
- self.collection_name = 'garbage_leads'
643
-
644
-
645
- class SpamUserLeadsMongoRepository(UserLeadMongoRepository):
646
- pass
647
-
648
- def __init__(self):
649
- self.database_name = 'lgt_admin'
650
- self.collection_name = 'spam_leads'
651
-
652
-
653
- class BoardsMongoRepository(BaseMongoRepository):
654
- pass
655
-
656
- collection_name = 'boards'
657
- model = BoardModel
658
-
659
- def create_board(self, user_id: str, name: str, **kwargs):
660
- is_primary = kwargs.get('is_primary', False)
661
-
662
- if is_primary:
663
- primary_board: dict = self.collection().find_one({'user_id': to_object_id(user_id),
664
- 'is_primary': is_primary})
665
- if primary_board:
666
- return BoardModel.from_dic(primary_board)
667
-
668
- board = BoardModel()
669
- board.name = name
670
- board.created_at = datetime.now(UTC)
671
- board.user_id = to_object_id(user_id)
672
- board.is_primary = is_primary
673
- self.collection().insert_one(BoardModel.to_dic(board))
674
-
675
- return BoardModel.from_dic(self.collection().find_one({'user_id': to_object_id(user_id), 'name': name}))
676
-
677
- def add_default_statuses(self, user_id: str, board_id: str):
678
- pipeline = {'user_id': to_object_id(user_id), '_id': to_object_id(board_id)}
679
- board = BoardModel.from_dic(self.collection().find_one(pipeline))
680
-
681
- if not board:
682
- return None
683
-
684
- board.statuses.append(BoardedStatus().from_dic({'name': 'Lead', 'order': 0}))
685
- board.statuses.append(BoardedStatus().from_dic({'name': 'Prospect', 'order': 1}))
686
- board.statuses.append(BoardedStatus().from_dic({'name': 'Opportunity', 'order': 2}))
687
- board.statuses.append(BoardedStatus().from_dic({'name': 'Call', 'order': 3}))
688
- board.statuses.append(BoardedStatus().from_dic({'name': 'Contract', 'order': 4}))
689
- board.statuses.append(BoardedStatus().from_dic({'name': 'Refused', 'order': 5}))
690
-
691
- return self.update_board(user_id, board_id, statuses=board.statuses)
692
-
693
- def get(self, user_id: str, **kwargs):
694
- pipeline = {'user_id': to_object_id(user_id)}
695
- is_primary = kwargs.get('is_primary')
696
- default = kwargs.get('default')
697
- name = kwargs.get('name')
698
-
699
- if is_primary is not None:
700
- pipeline['is_primary'] = is_primary
701
-
702
- if default is not None:
703
- pipeline['default'] = default
704
-
705
- if name:
706
- pipeline['name'] = name
707
-
708
- docs = self.collection().find(pipeline).sort('created_at', 1)
709
- return [BoardModel.from_dic(doc) for doc in docs]
710
-
711
- def get_primary(self, user_id: str):
712
- return BoardModel.from_dic(self.collection().find_one({'user_id': to_object_id(user_id), 'is_primary': True}))
713
-
714
- def get_by_id(self, id: str):
715
- return BoardModel.from_dic(self.collection().find_one({'_id': to_object_id(id)}))
716
-
717
- def delete_by_id(self, id: str):
718
- return self.collection().delete_many({'_id': to_object_id(id)})
719
-
720
- def update_board(self, user_id, board_id: str, **kwargs):
721
- pipeline = {'user_id': to_object_id(user_id), '_id': to_object_id(board_id)}
722
-
723
- if kwargs.get('statuses'):
724
- kwargs['statuses'] = [status.to_dic() for status in kwargs.get('statuses')
725
- if isinstance(status, BoardedStatus)]
726
-
727
- doc = BoardModel.from_dic(self.collection().find_one(pipeline))
728
- if not doc:
729
- return None
730
-
731
- update_dict = {k: v for k, v in kwargs.items() if v is not None}
732
- self.collection().update_one(pipeline, {'$set': update_dict})
733
-
734
- self._collection('user_leads').update_many({'user_id': to_object_id(user_id), 'board_id': to_object_id(doc.id)},
735
- {'$set': {'board_id': to_object_id(board_id)}})
736
-
737
- return BoardModel.from_dic(self.collection().find_one(pipeline))
738
-
739
-
740
- class DedicatedBotRepository(BaseMongoRepository):
741
- pass
742
-
743
- collection_name = 'dedicated_bots'
744
-
745
- def get_by_user_and_source_id(self, user_id: str, source_id: str) -> Optional[DedicatedBotModel]:
746
- doc = self.collection().find_one({"user_id": ObjectId(f"{user_id}"), "source.source_id": source_id})
747
- return DedicatedBotModel.from_dic(doc)
748
-
749
- def get_by_user_and_active_server_id(self, user_id: str, active_server_id: str) -> Optional[DedicatedBotModel]:
750
- doc = self.collection().find_one({"user_id": ObjectId(f"{user_id}"), "active_servers.id": active_server_id})
751
- return DedicatedBotModel.from_dic(doc)
752
-
753
- def add_or_update(self, bot: DedicatedBotModel):
754
- bot_dict = bot.to_dic()
755
- if '_id' in bot_dict:
756
- bot_dict.pop('_id')
757
- update_response = self.collection().update_one({"source.source_id": bot.source.source_id,
758
- "user_name": bot.user_name,
759
- "user_id": to_object_id(bot.user_id)},
760
- {'$set': bot_dict}, upsert=True)
761
- bot.id = update_response.upserted_id if update_response.upserted_id else bot.id
762
- return bot
763
-
764
- def get_all(self, only_valid: bool = False, **kwargs) -> List[DedicatedBotModel]:
765
- kwargs["only_valid"] = only_valid
766
- pipeline = self.__create_bots_filter(**kwargs)
767
- docs = self.collection().find(pipeline)
768
- return [DedicatedBotModel.from_dic(doc) for doc in docs]
769
-
770
- def get_one(self, **kwargs):
771
- pipeline = self.__create_bots_filter(**kwargs)
772
- return DedicatedBotModel.from_dic(self.collection().find_one(pipeline))
773
-
774
- def get_user_bots(self, user_id: str, only_valid: bool = False,
775
- include_deleted: bool = False, include_paused: bool = False, **kwargs) -> List[DedicatedBotModel]:
776
- pipeline = {'user_id': to_object_id(user_id)}
777
- user_name = kwargs.get('user_name')
778
-
779
- if user_name:
780
- pipeline['user_name'] = user_name
781
-
782
- if not include_deleted:
783
- pipeline['deleted'] = False
784
-
785
- if not include_paused:
786
- pipeline['paused'] = False
787
-
788
- if only_valid:
789
- pipeline['invalid_creds'] = False
790
-
791
- return [DedicatedBotModel.from_dic(doc) for doc in self.collection().find(pipeline)]
792
-
793
- def delete(self, _id: str):
794
- self.collection().update_one({'_id': to_object_id(f"{_id}")}, {"$set": {"deleted": True}})
795
-
796
- @staticmethod
797
- def __create_bots_filter(**kwargs):
798
- pipeline = {}
799
- name = kwargs.get('name')
800
- source_type = kwargs.get('source_type', None)
801
- user_name = kwargs.get('user_name')
802
- only_valid = kwargs.get('only_valid')
803
- include_paused = kwargs.get('include_paused', False)
804
- include_deleted = kwargs.get('include_deleted', False)
805
- bot_id = kwargs.get('id')
806
- user_id = kwargs.get('user_id')
807
- source_id = kwargs.get('source_id')
808
- server_id = kwargs.get('server_id')
809
-
810
- if bot_id:
811
- pipeline["_id"] = to_object_id(bot_id)
812
-
813
- if user_id:
814
- pipeline["user_id"] = to_object_id(user_id)
815
-
816
- if name:
817
- pipeline["name"] = name
818
-
819
- if source_type:
820
- pipeline["source.source_type"] = source_type
821
-
822
- if user_name:
823
- pipeline["user_name"] = user_name
824
-
825
- if source_id:
826
- pipeline["source.source_id"] = source_id
827
-
828
- if server_id:
829
- pipeline["servers"] = {'$elemMatch': {'id': server_id, 'active': True}}
830
-
831
- if only_valid:
832
- pipeline['invalid_creds'] = False
833
-
834
- if not include_deleted:
835
- pipeline['deleted'] = False
836
-
837
- if not include_paused and source_type == SourceType.DISCORD:
838
- pipeline['paused'] = False
839
-
840
- return pipeline
841
-
842
-
843
- class SlackContactUserRepository(BaseMongoRepository):
844
- collection_name = "slack_contact"
845
- model = SlackMemberInformation
846
-
847
- def find(self, text: Optional[str] = None, skip: int = 0, limit: int = 1000, **kwargs):
848
- pipeline = {}
849
-
850
- users = kwargs.pop("users")
851
- presence_updated_at = kwargs.pop("online_updated_at", None)
852
- if text:
853
- pipeline['$text'] = {'$search': text}
854
-
855
- if users:
856
- pipeline['sender_id'] = {'$in': users}
857
-
858
- if presence_updated_at:
859
- pipeline['online_updated_at'] = {'$gte': presence_updated_at}
860
-
861
- pipeline = {**pipeline, **kwargs}
862
-
863
- docs = self.collection().find(pipeline).sort([("real_name", pymongo.ASCENDING)]) \
864
- .skip(skip) \
865
- .limit(limit)
866
- return [SlackMemberInformation.from_dic(doc) for doc in docs]
867
-
868
- def find_one(self, user_id: str):
869
- pipeline = {"sender_id": user_id}
870
- return SlackMemberInformation.from_dic(self.collection().find_one(pipeline))
871
-
872
- def get_count_in_workspaces(self):
873
- pipeline = [
874
- {
875
- '$match': {
876
- 'user': {
877
- '$ne': 'USLACKBOT'
878
- }
879
- }
880
- }, {
881
- '$group': {
882
- '_id': '$workspace',
883
- 'count': {
884
- '$sum': 1
885
- }
886
- }
887
- }
888
- ]
889
- docs = list(self.collection().aggregate(pipeline))
890
- return {doc['_id']: doc['count'] for doc in docs}
891
-
892
-
893
- class UserTemplatesRepository(BaseMongoRepository):
894
- collection_name = "user_templates"
895
- model = UserTemplateModel
896
-
897
- def get_all(self, user_id: str):
898
- return [UserTemplateModel.from_dic(doc) for doc in self.collection().find({'user_id': to_object_id(user_id)})]
899
-
900
- def get(self, id: str):
901
- return UserTemplateModel.from_dic(self.collection().find_one({'_id': to_object_id(id)}))
902
-
903
- def create_or_update(self, template: UserTemplateModel):
904
- result = self.collection().update_one(
905
- {"_id": to_object_id(template.id)},
906
- {'$set': template.to_dic()},
907
- upsert=True)
908
-
909
- if result.upserted_id:
910
- template.id = result.upserted_id
911
-
912
- return template
913
-
914
- def delete_by_id(self, id: str):
915
- return self.collection().find_one_and_delete({'_id': to_object_id(id)})
916
-
917
-
918
- class LinkedinContactRepository(BaseMongoRepository):
919
- collection_name = "linkedin_contact"
920
- model = LinkedinContact
921
-
922
- def find(self, **kwargs):
923
- docs = self.collection().find({**kwargs})
924
- return [LinkedinContact.from_dic(doc) for doc in docs]
925
-
926
-
927
- class UserContactsRepository(BaseMongoRepository):
928
- collection_name = 'user_contacts'
929
-
930
- def find(self, user_id: str, **kwargs):
931
- pipeline = {'user_id': to_object_id(user_id)}
932
-
933
- users = kwargs.get('users')
934
- spam = kwargs.get('spam')
935
- with_chat_only = kwargs.get('with_chat_only', False)
936
-
937
- if users:
938
- pipeline['sender_id'] = {'$in': users}
939
-
940
- if spam is not None:
941
- pipeline['spam'] = spam
942
-
943
- if with_chat_only:
944
- pipeline['chat_id'] = {'$ne': None}
945
-
946
- docs = self.collection().find(pipeline)
947
- return [UserContact.from_dic(doc) for doc in docs]
948
-
949
- def find_one(self, user_id: str, **kwargs):
950
- pipeline = {'user_id': to_object_id(user_id)}
951
-
952
- sender_id = kwargs.get('sender_id')
953
- if sender_id:
954
- pipeline['sender_id'] = sender_id
955
-
956
- if contact_instance := self.collection().find_one(pipeline):
957
- return UserContact.from_dic(contact_instance)
958
-
959
- def update(self, user_id: str, sender_id: str, **kwargs):
960
- pipeline = {'user_id': to_object_id(user_id), 'sender_id': sender_id}
961
- update_dict = {k: v for k, v in kwargs.items() if v is not None}
962
- self.collection().update_one(pipeline, {'$set': update_dict}, upsert=False)
963
-
964
-
965
- class TeamRepository(BaseMongoRepository):
966
- collection_name = 'teams'
967
-
968
- def get_teammate(self, user_id: str, teammate_id: str):
969
- pipeline = [
970
- {
971
- "$match": {
972
- "$or": [
973
- {"author_id": to_object_id(user_id), "recipient_id": to_object_id(teammate_id)},
974
- {"author_id": to_object_id(teammate_id), "recipient_id": to_object_id(user_id)}
975
- ],
976
- "status": "approved"
977
- }
978
- }
979
- ]
980
- return list(self.collection().aggregate(pipeline))