DingTalk.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. <?php
  2. class DingTalk
  3. {
  4. private $host = 'https://oapi.dingtalk.com';
  5. private $appkey = '';
  6. private $appsecret = '';
  7. private $access_token = '';
  8. /**
  9. * DingTalk constructor.
  10. * @param $conf
  11. * @throws \Exception
  12. */
  13. public function __construct($conf)
  14. {
  15. if (!$conf || !isset($conf['appkey']) || !isset($conf['appsecret'])) {
  16. throw new \Exception('dingding config is missed.');
  17. }
  18. $this->appkey = $conf['appkey'];
  19. $this->appsecret = $conf['appsecret'];
  20. $this->gettoken();
  21. }
  22. /**
  23. * 获取access_token
  24. * 正常情况下access_token有效期为7200秒,有效期内重复获取返回相同结果,并自动续期。
  25. * @author yjc@52fhy.com
  26. * @date 2018/9/10
  27. */
  28. private function gettoken()
  29. {
  30. $uri = "/gettoken?appkey={$this->appkey}&appsecret={$this->appsecret}";
  31. $res = $this->request($uri);
  32. $this->access_token = $res['access_token'] ?? '';
  33. }
  34. /**
  35. * 获取通讯录权限范围
  36. * 该接口返回的auth_org_scopes的authed_dept字段是企业授权的部门id列表,通过这个列表可以获取用户列表
  37. * @author yjc@52fhy.com
  38. * @date 2018/9/10
  39. * @return array
  40. */
  41. public function getAuthScopes()
  42. {
  43. $uri = "/auth/scopes?access_token={$this->access_token}";
  44. $res = $this->request($uri);
  45. return $res;
  46. }
  47. /**
  48. * 获取所有部门列表(需要授权整个公司的权限,而不是按组授权)
  49. * 返回的department字段是数组
  50. * @example {"errmsg":"ok","errcode":0,"department":[{"id":5678120,"createDeptGroup":true,"name":"行政人事部","parentid":1,"autoAddUser":true}]}
  51. *
  52. * @author yjc@52fhy.com
  53. * @date 2018/9/11
  54. * @param int $id
  55. * @return array
  56. */
  57. public function getDepartmentList($id = 1)
  58. {
  59. $uri = "/department/list?access_token={$this->access_token}&id={$id}";
  60. $res = $this->request($uri);
  61. return $res;
  62. }
  63. /**
  64. * 获取部门详情
  65. * @see https://open-doc.dingtalk.com/microapp/serverapi2/dubakq#a-names1a%E8%8E%B7%E5%8F%96%E9%83%A8%E9%97%A8%E5%88%97%E8%A1%A8
  66. * @author yjc@52fhy.com
  67. * @date 2018/11/22
  68. * @param int $id
  69. * @return array
  70. */
  71. public function getDepartInfo($id = 1)
  72. {
  73. $uri = "/department/get?access_token={$this->access_token}&id={$id}";
  74. $res = $this->request($uri);
  75. return $res;
  76. }
  77. /**
  78. * 获取部门用户列表(详情)
  79. * @author yjc@52fhy.com
  80. * @date 2018/9/10
  81. * @param int $id
  82. * @return array
  83. */
  84. public function getUserList($department_id)
  85. {
  86. $uri = "/user/list?access_token={$this->access_token}&department_id={$department_id}";
  87. $res = $this->request($uri);
  88. return $res;
  89. }
  90. /**
  91. * @param $code
  92. * @return array
  93. * 根据code获取用户数据
  94. */
  95. public function getUserByCode($code){
  96. $uri = "/user/getuserinfo?access_token={$this->access_token}&code={$code}";
  97. $res = $this->request($uri);
  98. return $res;
  99. }
  100. /**
  101. * 获取用户信息详情
  102. * @author yjc@52fhy.com
  103. * @date 2018/9/13
  104. * @param $userid
  105. * @return array
  106. */
  107. public function getUser($userid)
  108. {
  109. $uri = "/user/get?access_token={$this->access_token}&userid={$userid}";
  110. $res = $this->request($uri);
  111. return $res;
  112. }
  113. /**
  114. * @author yjc@52fhy.com
  115. * @date 2018/9/11
  116. * @param string $name 群名称,长度限制为1~20个字符
  117. * @param string $owner 群主userId,员工唯一标识ID;必须为该会话useridlist的成员之一
  118. * @param array $useridlist 群成员列表,每次最多支持40人,群人数上限为1000
  119. * @param int $showHistoryType 新成员是否可查看聊天历史消息(新成员入群是否可查看最近100条聊天记录),0代表否,1代表是,不传默认为否
  120. * @return array {"chatid":"chat304e7f19961a7fcad9bd336ad5285092","openConversationId":"cidy9pO96ou5kAfGPprQuhFMw==","conversationTag":2,"errmsg":"ok","errcode":0}
  121. */
  122. public function chatCreate(string $name, string $owner, array $useridlist, int $showHistoryType = 0)
  123. {
  124. $uri = "/chat/create?access_token={$this->access_token}";
  125. $res = $this->request($uri, [
  126. 'name' => $name,
  127. 'owner' => $owner,
  128. 'useridlist' => $useridlist,
  129. 'showHistoryType' => $showHistoryType,
  130. ], [], 'POST');
  131. return $res;
  132. }
  133. /**
  134. * @author yjc@52fhy.com
  135. * @date 2018/9/11
  136. * @param string $chatid 群会话的id
  137. * @return array {"chat_info":{"useridlist":["0565176120300975","05524434011110721"],"name":"测试群呀","owner":"0565176120300975","chatid":"chat10d3b21f02c3c94abd1d0ad91330e8d9","conversationTag":2},"errmsg":"ok","errcode":0}
  138. */
  139. public function chatGet(string $chatid)
  140. {
  141. $uri = "/chat/get?access_token={$this->access_token}&chatid={$chatid}";
  142. $res = $this->request($uri);
  143. return $res;
  144. }
  145. /**
  146. * 发送群消息
  147. * @author yjc@52fhy.com
  148. * @date 2018/9/11
  149. * @param string $chatid
  150. * @param string $msgtype
  151. * @param array $ext
  152. * @return array
  153. * @see https://open-doc.dingtalk.com/microapp/serverapi2/isu6nk
  154. */
  155. public function chatSend(string $chatid, array $msg)
  156. {
  157. $uri = "/chat/send?access_token={$this->access_token}";
  158. $params = [
  159. 'chatid' => $chatid,
  160. 'msg' => $msg,
  161. ];
  162. $res = $this->request($uri, $params, [], 'POST');
  163. return $res;
  164. }
  165. /**
  166. * 发送文本消息
  167. * @author yjc@52fhy.com
  168. * @date 2018/9/11
  169. * @param string $chatid
  170. * @param string $content
  171. * @return array {"errcode":0,"errmsg":"ok","messageId":"abcd"}
  172. */
  173. public function chatSendText(string $chatid, string $content)
  174. {
  175. return $this->chatSend($chatid, self::makeTextMsg($content));
  176. }
  177. /**
  178. * 发送Markdown消息
  179. * @author yjc@52fhy.com
  180. * @date 2018/9/12
  181. * @param string $chatid
  182. * @param string $title
  183. * @param string $text
  184. * @return array
  185. */
  186. public function chatSendMarkdown(string $chatid, string $title, string $text)
  187. {
  188. return $this->chatSend($chatid, self::makeMarkdownMsg($title, $text));
  189. }
  190. /**
  191. * 发送机器人消息
  192. * @author yjc@52fhy.com
  193. * @date 2018/9/11
  194. * @param string $access_token 添加机器人得到的webhook的access_token
  195. * @param string $content
  196. * @return array {"errmsg":"ok","errcode":0}
  197. */
  198. public function robotSend(string $access_token, string $content)
  199. {
  200. $uri = "/robot/send?access_token={$access_token}";
  201. $params = [
  202. 'msgtype' => 'text',
  203. 'text' => [
  204. 'content' => $content
  205. ],
  206. ];
  207. $res = $this->request($uri, $params, [], 'POST');
  208. return $res;
  209. }
  210. /**
  211. * 发送应用内工作通知消息
  212. * @author yjc@52fhy.com
  213. * @date 2018/9/12
  214. * @param int $agent_id 企业开发者可在应用设置页面获取
  215. * @param string $userid_list 接收者的用户userid列表,多个英文逗号隔开。最大列表长度:20。
  216. * @param array $msg 通用消息内容
  217. * @return array {"errcode":0,"errmsg":"ok","task_id":123}
  218. */
  219. public function sendAppNotice(int $agent_id, string $userid_list, array $msg)
  220. {
  221. $uri = "/topapi/message/corpconversation/asyncsend_v2?access_token={$this->access_token}";
  222. $params = [
  223. 'agent_id' => $agent_id,
  224. 'userid_list' => $userid_list,
  225. 'to_all_user' => false,
  226. 'msg' => $msg
  227. ];
  228. $res = $this->request($uri, $params, [], 'POST');
  229. return $res;
  230. }
  231. /**
  232. * 发送文本通知消息
  233. * @author yjc@52fhy.com
  234. * @date 2018/9/12
  235. * @param int $agent_id
  236. * @param string $userid_list
  237. * @param string $content
  238. * @return array
  239. */
  240. public function sendAppNoticeText(int $agent_id, string $userid_list, string $content)
  241. {
  242. return $this->sendAppNotice($agent_id, $userid_list, self::makeTextMsg($content));
  243. }
  244. /**
  245. * text消息
  246. * @see https://open-doc.dingtalk.com/microapp/serverapi2/ye8tup
  247. * @author yjc@52fhy.com
  248. * @date 2018/9/12
  249. * @param $content
  250. * @return array
  251. */
  252. public static function makeTextMsg($content): array
  253. {
  254. return [
  255. 'msgtype' => 'text',
  256. 'text' => [
  257. 'content' => $content
  258. ]];
  259. }
  260. /**
  261. * @author yjc@52fhy.com
  262. * @date 2018/9/12
  263. * @param string $media_id
  264. * @return array 图片媒体文件id,可以调用上传媒体文件接口获取。建议宽600像素 x 400像素,宽高比3 : 2
  265. */
  266. public static function makeImageMsg($media_id): array
  267. {
  268. return [
  269. 'msgtype' => 'image',
  270. 'image' => [
  271. 'media_id' => $media_id
  272. ]];
  273. }
  274. /**
  275. * voice消息
  276. * @author yjc@52fhy.com
  277. * @date 2018/9/12
  278. * @param string $media_id 语音媒体文件id,可以调用上传媒体文件接口获取。2MB,播放长度不超过60s,AMR格式
  279. * @param int $duration 正整数,小于60,表示音频时长
  280. * @return array
  281. */
  282. public static function makeVoiceMsg($media_id, int $duration): array
  283. {
  284. return [
  285. 'msgtype' => 'voice',
  286. 'voice' => [
  287. 'media_id' => $media_id,
  288. 'duration' => $media_id,
  289. ]];
  290. }
  291. /**
  292. * link消息
  293. * @author yjc@52fhy.com
  294. * @date 2018/9/12
  295. * @param string $messageUrl 消息点击链接地址
  296. * @param string $picUrl 图片媒体文件id,可以调用上传媒体文件接口获取
  297. * @param string $title 消息标题
  298. * @param string $text 消息描述
  299. * @return array
  300. */
  301. public static function makeLinkMsg(string $messageUrl, string $picUrl, string $title, string $text): array
  302. {
  303. return [
  304. 'msgtype' => 'link',
  305. 'link' => [
  306. 'messageUrl' => $messageUrl,
  307. 'picUrl' => $picUrl,
  308. 'title' => $title,
  309. 'text' => $text,
  310. ]];
  311. }
  312. /**
  313. * markdown消息
  314. * @author yjc@52fhy.com
  315. * @date 2018/9/12
  316. * @param string $title 首屏会话透出的展示内容
  317. * @param string $text markdown格式的消息
  318. * @return array
  319. */
  320. public static function makeMarkdownMsg(string $title, string $text): array
  321. {
  322. return [
  323. 'msgtype' => 'markdown',
  324. 'markdown' => [
  325. 'title' => $title,
  326. 'text' => $text,
  327. ]];
  328. }
  329. /**
  330. * 注册业务事件回调接口
  331. *
  332. * 在您注册事件回调接口的时候,钉钉服务器会向您“注册回调接口”时候上传的url(接收回调的url)推送一条消息,用来测试url的合法性。
  333. * 收到消息后,需要返回经过加密后的字符串“success”的json数据,否则钉钉服务器将认为url不合法。
  334. *
  335. * @author yjc@52fhy.com
  336. * @date 2018/9/12
  337. * @param array $call_back_tag 需要监听的事件类型,例如["user_add_org", "user_modify_org", "user_leave_org"]
  338. * @param string $token 加解密需要用到的token,普通企业可以随机填写
  339. * @param string $aes_key 数据加密密钥。用于回调数据的加密,长度固定为43个字符,从a-z, A-Z, 0-9共62个字符中选取,您可以随机生成
  340. * @param string $url 接收事件回调的url
  341. * @return array {"errmsg":"ok","errcode":0}
  342. */
  343. public function registerCallBack(array $call_back_tag, string $token, string $aes_key, string $url)
  344. {
  345. $uri = "/call_back/register_call_back?access_token={$this->access_token}";
  346. $params = [
  347. 'call_back_tag' => $call_back_tag,
  348. 'token' => $token,
  349. 'aes_key' => $aes_key,
  350. 'url' => $url,
  351. ];
  352. $res = $this->request($uri, $params, [], 'POST');
  353. return $res;
  354. }
  355. /**
  356. * 查询事件回调接口
  357. * @author yjc@52fhy.com
  358. * @date 2018/9/12
  359. * @return array {"errcode":0,"errmsg":"ok","call_back_tag":["user_add_org","user_modify_org","user_leave_org"],"token":"123456","aes_key":"","url":"www.dingtalk.com"}
  360. */
  361. public function getCallBack()
  362. {
  363. $uri = "/call_back/get_call_back?access_token={$this->access_token}";
  364. $res = $this->request($uri);
  365. return $res;
  366. }
  367. /**
  368. * 更新事件回调接口
  369. * @author yjc@52fhy.com
  370. * @date 2018/9/12
  371. * @param array $call_back_tag
  372. * @param string $token
  373. * @param string $aes_key
  374. * @param string $url
  375. * @return array {"errmsg":"ok","errcode":0}
  376. */
  377. public function updateCallBack(array $call_back_tag, string $token, string $aes_key, string $url)
  378. {
  379. $uri = "/call_back/update_call_back?access_token={$this->access_token}";
  380. $params = [
  381. 'call_back_tag' => $call_back_tag,
  382. 'token' => $token,
  383. 'aes_key' => $aes_key,
  384. 'url' => $url,
  385. ];
  386. $res = $this->request($uri, $params, [], 'POST');
  387. return $res;
  388. }
  389. /**
  390. * 删除事件回调接口
  391. * @author yjc@52fhy.com
  392. * @date 2018/9/12
  393. * @return array {"errmsg":"ok","errcode":0}
  394. */
  395. public function deleteCallBack()
  396. {
  397. $uri = "/call_back/delete_call_back?access_token={$this->access_token}";
  398. $res = $this->request($uri);
  399. return $res;
  400. }
  401. /**
  402. * 获取回调失败的结果
  403. *
  404. * 钉钉服务器给回调接口推送时,有可能因为各种原因推送失败(比如网络异常),此时钉钉将保留此次变更事件。用户可以通过此回调接口获取推送失败的变更事件。
  405. *
  406. * @author yjc@52fhy.com
  407. * @date 2018/9/12
  408. * @return array {"errcode":0,"errmsg":"ok","has_more":false,"failed_list":[{"event_time":32112412,"call_back_tag":"user_add_org","userid":["",""],"corpid":""}]}
  409. */
  410. public function getCallBackFailedResult()
  411. {
  412. $uri = "/call_back/get_call_back_failed_result?access_token={$this->access_token}";
  413. $res = $this->request($uri);
  414. return $res;
  415. }
  416. /**
  417. * CURL request
  418. * @return array {"errmsg":"ok","errcode":0}
  419. */
  420. private function request($uri, $data = [], $header = [], $type = 'GET')
  421. {
  422. $url = $this->host . $uri;
  423. $ssl = stripos($url, 'https://') === 0 ? true : false;
  424. //缺省header
  425. $header[] = "Content-Type: application/json";
  426. $header[] = "Content-Encoding: gzip";
  427. $curl = curl_init(); // 启动一个CURL会话
  428. curl_setopt($curl, CURLOPT_URL, $url); // 要访问的地址
  429. if ($ssl) {
  430. curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0); // 对认证证书来源的检查
  431. curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0); // 从证书中检查SSL加密算法是否存在
  432. }
  433. curl_setopt($curl, CURLOPT_HEADER, 0); //启用时会将头文件的信息作为数据流输出
  434. if (!empty ($data)) {
  435. $options = json_encode($data);
  436. curl_setopt($curl, CURLOPT_POSTFIELDS, $options); // Post提交的数据包
  437. }
  438. curl_setopt($curl, CURLOPT_TIMEOUT, 5); // 设置超时
  439. if (!empty($header)) {
  440. curl_setopt($curl, CURLOPT_HTTPHEADER, $header); // 设置HTTP头
  441. }
  442. curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); // 获取的信息以文件流的形式返回
  443. curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $type);
  444. $result = curl_exec($curl); // 执行操作
  445. curl_close($curl); // 关闭CURL会话
  446. $res = json_decode($result, true) ?: [];
  447. if (!$res) {
  448. return [
  449. 'errcode' => __LINE__,
  450. 'errmsg' => 'request dingding api no response',
  451. ];
  452. }
  453. return $res;
  454. }
  455. }