33import concurrent .futures
44import functools
55import json
6+ import re
67import os
78import requests
89import sys
10+ import platform
911import traceback
1012import tempfile
1113import urwid
1214from datetime import datetime
15+
1316from sclack .components import Attachment , Channel , ChannelHeader , ChatBox , Dm
1417from sclack .components import Indicators , MarkdownText , Message , MessageBox
1518from sclack .components import NewMessagesDivider , Profile , ProfileSideBar
@@ -81,6 +84,48 @@ def __init__(self, config):
8184 unhandled_input = self .unhandled_input
8285 )
8386 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
84129
85130 def start (self ):
86131 self ._loading = True
@@ -137,6 +182,8 @@ def mount_sidebar(self, executor):
137182 loop .run_in_executor (executor , self .store .load_users ),
138183 loop .run_in_executor (executor , self .store .load_user_dnd ),
139184 )
185+ self .mentioned_patterns = self .get_mentioned_patterns ()
186+
140187 profile = Profile (name = self .store .state .auth ['user' ], is_snoozed = self .store .state .is_snoozed )
141188 channels = [
142189 Channel (
@@ -155,7 +202,7 @@ def mount_sidebar(self, executor):
155202 if user :
156203 dms .append (Dm (
157204 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 ) ,
159206 user = dm ['user' ],
160207 you = user ['id' ] == self .store .state .auth ['user_id' ]
161208 ))
@@ -280,7 +327,7 @@ def go_to_profile(self, user_id):
280327 return
281328 self .store .state .profile_user_id = user_id
282329 profile = ProfileSideBar (
283- user . get ( 'display_name' ) or user . get ( 'real_name' ) or user [ 'name' ] ,
330+ self . store . get_user_display_name ( user ) ,
284331 user ['profile' ].get ('status_text' , None ),
285332 user ['profile' ].get ('tz_label' , None ),
286333 user ['profile' ].get ('phone' , None ),
@@ -296,7 +343,7 @@ def render_chatbox_header(self):
296343 if self .store .state .channel ['id' ][0 ] == 'D' :
297344 user = self .store .find_user_by_id (self .store .state .channel ['user' ])
298345 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 ) ,
300347 topic = user ['profile' ]['status_text' ],
301348 is_starred = self .store .state .channel .get ('is_starred' , False ),
302349 is_dm_workaround_please_remove_me = True
@@ -318,6 +365,16 @@ def on_change_topic(self, text):
318365 self .store .set_topic (self .store .state .channel ['id' ], text )
319366 self .go_to_sidebar ()
320367
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+
321378 def render_message (self , message ):
322379 is_app = False
323380 subtype = message .get ('subtype' )
@@ -367,6 +424,7 @@ def render_message(self, message):
367424 return None
368425
369426 user_id = user ['id' ]
427+ # TODO
370428 user_name = user ['profile' ]['display_name' ] or user .get ('name' )
371429 color = user .get ('color' )
372430 if message .get ('file' ):
@@ -379,6 +437,7 @@ def render_message(self, message):
379437 return None
380438
381439 user_id = user ['id' ]
440+ # TODO
382441 user_name = user ['profile' ]['display_name' ] or user .get ('name' )
383442 color = user .get ('color' )
384443
@@ -391,6 +450,7 @@ def render_message(self, message):
391450 ]
392451
393452 attachments = []
453+
394454 for attachment in message .get ('attachments' , []):
395455 attachment_widget = Attachment (
396456 service_name = attachment .get ('service_name' ),
@@ -463,8 +523,9 @@ def render_messages(self, messages):
463523 previous_date = self .store .state .last_date
464524 last_read_datetime = datetime .fromtimestamp (float (self .store .state .channel .get ('last_read' , '0' )))
465525 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' ]))
468529 message_date = message_datetime .date ()
469530 date_text = None
470531 unread_text = None
@@ -485,11 +546,50 @@ def render_messages(self, messages):
485546 _messages .append (NewMessagesDivider (unread_text , date = date_text ))
486547 elif date_text is not None :
487548 _messages .append (TextDivider (('history_date' , date_text ), 'center' ))
488- message = self .render_message (message )
549+
550+ message = self .render_message (raw_message )
551+
489552 if message is not None :
490553 _messages .append (message )
554+
491555 return _messages
492556
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+
493593 @asyncio .coroutine
494594 def _go_to_channel (self , channel_id ):
495595 with concurrent .futures .ThreadPoolExecutor (max_workers = 20 ) as executor :
@@ -573,6 +673,7 @@ def stop_typing(*args):
573673 self .chatbox .message_box .typing = None
574674
575675 alarm = None
676+
576677 while self .store .slack .server .connected is True :
577678 events = self .store .slack .rtm_read ()
578679 for event in events :
@@ -602,9 +703,17 @@ def stop_typing(*args):
602703 else :
603704 self .chatbox .body .body .extend (self .render_messages ([event ]))
604705 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' ]:
606714 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+
608717 if alarm is not None :
609718 self .urwid_loop .remove_alarm (alarm )
610719 self .chatbox .message_box .typing = name
0 commit comments