1# Copyright (c) 2012-2019 Seafile Ltd. 2# encoding: utf-8 3from datetime import datetime 4import logging 5import re 6import requests 7import json 8 9from django.core.management.base import BaseCommand 10from django.urls import reverse 11from django.utils import translation 12from django.utils.translation import ungettext 13 14from seahub.base.models import CommandsLastCheck 15from seahub.notifications.models import UserNotification 16from seahub.utils import get_site_scheme_and_netloc, get_site_name 17from seahub.auth.models import SocialAuthUser 18 19from seahub.dingtalk.utils import dingtalk_get_access_token, dingtalk_get_userid_by_unionid 20from seahub.dingtalk.settings import DINGTALK_MESSAGE_SEND_TO_CONVERSATION_URL, \ 21 DINGTALK_AGENT_ID 22 23# Get an instance of a logger 24logger = logging.getLogger(__name__) 25 26 27# https://ding-doc.dingtalk.com/doc#/serverapi3/wvdxel 28 29########## Utility Functions ########## 30def remove_html_a_element(s): 31 """ 32 Replace <a ..>xx</a> to xx and wrap content with <div></div>. 33 """ 34 patt = '<a.*?>(.+?)</a>' 35 36 def repl(matchobj): 37 return matchobj.group(1) 38 39 return re.sub(patt, repl, s) 40 41 42class CommandLogMixin(object): 43 44 def println(self, msg): 45 self.stdout.write('[%s] %s\n' % (str(datetime.now()), msg)) 46 47 def log_error(self, msg): 48 logger.error(msg) 49 self.println(msg) 50 51 def log_info(self, msg): 52 logger.info(msg) 53 self.println(msg) 54 55 def log_debug(self, msg): 56 logger.debug(msg) 57 self.println(msg) 58 59 60####################################### 61 62class Command(BaseCommand, CommandLogMixin): 63 """ send dingtalk notifications 64 """ 65 66 help = 'Send dingtalk msg to user if he/she has unseen notices every ' 67 'period of time.' 68 label = "notifications_send_dingtalk_notices" 69 70 def handle(self, *args, **options): 71 self.log_debug('Start sending dingtalk msg...') 72 self.do_action() 73 self.log_debug('Finish sending dingtalk msg.\n') 74 75 def send_dingtalk_msg(self, user_id, title, content): 76 77 self.log_info('Send dingtalk msg to user: %s, msg: %s' % (user_id, content)) 78 data = { 79 "agent_id": DINGTALK_AGENT_ID, 80 "userid_list": user_id, 81 "msg": { 82 "msgtype": "markdown", 83 "markdown": { 84 "title": title, 85 "text": content 86 } 87 } 88 } 89 resp_json = requests.post(self.dingtalk_message_send_to_conversation_url, 90 data=json.dumps(data)).json() 91 if resp_json.get('errcode') != 0: 92 self.log_info(resp_json) 93 94 def do_action(self): 95 96 # check before start 97 access_token = dingtalk_get_access_token() 98 if not access_token: 99 self.log_error('can not get access_token') 100 101 self.dingtalk_message_send_to_conversation_url = DINGTALK_MESSAGE_SEND_TO_CONVERSATION_URL + '?access_token=' + access_token 102 self.detail_url = get_site_scheme_and_netloc().rstrip('/') + reverse('user_notification_list') 103 site_name = get_site_name() 104 105 # start 106 now = datetime.now() 107 today = datetime.now().replace(hour=0).replace(minute=0).replace( 108 second=0).replace(microsecond=0) 109 110 # 1. get all users who are connected dingtalk 111 socials = SocialAuthUser.objects.filter(provider='dingtalk') 112 users = [(x.username, x.uid) for x in socials] 113 self.log_info('Found %d users' % len(users)) 114 if not users: 115 return 116 117 user_uid_map = {} 118 for username, uid in users: 119 user_uid_map[username] = dingtalk_get_userid_by_unionid(uid) 120 121 # 2. get previous time that command last runs 122 try: 123 cmd_last_check = CommandsLastCheck.objects.get(command_type=self.label) 124 self.log_debug('Last check time is %s' % cmd_last_check.last_check) 125 126 last_check_dt = cmd_last_check.last_check 127 128 cmd_last_check.last_check = now 129 cmd_last_check.save() 130 except CommandsLastCheck.DoesNotExist: 131 last_check_dt = today 132 self.log_debug('Create new last check time: %s' % now) 133 CommandsLastCheck(command_type=self.label, last_check=now).save() 134 135 # 3. get all unseen notices for those users 136 qs = UserNotification.objects.filter( 137 timestamp__gt=last_check_dt 138 ).filter(seen=False).filter( 139 to_user__in=list(user_uid_map.keys()) 140 ) 141 self.log_info('Found %d notices' % qs.count()) 142 if qs.count() == 0: 143 return 144 145 user_notices = {} 146 for q in qs: 147 if q.to_user not in user_notices: 148 user_notices[q.to_user] = [q] 149 else: 150 user_notices[q.to_user].append(q) 151 152 # save current language 153 cur_language = translation.get_language() 154 # active zh-cn 155 translation.activate('zh-cn') 156 self.log_info('the language is set to zh-cn') 157 158 # 4. send msg to users 159 for username, uid in users: 160 user_id = user_uid_map[username] 161 notices = user_notices.get(username, []) 162 count = len(notices) 163 if count == 0: 164 continue 165 166 title = ungettext( 167 "\n" 168 "You've got 1 new notice on %(site_name)s:\n", 169 "\n" 170 "You've got %(num)s new notices on %(site_name)s:\n", 171 count 172 ) % {'num': count, 'site_name': site_name, } 173 174 content = ' \n '.join([remove_html_a_element(x.format_msg()) for x in notices]) 175 self.send_dingtalk_msg(user_id, title, content) 176 177 # reset language 178 translation.activate(cur_language) 179 self.log_info('reset language success') 180