1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-25 14:50:29 +00:00

dingtalk send msg (#4524)

* dingtalk msg

* use unionid

* update

Co-authored-by: lian <lian@seafile.com>
This commit is contained in:
lian
2020-06-02 21:45:49 +08:00
committed by GitHub
parent 072637de5b
commit 7da8e55d68
4 changed files with 252 additions and 35 deletions

View File

@@ -5,7 +5,6 @@
import logging import logging
import requests import requests
from django.core.cache import cache
from django.core.files.base import ContentFile from django.core.files.base import ContentFile
from rest_framework.authentication import SessionAuthentication from rest_framework.authentication import SessionAuthentication
@@ -28,9 +27,8 @@ from seahub.profile.models import Profile
from seahub.avatar.models import Avatar from seahub.avatar.models import Avatar
from seahub.group.utils import validate_group_name from seahub.group.utils import validate_group_name
from seahub.utils import normalize_cache_key from seahub.dingtalk.utils import dingtalk_get_access_token
from seahub.dingtalk.settings import ENABLE_DINGTALK, DINGTALK_DEPARTMENT_APP_KEY, \ from seahub.dingtalk.settings import ENABLE_DINGTALK, \
DINGTALK_DEPARTMENT_APP_SECRET, DINGTALK_DEPARTMENT_GET_ACCESS_TOKEN_URL, \
DINGTALK_DEPARTMENT_LIST_DEPARTMENT_URL, \ DINGTALK_DEPARTMENT_LIST_DEPARTMENT_URL, \
DINGTALK_DEPARTMENT_GET_DEPARTMENT_URL, \ DINGTALK_DEPARTMENT_GET_DEPARTMENT_URL, \
DINGTALK_DEPARTMENT_GET_DEPARTMENT_USER_LIST_URL, \ DINGTALK_DEPARTMENT_GET_DEPARTMENT_USER_LIST_URL, \
@@ -40,34 +38,6 @@ DEPARTMENT_OWNER = 'system admin'
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def get_dingtalk_access_token():
cache_key = normalize_cache_key('DINGTALK_ACCESS_TOKEN')
access_token = cache.get(cache_key, None)
if not access_token:
data = {
'appkey': DINGTALK_DEPARTMENT_APP_KEY,
'appsecret': DINGTALK_DEPARTMENT_APP_SECRET,
}
resp_json = requests.get(DINGTALK_DEPARTMENT_GET_ACCESS_TOKEN_URL,
params=data).json()
access_token = resp_json.get('access_token', '')
if not access_token:
logger.error('failed to get dingtalk access_token')
logger.error(data)
logger.error(DINGTALK_DEPARTMENT_GET_ACCESS_TOKEN_URL)
logger.error(resp_json)
return ''
expires_in = resp_json.get('expires_in', 7200)
cache.set(cache_key, access_token, expires_in)
return access_token
def update_dingtalk_user_info(email, name, contact_email, avatar_url): def update_dingtalk_user_info(email, name, contact_email, avatar_url):
# make sure the contact_email is unique # make sure the contact_email is unique
@@ -114,7 +84,7 @@ class AdminDingtalkDepartments(APIView):
if not request.user.admin_permissions.can_manage_user(): if not request.user.admin_permissions.can_manage_user():
return api_error(status.HTTP_403_FORBIDDEN, 'Permission denied.') return api_error(status.HTTP_403_FORBIDDEN, 'Permission denied.')
access_token = get_dingtalk_access_token() access_token = dingtalk_get_access_token()
if not access_token: if not access_token:
error_msg = '获取钉钉组织架构失败' error_msg = '获取钉钉组织架构失败'
return api_error(status.HTTP_404_NOT_FOUND, error_msg) return api_error(status.HTTP_404_NOT_FOUND, error_msg)
@@ -151,7 +121,7 @@ class AdminDingtalkDepartmentMembers(APIView):
if not request.user.admin_permissions.can_manage_user(): if not request.user.admin_permissions.can_manage_user():
return api_error(status.HTTP_403_FORBIDDEN, 'Permission denied.') return api_error(status.HTTP_403_FORBIDDEN, 'Permission denied.')
access_token = get_dingtalk_access_token() access_token = dingtalk_get_access_token()
if not access_token: if not access_token:
error_msg = '获取钉钉组织架构成员失败' error_msg = '获取钉钉组织架构成员失败'
return api_error(status.HTTP_404_NOT_FOUND, error_msg) return api_error(status.HTTP_404_NOT_FOUND, error_msg)
@@ -320,7 +290,7 @@ class AdminDingtalkDepartmentsImport(APIView):
error_msg = 'department_id invalid.' error_msg = 'department_id invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg) return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
access_token = get_dingtalk_access_token() access_token = dingtalk_get_access_token()
if not access_token: if not access_token:
error_msg = '获取钉钉组织架构失败' error_msg = '获取钉钉组织架构失败'
return api_error(status.HTTP_404_NOT_FOUND, error_msg) return api_error(status.HTTP_404_NOT_FOUND, error_msg)

View File

@@ -1,6 +1,8 @@
import seahub.settings as settings import seahub.settings as settings
ENABLE_DINGTALK = getattr(settings, 'ENABLE_DINGTALK', False) ENABLE_DINGTALK = getattr(settings, 'ENABLE_DINGTALK', False)
DINGTALK_AGENT_ID = getattr(settings, 'DINGTALK_AGENT_ID', '')
DINGTALK_GET_USERID_BY_UNIONID = getattr(settings, 'DINGTALK_GET_USERID_BY_UNIONID', 'https://oapi.dingtalk.com/user/getUseridByUnionid')
# for dingtalk qr connect # for dingtalk qr connect
DINGTALK_QR_CONNECT_LOGIN_REMEMBER_ME = True DINGTALK_QR_CONNECT_LOGIN_REMEMBER_ME = True
@@ -21,3 +23,6 @@ DINGTALK_DEPARTMENT_LIST_DEPARTMENT_URL = getattr(settings, 'DINGTALK_DEPARTMENT
DINGTALK_DEPARTMENT_GET_DEPARTMENT_URL = getattr(settings, 'DINGTALK_DEPARTMENT_GET_DEPARTMENT_URL', 'https://oapi.dingtalk.com/department/get') DINGTALK_DEPARTMENT_GET_DEPARTMENT_URL = getattr(settings, 'DINGTALK_DEPARTMENT_GET_DEPARTMENT_URL', 'https://oapi.dingtalk.com/department/get')
DINGTALK_DEPARTMENT_GET_DEPARTMENT_USER_LIST_URL = getattr(settings, 'DINGTALK_DEPARTMENT_GET_DEPARTMENT_USER_LIST_URL', 'https://oapi.dingtalk.com/user/listbypage') DINGTALK_DEPARTMENT_GET_DEPARTMENT_USER_LIST_URL = getattr(settings, 'DINGTALK_DEPARTMENT_GET_DEPARTMENT_USER_LIST_URL', 'https://oapi.dingtalk.com/user/listbypage')
DINGTALK_DEPARTMENT_USER_SIZE = 100 DINGTALK_DEPARTMENT_USER_SIZE = 100
# for dingtalk message
DINGTALK_MESSAGE_SEND_TO_CONVERSATION_URL = getattr(settings, 'DINGTALK_MESSAGE_SEND_TO_CONVERSATION_URL', 'https://oapi.dingtalk.com/topapi/message/corpconversation/asyncsend_v2')

63
seahub/dingtalk/utils.py Normal file
View File

@@ -0,0 +1,63 @@
import logging
import requests
from django.core.cache import cache
from seahub.utils import normalize_cache_key
from seahub.dingtalk.settings import DINGTALK_DEPARTMENT_APP_KEY, \
DINGTALK_DEPARTMENT_APP_SECRET, \
DINGTALK_DEPARTMENT_GET_ACCESS_TOKEN_URL, \
DINGTALK_GET_USERID_BY_UNIONID
logger = logging.getLogger(__name__)
def dingtalk_get_access_token():
cache_key = normalize_cache_key('DINGTALK_ACCESS_TOKEN')
access_token = cache.get(cache_key, None)
if access_token:
return access_token
data = {
'appkey': DINGTALK_DEPARTMENT_APP_KEY,
'appsecret': DINGTALK_DEPARTMENT_APP_SECRET,
}
resp_json = requests.get(DINGTALK_DEPARTMENT_GET_ACCESS_TOKEN_URL,
params=data).json()
access_token = resp_json.get('access_token', '')
if not access_token:
logger.error('failed to get dingtalk access_token')
logger.error(DINGTALK_DEPARTMENT_GET_ACCESS_TOKEN_URL)
logger.error(data)
logger.error(resp_json)
return ''
expires_in = resp_json.get('expires_in', 7200)
cache.set(cache_key, access_token, expires_in)
return access_token
def dingtalk_get_userid_by_unionid(union_id):
cache_key = normalize_cache_key('DINGTALK_UNION_ID_%s' % union_id)
user_id = cache.get(cache_key, None)
if user_id:
return user_id
access_token = dingtalk_get_access_token()
data = {
'access_token': access_token,
'unionid': union_id,
}
resp_json = requests.get(DINGTALK_GET_USERID_BY_UNIONID, params=data).json()
user_id = resp_json.get('userid', '')
if not user_id:
logger.error('failed to get userid by unionid: %s' % union_id)
logger.error(DINGTALK_GET_USERID_BY_UNIONID)
logger.error(data)
logger.error(resp_json)
return ''
cache.set(cache_key, user_id)
return user_id

View File

@@ -0,0 +1,179 @@
# Copyright (c) 2012-2019 Seafile Ltd.
# encoding: utf-8
from datetime import datetime
import logging
import re
import requests
import json
from django.core.management.base import BaseCommand
from django.core.urlresolvers import reverse
from django.utils import translation
from django.utils.translation import ungettext
from seahub.base.models import CommandsLastCheck
from seahub.notifications.models import UserNotification
from seahub.utils import get_site_scheme_and_netloc, get_site_name
from seahub.auth.models import SocialAuthUser
from seahub.dingtalk.utils import dingtalk_get_access_token, dingtalk_get_userid_by_unionid
from seahub.dingtalk.settings import DINGTALK_MESSAGE_SEND_TO_CONVERSATION_URL, \
DINGTALK_AGENT_ID
# Get an instance of a logger
logger = logging.getLogger(__name__)
# https://ding-doc.dingtalk.com/doc#/serverapi3/wvdxel
########## Utility Functions ##########
def remove_html_a_element(s):
"""
Replace <a ..>xx</a> to xx and wrap content with <div></div>.
"""
patt = '<a.*?>(.+?)</a>'
def repl(matchobj):
return matchobj.group(1)
return re.sub(patt, repl, s)
class CommandLogMixin(object):
def println(self, msg):
self.stdout.write('[%s] %s\n' % (str(datetime.now()), msg))
def log_error(self, msg):
logger.error(msg)
self.println(msg)
def log_info(self, msg):
logger.info(msg)
self.println(msg)
def log_debug(self, msg):
logger.debug(msg)
self.println(msg)
#######################################
class Command(BaseCommand, CommandLogMixin):
""" send dingtalk notifications
"""
help = 'Send dingtalk msg to user if he/she has unseen notices every '
'period of time.'
label = "notifications_send_dingtalk_notices"
def handle(self, *args, **options):
self.log_debug('Start sending dingtalk msg...')
self.do_action()
self.log_debug('Finish sending dingtalk msg.\n')
def send_dingtalk_msg(self, user_id, title, content):
self.log_info('Send dingtalk msg to user: %s, msg: %s' % (user_id, content))
data = {
"agent_id": DINGTALK_AGENT_ID,
"userid_list": user_id,
"msg": {
"msgtype": "markdown",
"markdown": {
"title": title,
"text": content
}
}
}
resp_json = requests.post(self.dingtalk_message_send_to_conversation_url,
data=json.dumps(data)).json()
if resp_json.get('errcode') != 0:
self.log_info(resp_json)
def do_action(self):
# check before start
access_token = dingtalk_get_access_token()
if not access_token:
self.log_error('can not get access_token')
self.dingtalk_message_send_to_conversation_url = DINGTALK_MESSAGE_SEND_TO_CONVERSATION_URL + '?access_token=' + access_token
self.detail_url = get_site_scheme_and_netloc().rstrip('/') + reverse('user_notification_list')
site_name = get_site_name()
# start
now = datetime.now()
today = datetime.now().replace(hour=0).replace(minute=0).replace(
second=0).replace(microsecond=0)
# 1. get all users who are connected dingtalk
socials = SocialAuthUser.objects.filter(provider='dingtalk')
users = [(x.username, x.uid) for x in socials]
self.log_info('Found %d users' % len(users))
if not users:
return
user_uid_map = {}
for username, uid in users:
user_uid_map[username] = dingtalk_get_userid_by_unionid(uid)
# 2. get previous time that command last runs
try:
cmd_last_check = CommandsLastCheck.objects.get(command_type=self.label)
self.log_debug('Last check time is %s' % cmd_last_check.last_check)
last_check_dt = cmd_last_check.last_check
cmd_last_check.last_check = now
cmd_last_check.save()
except CommandsLastCheck.DoesNotExist:
last_check_dt = today
self.log_debug('Create new last check time: %s' % now)
CommandsLastCheck(command_type=self.label, last_check=now).save()
# 3. get all unseen notices for those users
qs = UserNotification.objects.filter(
timestamp__gt=last_check_dt
).filter(seen=False).filter(
to_user__in=list(user_uid_map.keys())
)
self.log_info('Found %d notices' % qs.count())
if qs.count() == 0:
return
user_notices = {}
for q in qs:
if q.to_user not in user_notices:
user_notices[q.to_user] = [q]
else:
user_notices[q.to_user].append(q)
# save current language
cur_language = translation.get_language()
# active zh-cn
translation.activate('zh-cn')
self.log_info('the language is set to zh-cn')
# 4. send msg to users
for username, uid in users:
user_id = user_uid_map[username]
notices = user_notices.get(username, [])
count = len(notices)
if count == 0:
continue
title = ungettext(
"\n"
"You've got 1 new notice on %(site_name)s:\n",
"\n"
"You've got %(num)s new notices on %(site_name)s:\n",
count
) % {'num': count, 'site_name': site_name, }
content = ' \n '.join([remove_html_a_element(x.format_msg()) for x in notices])
self.send_dingtalk_msg(user_id, title, content)
# reset language
translation.activate(cur_language)
self.log_info('reset language success')