3
3
import concurrent .futures
4
4
import functools
5
5
import json
6
+ import re
6
7
import os
7
8
import requests
8
9
import sys
10
+ import platform
9
11
import traceback
10
12
import tempfile
11
13
import urwid
12
14
from datetime import datetime
15
+
13
16
from sclack .components import Attachment , Channel , ChannelHeader , ChatBox , Dm
14
17
from sclack .components import Indicators , MarkdownText , Message , MessageBox
15
18
from sclack .components import NewMessagesDivider , Profile , ProfileSideBar
@@ -81,6 +84,48 @@ def __init__(self, config):
81
84
unhandled_input = self .unhandled_input
82
85
)
83
86
self .configure_screen (self .urwid_loop .screen )
87
+ self .mentioned_patterns = None
88
+
89
+ def get_mentioned_patterns (self ):
90
+ slack_mentions = [
91
+ '<!everyone>' ,
92
+ '<!here>' ,
93
+ '<!channel>' ,
94
+ '<@{}>' .format (self .store .state .auth ['user_id' ]),
95
+ ]
96
+
97
+ patterns = []
98
+
99
+ for mention in slack_mentions :
100
+ patterns .append ('^{}[ ]+' .format (mention ))
101
+ patterns .append ('^{}$' .format (mention ))
102
+ patterns .append ('[ ]+{}' .format (mention ))
103
+
104
+ return re .compile ('|' .join (patterns ))
105
+
106
+ def should_notify_me (self , message_obj ):
107
+ """
108
+ Checking whether notify to user
109
+ :param message_obj:
110
+ :return:
111
+ """
112
+ # You send message, don't need notification
113
+ if self .config ['features' ]['notification' ] in ['' , 'none' ] or message_obj ['user' ] == self .store .state .auth ['user_id' ]:
114
+ return False
115
+
116
+ if self .config ['features' ]['notification' ] == 'all' :
117
+ return True
118
+
119
+ # Private message
120
+ if message_obj .get ('channel' ) is not None and message_obj .get ('channel' )[0 ] == 'D' :
121
+ return True
122
+
123
+ regex = self .mentioned_patterns
124
+ if regex is None :
125
+ regex = self .get_mentioned_patterns ()
126
+ self .mentioned_patterns = regex
127
+
128
+ return len (re .findall (regex , message_obj ['text' ])) > 0
84
129
85
130
def start (self ):
86
131
self ._loading = True
@@ -137,6 +182,8 @@ def mount_sidebar(self, executor):
137
182
loop .run_in_executor (executor , self .store .load_users ),
138
183
loop .run_in_executor (executor , self .store .load_user_dnd ),
139
184
)
185
+ self .mentioned_patterns = self .get_mentioned_patterns ()
186
+
140
187
profile = Profile (name = self .store .state .auth ['user' ], is_snoozed = self .store .state .is_snoozed )
141
188
channels = [
142
189
Channel (
@@ -155,7 +202,7 @@ def mount_sidebar(self, executor):
155
202
if user :
156
203
dms .append (Dm (
157
204
dm ['id' ],
158
- name = user . get ( 'display_name' ) or user . get ( 'real_name' ) or user [ 'name' ] ,
205
+ name = self . store . get_user_display_name ( user ) ,
159
206
user = dm ['user' ],
160
207
you = user ['id' ] == self .store .state .auth ['user_id' ]
161
208
))
@@ -280,7 +327,7 @@ def go_to_profile(self, user_id):
280
327
return
281
328
self .store .state .profile_user_id = user_id
282
329
profile = ProfileSideBar (
283
- user . get ( 'display_name' ) or user . get ( 'real_name' ) or user [ 'name' ] ,
330
+ self . store . get_user_display_name ( user ) ,
284
331
user ['profile' ].get ('status_text' , None ),
285
332
user ['profile' ].get ('tz_label' , None ),
286
333
user ['profile' ].get ('phone' , None ),
@@ -296,7 +343,7 @@ def render_chatbox_header(self):
296
343
if self .store .state .channel ['id' ][0 ] == 'D' :
297
344
user = self .store .find_user_by_id (self .store .state .channel ['user' ])
298
345
header = ChannelHeader (
299
- name = user . get ( 'display_name' ) or user . get ( 'real_name' ) or user [ 'name' ] ,
346
+ name = self . store . get_user_display_name ( user ) ,
300
347
topic = user ['profile' ]['status_text' ],
301
348
is_starred = self .store .state .channel .get ('is_starred' , False ),
302
349
is_dm_workaround_please_remove_me = True
@@ -318,6 +365,16 @@ def on_change_topic(self, text):
318
365
self .store .set_topic (self .store .state .channel ['id' ], text )
319
366
self .go_to_sidebar ()
320
367
368
+ def notification_messages (self , messages ):
369
+ """
370
+ Check and send notifications
371
+ :param messages:
372
+ :return:
373
+ """
374
+ for message in messages :
375
+ if self .should_notify_me (message ):
376
+ self .send_notification (message , MarkdownText (message ['text' ]))
377
+
321
378
def render_message (self , message ):
322
379
is_app = False
323
380
subtype = message .get ('subtype' )
@@ -367,6 +424,7 @@ def render_message(self, message):
367
424
return None
368
425
369
426
user_id = user ['id' ]
427
+ # TODO
370
428
user_name = user ['profile' ]['display_name' ] or user .get ('name' )
371
429
color = user .get ('color' )
372
430
if message .get ('file' ):
@@ -379,6 +437,7 @@ def render_message(self, message):
379
437
return None
380
438
381
439
user_id = user ['id' ]
440
+ # TODO
382
441
user_name = user ['profile' ]['display_name' ] or user .get ('name' )
383
442
color = user .get ('color' )
384
443
@@ -391,6 +450,7 @@ def render_message(self, message):
391
450
]
392
451
393
452
attachments = []
453
+
394
454
for attachment in message .get ('attachments' , []):
395
455
attachment_widget = Attachment (
396
456
service_name = attachment .get ('service_name' ),
@@ -463,8 +523,9 @@ def render_messages(self, messages):
463
523
previous_date = self .store .state .last_date
464
524
last_read_datetime = datetime .fromtimestamp (float (self .store .state .channel .get ('last_read' , '0' )))
465
525
today = datetime .today ().date ()
466
- for message in messages :
467
- message_datetime = datetime .fromtimestamp (float (message ['ts' ]))
526
+
527
+ for raw_message in messages :
528
+ message_datetime = datetime .fromtimestamp (float (raw_message ['ts' ]))
468
529
message_date = message_datetime .date ()
469
530
date_text = None
470
531
unread_text = None
@@ -485,11 +546,50 @@ def render_messages(self, messages):
485
546
_messages .append (NewMessagesDivider (unread_text , date = date_text ))
486
547
elif date_text is not None :
487
548
_messages .append (TextDivider (('history_date' , date_text ), 'center' ))
488
- message = self .render_message (message )
549
+
550
+ message = self .render_message (raw_message )
551
+
489
552
if message is not None :
490
553
_messages .append (message )
554
+
491
555
return _messages
492
556
557
+ def send_notification (self , raw_message , markdown_text ):
558
+ """
559
+ Only MacOS
560
+ @TODO Linux libnotify and Windows
561
+ :param raw_message:
562
+ :param markdown_text:
563
+ :return:
564
+ """
565
+ user = self .store .find_user_by_id (raw_message .get ('user' ))
566
+ sender_name = self .store .get_user_display_name (user )
567
+
568
+ # TODO Checking bot
569
+ if raw_message .get ('channel' )[0 ] == 'D' :
570
+ notification_title = 'New message in {}' .format (
571
+ self .store .state .auth ['team' ]
572
+ )
573
+ else :
574
+ notification_title = 'New message in {} #{}' .format (
575
+ self .store .state .auth ['team' ],
576
+ self .store .get_channel_name (raw_message .get ('channel' )),
577
+ )
578
+
579
+ sub_title = sender_name
580
+
581
+ if platform .system () == 'Darwin' :
582
+ # Macos
583
+ import pync
584
+ pync .notify (
585
+ markdown_text .render_text (),
586
+ title = notification_title ,
587
+ subtitle = sub_title ,
588
+ appIcon = './resources/slack_icon.png'
589
+ )
590
+ else :
591
+ pass
592
+
493
593
@asyncio .coroutine
494
594
def _go_to_channel (self , channel_id ):
495
595
with concurrent .futures .ThreadPoolExecutor (max_workers = 20 ) as executor :
@@ -573,6 +673,7 @@ def stop_typing(*args):
573
673
self .chatbox .message_box .typing = None
574
674
575
675
alarm = None
676
+
576
677
while self .store .slack .server .connected is True :
577
678
events = self .store .slack .rtm_read ()
578
679
for event in events :
@@ -602,9 +703,17 @@ def stop_typing(*args):
602
703
else :
603
704
self .chatbox .body .body .extend (self .render_messages ([event ]))
604
705
self .chatbox .body .scroll_to_bottom ()
605
- elif event ['type' ] == 'user_typing' :
706
+ else :
707
+ pass
708
+
709
+ if event .get ('subtype' ) != 'message_deleted' and event .get ('subtype' ) != 'message_changed' :
710
+ # Notification
711
+ self .notification_messages ([event ])
712
+ elif event ['type' ] == 'user_typing' :
713
+ if event .get ('channel' ) == self .store .state .channel ['id' ]:
606
714
user = self .store .find_user_by_id (event ['user' ])
607
- name = user .get ('display_name' ) or user .get ('real_name' ) or user ['name' ]
715
+ name = self .store .get_user_display_name (user )
716
+
608
717
if alarm is not None :
609
718
self .urwid_loop .remove_alarm (alarm )
610
719
self .chatbox .message_box .typing = name
0 commit comments