IT/etc

[TeamsWebhook] 팀즈 웹훅으로 채널에 메세지 보내기

음료요정 2021. 6. 17. 10:45

 

 

최근 회사 커뮤니케이션 도구로 마이크로소프트의 Teams를 사용하는 기업이 늘고있다.

회사에서 기존에 쓰던 알림메세지 어플리케이션은 Telegram 이었는데

업무적으로 얽힌 사람들에게 url 을 보내 초대하는것도 쉽지않고, 보안의 문제가 될 수 있어

팀즈 채널에 전송하기로 변경 개발하였다.

이에 대한 기록을 남겨 놓으려 한다

 

 

 

 

먼저 팀즈에서의 채널 설정이 필요하다. 


1. 커넥터 생성

팀즈 채널옆에 ... 을 클릭 > 커넥터를 클릭

 

2. 채컬 커넥터의 Incoming Webhook 구성

 

커넥터의 리스트 중 Webhook 구성을 클릭

 

커넥터 Webhook 이름과 이미지를 선택 후 만들기 클릭

3. 만들기가 완료되면 하단에 해당 url이 생성된다 

 

하단 URL을 이용하여 웹훅으로 메세지를 발송하는것, URL를 클릭보드에 복사한다. 그리고 완료 버튼을 클릭!

4. 웹훅이 생성되면 팀즈방에 생성을 알리는 메세지가 게시가 된다.

 

 


테스트 해보기

1. postman을 이용해서 테스트

설정이 되었다면, curl이나 postman으로 채널에 메세지를 쏴보는 테스트가 가능하다.

위에서 발급 받은 URL을 어딘가에 기록해두고, POST메소드로 JSON형태의 데이터를 발송해보자

나는 postman을 이용해서 테스트를 해보았다.

 

URL에 JSON데이터를 전송하면 된다

 

2. curl 로도 전송 테스트가 가능하다. 

 curl -d '{"@context":"https://schema.org/extensions","@type":"MessageCard","themeColor":"0072C6","title":"Hello Teams!","text":"Hell Teams World"}' -H "Content-Type: Application/JSON" -X POST 웹훅URL

 

3. 발송된 결과 

 

발송된 메세지 화면, 간단한 커스텀 옵션 설정이 가능하다

 

 

 


 

코드 설정 

팀즈 채널 설정이 완료 되었으면 시스템에서의 코드 설정이 필요하다. 

나는 컴포넌트로 등록하여 static 메소드로 배치에 오류있을때 익셉션 캐치하여 호출을 했다. 

 

package com.wonders.wms.ext.commons.util;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.springframework.http.*;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import javax.annotation.PostConstruct;
import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.Properties;

