GAS의 Language App으로 슬랙의 정보를 번역하고 답장하는 Bot을 만들었습니다.
슬랙으로 쉽게 번역하고 싶어요.
당사는 해외 고객과 슬랙을 통해 대화를 진행합니다.
역외지의 브리지 SE는 모두 우수한 사람들이기 때문에 일본어로 연락해도 문제가 없지만, 미세한 어감을 전달하지 못해 실제 일하는 엔지니어가 영어를 더 잘한다.
그래서 슬랙에 영어를 끼고 대화하는 경우도 있다.
또한 주로 GCP를 사용하는 경우가 많기 때문에 GCP의 서비스 운행 상황을 통지하지만 서비스 정지와 지연이 발생할 때의 통지는 기본적으로 영어로 발송된다.
서비스 이름은 좌우로 읽을 수 있고, 최악은 구글 번역과 DeepL 흐름이면 좋겠지만, 시원하게 번역하고 싶어요.
조사 결과 이 분은 자신이 하고 싶은 일을 이룬 것으로 밝혀졌다.
다만, 이것을 가져올 때 몇 가지 문제가 생겨서 일부 코드를 수정했다.
이번에는 그 코드를 공개하기 위해 기사를 썼어요.
(주로 Slack 사양 변경에 대응하는 형태가 추가됨)
동작 개요
다음 동작을 만드는bot.
동작 이미지
소스 코드
소스 코드는 다음과 같습니다.
소스 코드
script.gs
var TOKEN = PropertiesService.getScriptProperties().getProperty("TOKEN");
const flag_map = {
"cn": { translateTo: "zh", languagePrefix: ":cn:" }, // 中国:中国語
"flag-cn": { translateTo: "zh", languagePrefix: ":cn:" }, // 中国:中国語
"de": { translateTo: "de", languagePrefix: ":de:" }, // ドイツ:ドイツ語
"flag-de": { translateTo: "de", languagePrefix: ":de:" }, // ドイツ:ドイツ語
"es": { translateTo: "es", languagePrefix: ":es:" }, // スペイン:スペイン語
"flag-es": { translateTo: "es", languagePrefix: ":es:" }, // スペイン:スペイン語
"fr": { translateTo: "fr", languagePrefix: ":fr:" }, // フランス:フランス語
"flag-fr": { translateTo: "fr", languagePrefix: ":fr:" }, // フランス:フランス語
"gb": { translateTo: "en", languagePrefix: ":gb:" }, // イギリス:英語
"flag-gb": { translateTo: "en", languagePrefix: ":gb:" }, // イギリス:英語
"it": { translateTo: "it", languagePrefix: ":it:" }, // イタリア:イタリア語
"flag-it": { translateTo: "it", languagePrefix: ":it:" }, // イタリア:イタリア語
"jp": { translateTo: "ja", languagePrefix: ":jp:" }, // 日本:日本語
"flag-jp": { translateTo: "ja", languagePrefix: ":jp:" }, // 日本:日本語
"kr": { translateTo: "ko", languagePrefix: ":kr:" }, // 韓国:韓国語
"flag-kr": { translateTo: "ko", languagePrefix: ":kr:" }, // 韓国:韓国語
"ru": { translateTo: "ru", languagePrefix: ":ru:" }, // ロシア:ロシア語
"flag-ru": { translateTo: "ru", languagePrefix: ":ru:" }, // ロシア:ロシア語
"us": { translateTo: "en", languagePrefix: ":us:" }, // ロシア:ロシア語
"flag-us": { translateTo: "en", languagePrefix: ":us:" }, // ロシア:ロシア語
"flag-mm": { translateTo: "my", languagePrefix: ":flag-mm:" }, // ミャンマー:ミャンマー語
}
function doPost(e) {
try {
var json = JSON.parse(e.postData.getDataAsString());
if (json.type == "url_verification") {
return ContentService.createTextOutput(json.challenge);
}
// https://api.slack.com/events/reaction_added
// scope: "reactions:read"
if (json.type == "event_callback" && json.event.type == "reaction_added") {
return ContentService.createTextOutput(onReactionAdded(json.event));
}
} catch (ex) {
console.log(ex);
}
}
// https://api.slack.com/methods/chat.postMessage
// scope: "chat:write:user" or "chat:write:bot"
function postThreadMessage(channel, ts, text) {
if (channel && ts && text) {
var headers = {
"Content-Type": "application/json",
"Authorization": "Bearer " + TOKEN
};
var payload = {
"token": TOKEN,
"channel": channel,
"thread_ts": ts,
"text": text
}
var options = {
"headers": headers,
"method": "post",
"payload": JSON.stringify(payload)
};
try {
var response = UrlFetchApp.fetch("https://slack.com/api/chat.postMessage", options);
console.log(JSON.parse(response.getContentText()));
} catch (ex) {
console.log(ex);
}
}
}
// https://api.slack.com/methods/conversations.replies
// "channels:history" or "groups:history"
function getMessages(channel, ts) {
var headers = {
"Authorization": "Bearer " + TOKEN
};
var options = {
"headers": headers
};
var response = UrlFetchApp.fetch("https://slack.com/api/conversations.replies?channel=" + channel + "&ts=" + ts, options);
var json = JSON.parse(response.getContentText());
return json.messages;
}
function isTranslated(messages, lang) {
for (var i in messages) {
var message = messages[i];
if (message.text.substring(0, lang.length) == lang) {
return true;
}
}
return false;
}
function onReactionAdded(json) {
var channel = json.item.channel;
var type = json.item.type;
var ts = json.item.ts;
var reaction = json.reaction;
if (type == "message") {
var messages = getMessages(channel, ts);
if (messages) {
if (messages[0].thread_ts) {
ts = messages[0].thread_ts;
}
var message = messages[0].text;
if (flag_map[reaction] && !isTranslated(messages, flag_map[reaction].languagePrefix)) {
var translatedMessage = flag_map[reaction].languagePrefix + " " + LanguageApp.translate(message, "", flag_map[reaction].translateTo);
postThreadMessage(channel, ts, translatedMessage);
}
}
}
return "OK";
}
원래 Qita의 글에서 포스트 메시지의 방법이 변경되었습니다(API 호출이 바뀌었습니까?)또는 대상으로부터 언어를 얻거나 원래 보도된 말의 답장 번역이 이상해지기 때문에 답장에 대한 반응도 변경된다.bot 제작 절차
다음 순서에 따라 제작한다.
・channeels:history
・chat:write
・reactions:read
그런 다음 Slack 애플리케이션 이름과 아이콘 등을 적절하게 변경하십시오.
시행착오를 비교한 부분도 있어 기술 누락이 있을 수 있다.
만약 순조롭지 못한 곳이 있으면 평론에서 저에게 알려주세요.
못한 일
안전 고려
안전성이 미묘하다.
누구나 할 수 있는 상태가 됐다.
'Verification Token'이러면 대응하는 게 좋을 것 같아요.
3초 규칙의 대응
슬랙이 3초 안에 API 호출에 응답하는 규칙이 있지만 지원되지 않습니다.
기존 기사에서는 같은 메시지가 전송되지 않도록 검사를 통해 대응했다.
사실 이쪽 기사를 사용해야 하고 번역 처리는 별도로 해야 한다.
그렇긴 하지만 지금이라도 그렇게 늦은 것은 아니기 때문에 문제가 발생하지 않았다.
끝맺다
슬랙으로 번역된 Bot을 만드는 방법입니다.
Reference
이 문제에 관하여(GAS의 Language App으로 슬랙의 정보를 번역하고 답장하는 Bot을 만들었습니다.), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://zenn.dev/hal1986/articles/20211014-slack-translation-bot텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)