33import concurrent .futures
44import functools
55import json
6+ import re
67import os
78import requests
89import sys
10+ import platform
911import time
1012import traceback
1113import tempfile
1214import urwid
1315from datetime import datetime
16+
1417from sclack .components import Attachment , Channel , ChannelHeader , ChatBox , Dm
1518from sclack .components import Indicators , MarkdownText , Message , MessageBox
1619from sclack .components import NewMessagesDivider , Profile , ProfileSideBar
@@ -84,6 +87,48 @@ def __init__(self, config):
8487 )
8588 self .configure_screen (self .urwid_loop .screen )
8689 self .last_keypress = (0 , None )
90+ self .mentioned_patterns = None
91+
92+ def get_mentioned_patterns (self ):
93+ slack_mentions = [
94+ '<!everyone>' ,
95+ '<!here>' ,
96+ '<!channel>' ,
97+ '<@{}>' .format (self .store .state .auth ['user_id' ]),
98+ ]
99+
100+ patterns = []
101+
102+ for mention in slack_mentions :
103+ patterns .append ('^{}[ ]+' .format (mention ))
104+ patterns .append ('^{}$' .format (mention ))
105+ patterns .append ('[ ]+{}' .format (mention ))
106+
107+ return re .compile ('|' .join (patterns ))
108+
109+ def should_notify_me (self , message_obj ):
110+ """
111+ Checking whether notify to user
112+ :param message_obj:
113+ :return:
114+ """
115+ # You send message, don't need notification
116+ if self .config ['features' ]['notification' ] in ['' , 'none' ] or message_obj ['user' ] == self .store .state .auth ['user_id' ]:
117+ return False
118+
119+ if self .config ['features' ]['notification' ] == 'all' :
120+ return True
121+
122+ # Private message
123+ if message_obj .get ('channel' ) is not None and message_obj .get ('channel' )[0 ] == 'D' :
124+ return True
125+
126+ regex = self .mentioned_patterns
127+ if regex is None :
128+ regex = self .get_mentioned_patterns ()
129+ self .mentioned_patterns = regex
130+
131+ return len (re .findall (regex , message_obj ['text' ])) > 0
87132
88133 def start (self ):
89134 self ._loading = True
@@ -140,6 +185,8 @@ def mount_sidebar(self, executor):
140185 loop .run_in_executor (executor , self .store .load_users ),
141186 loop .run_in_executor (executor , self .store .load_user_dnd ),
142187 )
188+ self .mentioned_patterns = self .get_mentioned_patterns ()
189+
143190 profile = Profile (name = self .store .state .auth ['user' ], is_snoozed = self .store .state .is_snoozed )
144191 channels = [
145192 Channel (
@@ -158,7 +205,7 @@ def mount_sidebar(self, executor):
158205 if user :
159206 dms .append (Dm (
160207 dm ['id' ],
161- name = user . get ( 'display_name' ) or user . get ( 'real_name' ) or user [ 'name' ] ,
208+ name = self . store . get_user_display_name ( user ) ,
162209 user = dm ['user' ],
163210 you = user ['id' ] == self .store .state .auth ['user_id' ]
164211 ))
@@ -286,7 +333,7 @@ def go_to_profile(self, user_id):
286333 return
287334 self .store .state .profile_user_id = user_id
288335 profile = ProfileSideBar (
289- user . get ( 'display_name' ) or user . get ( 'real_name' ) or user [ 'name' ] ,
336+ self . store . get_user_display_name ( user ) ,
290337 user ['profile' ].get ('status_text' , None ),
291338 user ['profile' ].get ('tz_label' , None ),
292339 user ['profile' ].get ('phone' , None ),
@@ -302,7 +349,7 @@ def render_chatbox_header(self):
302349 if self .store .state .channel ['id' ][0 ] == 'D' :
303350 user = self .store .find_user_by_id (self .store .state .channel ['user' ])
304351 header = ChannelHeader (
305- name = user . get ( 'display_name' ) or user . get ( 'real_name' ) or user [ 'name' ] ,
352+ name = self . store . get_user_display_name ( user ) ,
306353 topic = user ['profile' ]['status_text' ],
307354 is_starred = self .store .state .channel .get ('is_starred' , False ),
308355 is_dm_workaround_please_remove_me = True
@@ -324,6 +371,16 @@ def on_change_topic(self, text):
324371 self .store .set_topic (self .store .state .channel ['id' ], text )
325372 self .go_to_sidebar ()
326373
374+ def notification_messages (self , messages ):
375+ """
376+ Check and send notifications
377+ :param messages:
378+ :return:
379+ """
380+ for message in messages :
381+ if self .should_notify_me (message ):
382+ self .send_notification (message , MarkdownText (message ['text' ]))
383+
327384 def render_message (self , message , channel_id = None ):
328385 is_app = False
329386 subtype = message .get ('subtype' )
@@ -375,6 +432,7 @@ def render_message(self, message, channel_id=None):
375432 return None
376433
377434 user_id = user ['id' ]
435+ # TODO
378436 user_name = user ['profile' ]['display_name' ] or user .get ('name' )
379437 color = user .get ('color' )
380438 if message .get ('file' ):
@@ -387,6 +445,7 @@ def render_message(self, message, channel_id=None):
387445 return None
388446
389447 user_id = user ['id' ]
448+ # TODO
390449 user_name = user ['profile' ]['display_name' ] or user .get ('name' )
391450 color = user .get ('color' )
392451
@@ -399,6 +458,7 @@ def render_message(self, message, channel_id=None):
399458 ]
400459
401460 attachments = []
461+
402462 for attachment in message .get ('attachments' , []):
403463 attachment_widget = Attachment (
404464 service_name = attachment .get ('service_name' ),
@@ -475,8 +535,9 @@ def render_messages(self, messages, channel_id=None):
475535 previous_date = self .store .state .last_date
476536 last_read_datetime = datetime .fromtimestamp (float (self .store .state .channel .get ('last_read' , '0' )))
477537 today = datetime .today ().date ()
478- for message in messages :
479- message_datetime = datetime .fromtimestamp (float (message ['ts' ]))
538+
539+ for raw_message in messages :
540+ message_datetime = datetime .fromtimestamp (float (raw_message ['ts' ]))
480541 message_date = message_datetime .date ()
481542 date_text = None
482543 unread_text = None
@@ -505,6 +566,42 @@ def render_messages(self, messages, channel_id=None):
505566
506567 return _messages
507568
569+ def send_notification (self , raw_message , markdown_text ):
570+ """
571+ Only MacOS
572+ @TODO Linux libnotify and Windows
573+ :param raw_message:
574+ :param markdown_text:
575+ :return:
576+ """
577+ user = self .store .find_user_by_id (raw_message .get ('user' ))
578+ sender_name = self .store .get_user_display_name (user )
579+
580+ # TODO Checking bot
581+ if raw_message .get ('channel' )[0 ] == 'D' :
582+ notification_title = 'New message in {}' .format (
583+ self .store .state .auth ['team' ]
584+ )
585+ else :
586+ notification_title = 'New message in {} #{}' .format (
587+ self .store .state .auth ['team' ],
588+ self .store .get_channel_name (raw_message .get ('channel' )),
589+ )
590+
591+ sub_title = sender_name
592+
593+ if platform .system () == 'Darwin' :
594+ # Macos
595+ import pync
596+ pync .notify (
597+ markdown_text .render_text (),
598+ title = notification_title ,
599+ subtitle = sub_title ,
600+ appIcon = './resources/slack_icon.png'
601+ )
602+ else :
603+ pass
604+
508605 def handle_mark_read (self , data ):
509606 """
510607 Mark as read to bottom
@@ -633,6 +730,7 @@ def stop_typing(*args):
633730 self .chatbox .message_box .typing = None
634731
635732 alarm = None
733+
636734 while self .store .slack .server .connected is True :
637735 events = self .store .slack .rtm_read ()
638736 for event in events :
@@ -664,10 +762,15 @@ def stop_typing(*args):
664762 self .chatbox .body .scroll_to_bottom ()
665763 else :
666764 pass
765+
766+ if event .get ('subtype' ) != 'message_deleted' and event .get ('subtype' ) != 'message_changed' :
767+ # Notification
768+ self .notification_messages ([event ])
667769 elif event ['type' ] == 'user_typing' :
668770 if event .get ('channel' ) == self .store .state .channel ['id' ]:
669771 user = self .store .find_user_by_id (event ['user' ])
670- name = user .get ('display_name' ) or user .get ('real_name' ) or user ['name' ]
772+ name = self .store .get_user_display_name (user )
773+
671774 if alarm is not None :
672775 self .urwid_loop .remove_alarm (alarm )
673776 self .chatbox .message_box .typing = name
@@ -799,6 +902,7 @@ def ask_for_token(json_config):
799902 config_file .write (json .dumps (token_config , indent = False ))
800903 json_config .update (token_config )
801904
905+
802906if __name__ == '__main__' :
803907 json_config = {}
804908 with open ('./config.json' , 'r' ) as config_file :
0 commit comments