@Slf4j
@Component
public class TeamsWebHookApiUtil{

private static ObjectMapper objectMapper = new ObjectMapper();
private static String webhookUrl;
private static String serverType;
// private static List<String> themeColor;
private static final String DEFAULT_THEME_COLOR = "B4FFFB";
private static final String DEFAULT_TITLE = "배치 결과 Alarm";


@PostConstruct
void init() throws NoSuchAlgorithmException, KeyManagementException {

Properties properties = new Properties();
try{
properties.load(TeamsWebHookApiUtil.class.getResourceAsStream("/application.properties"));
webhookUrl = properties.getProperty("teams.webhook.url");
serverType = properties.getProperty("spring.profiles.active");
// TODO: themeColor = Arrays.asList(properties.getProperty("teams.themeColor.list").split(","));

log.debug("webhookUrl :{}/ serverType :{}", webhookUrl, serverType);

}catch (IOException e){
log.error("load Webhook error : {}", e.getMessage());
}
}

@Retryable(value= Exception.class, maxAttempts = 3, backoff = @Backoff(delay = 2000))
public static String requestWebhookCall(String title, String text, String themeColor) {
title = "[" + serverType + "]" + title;
TeamsWebHookMessageVO teamsVo = new TeamsWebHookMessageVO(themeColor, title, text);

try {
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
//factory.setConnectTimeout(5000); // timeout 5초
//factory.setReadTimeout(5000); // timeout 5초

SSLContext context = SSLContext.getInstance("TLSv1.2");
context.init(null, null, null);

HttpClient httpClient = HttpClientBuilder.create()
.setMaxConnTotal(150) // 연결유지 최대 숫자
.setMaxConnPerRoute(50) // 특정 경로당 최대 숫자
.setSslcontext(context) // TLS1.2적용
.build();

factory.setHttpClient(httpClient); // 동기실행에 사용될 httpClient 세팅
RestTemplate restTemplate = new RestTemplate(factory);

HttpHeaders headers = new HttpHeaders();
Charset utf8 = StandardCharsets.UTF_8;
MediaType mediaType = new MediaType("application", "xml", utf8);
headers.setContentType(mediaType);

HttpEntity<String> requestEntity = new HttpEntity<>(objectMapper.writeValueAsString(teamsVo), headers);
ResponseEntity<String> responseEntity = restTemplate.exchange(webhookUrl, HttpMethod.POST, requestEntity, String.class);

log.debug("Teams sendTeamsCall result = {}", responseEntity);
if(responseEntity.getStatusCode() != HttpStatus.OK){
log.error("Send teams message error - requestEntity : [{}]/ responseEntity : [{}]", requestEntity.toString() , responseEntity.toString());
}
return responseEntity.getBody();

}catch (JsonProcessingException | KeyManagementException | NoSuchAlgorithmException e){
e.printStackTrace();
log.error("Error during sending TeamsMessage, {}", e.getMessage());
return null;
}
}

public static String sendTeamsMessage(String text){
return requestWebhookCall(DEFAULT_TITLE, text, DEFAULT_THEME_COLOR);
}

public static String sendTeamsMessage(String title, String text){
return requestWebhookCall(title, text, DEFAULT_THEME_COLOR);
}

//TODO: 알림 성향별 테마 설정
/* public static String sendTeamsMessage(String text, int resultCode){
return requestWebhookCall(DEFAULT_TITLE, text, themeColor.get(resultCode));
} */
}

 

 

 

호출 코드 

@Transactional
public AggregationSettlement createSettlements(Partner partner,
SettlementsCalendar settlementsCalendar) {
String resultMsg = "";
Boolean existYn = settlementService.existAggregationSettlement(partner.getSapPartnerId(), settlementsCalendar);
if (Boolean.TRUE.equals(existYn)) {
resultMsg = String.format("이미 존재하는 요청이 들어왔습니다. 확인 부탁 드립니다. Partner :%s(%s)", partner.getSapPartnerId(), partner.getRegiCorpName());
log.error("{}", resultMsg);
TeamsWebHookApiUtil.sendTeamsMessage("Batch : 집계", resultMsg);
return null;
}

 


커스텀방법

웹훅에 전송하는 JSON 데이터에 potentialAction 설정을 추가하여 커스텀 액션을 추가할 수 있다. 

 

참고 : https://docs.microsoft.com/en-us/outlook/actionable-messages/send-via-connectors?ranMID=24542&ranEAID=je6NUbpObpQ&ranSiteID=je6NUbpObpQ-xf.J1WeohTi3lpLFC9DM1w&epi=je6NUbpObpQ-xf.J1WeohTi3lpLFC9DM1w&irgwc=1&OCID=AID2000142_aff_7593_1243925&tduid=(ir__crhwxsfixokfqis9kk0sohzzzm2xuo1qlihe9l9900)(7593)(1243925)(je6NUbpObpQ-xf.J1WeohTi3lpLFC9DM1w)()&irclickid=_crhwxsfixokfqis9kk0sohzzzm2xuo1qlihe9l9900 

 

Get started with actionable messages via connectors - Outlook Developer

Learn how to create an actionable message card and send it via Office connectors.

docs.microsoft.com