@channel.io/channel-talk-integration-mcp
Version:
Channel Talk Integration MCP - Installation guides for Channel Talk SDK across multiple platforms
425 lines (359 loc) • 13.8 kB
JavaScript
import { z } from "zod";
export function registerGetWebViewGuideTool(server) {
server.tool("get_webview_guide", `case1: 유저가 웹뷰에 채널톡을 설치하는 방법을 알고 싶다고 말하면 이 툴을 호출합니다.
case2: 이전 툴의 next_step에서 get_webview_guide를 호출해야할 경우 이 툴을 호출합니다.`, {
language: z
.enum(["ko", "en"])
.optional()
.describe("Language for the guide. Use 'en' for English, defaults to Korean if not specified"),
}, (args) => {
try {
const language = args.language;
const result = language === "en"
? getEnglishWebViewGuide()
: getKoreanWebViewGuide();
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
}
catch (error) {
const message = error instanceof Error ? error.message : String(error);
throw new Error(`Failed to execute get_webview_guide: ${message}`);
}
});
console.log("Registered tool: get_webview_guide");
}
function getKoreanWebViewGuide() {
return {
result: `# 채널톡 웹뷰 설치 가이드
웹뷰 환경에서 채널톡 JavaScript SDK를 사용하는 방법을 안내합니다. 웹뷰는 네이티브 앱 내에서 웹 컨텐츠를 표시하는 컴포넌트입니다.
## ⚠️ 중요 제한사항
- **JavaScript SDK만 사용 시 푸시 알림 기능 지원 안함**
- 푸시 알림이 필요한 경우 각 플랫폼별 네이티브 SDK 사용 권장
---
## 🤖 안드로이드 웹뷰 설정
${getAndroidWebViewSettings()}
---
## 🍎 iOS 웹뷰 설정
${getIOSWebViewSettings()}
---
## 🔧 JavaScript SDK 설정
${getJavaScriptSDKSettings()}
---
## 📝 구현 체크리스트
${getWebViewChecklist()}
---
## 💡 웹뷰 vs 네이티브 SDK 비교
${getWebViewComparison()}
**📚 참고 문서**: https://developers.channel.io/reference/webview-quickstart-kr`,
};
}
function getEnglishWebViewGuide() {
return {
result: `# Channel Talk WebView Installation Guide
This guide explains how to use the Channel Talk JavaScript SDK in WebView environments. WebView is a component that displays web content within native apps.
## ⚠️ Important Limitations
- **Push notification feature not supported when using JavaScript SDK only**
- Use platform-specific native SDKs if push notifications are required
---
## 🤖 Android WebView Settings
${getAndroidWebViewSettings()}
---
## 🍎 iOS WebView Settings
${getIOSWebViewSettings()}
---
## 🔧 JavaScript SDK Settings
${getJavaScriptSDKSettings()}
---
## 📝 Implementation Checklist
${getWebViewChecklist()}
---
## 💡 WebView vs Native SDK Comparison
${getWebViewComparison()}
**📚 Reference Documentation**: https://developers.channel.io/reference/webview-quickstart`,
};
}
function getAndroidWebViewSettings() {
return `### 1️⃣ 새 탭 허용 설정
안드로이드 웹뷰는 기본적으로 새 탭을 허용하지 않으므로 추가 설정이 필요합니다.
\`\`\`kotlin
webChromeClient = object : WebChromeClient() {
override fun onCreateWindow(
view: WebView?,
isDialog: Boolean,
isUserGesture: Boolean,
resultMsg: Message?
): Boolean {
// 새 창 인터셉트
val transport = resultMsg?.obj as? WebView.WebViewTransport
val newWebView = WebView(this@WebViewActivity)
view?.addView(newWebView)
transport?.webView = newWebView
resultMsg?.sendToTarget()
newWebView.webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(
view: WebView,
url: String
): Boolean {
return when {
url.startsWith("tel:") -> {
val intent = Intent(Intent.ACTION_DIAL, Uri.parse(url))
startActivity(intent)
true
}
url.startsWith("mailto:") -> {
val intent = Intent(Intent.ACTION_SENDTO, Uri.parse(url))
startActivity(intent)
true
}
else -> {
Intent(view.context, WebViewActivity::class.java)
.apply { putExtra("url", url) }
.also { intent -> startActivity(intent) }
true
}
}
}
}
return true
}
}
\`\`\`
### 2️⃣ 파일 업로드 기능 구현
\`\`\`kotlin
// WebView 설정
yourWebViewInstance.apply {
settings.apply {
// 파일 시스템 접근 허용
allowFileAccess = true
}
webChromeClient = object : WebChromeClient() {
override fun onShowFileChooser(
webView: WebView,
filePathCallback: ValueCallback<Array<Uri>>,
fileChooserParams: FileChooserParams
): Boolean {
// WebViewAttachmentModule 사용
val intent = attachmentModule.getIntentChooser(
filePathCallback,
fileChooserParams.acceptTypes
)
startActivityForResult(intent, WEB_VIEW_FILE_UPLOAD_REQUEST_CODE)
return true // 반드시 true 반환
}
}
}
\`\`\`
### 3️⃣ WebViewAttachmentModule 클래스
\`\`\`kotlin
class WebViewAttachmentModule {
private var filePathCallback: ValueCallback<Array<Uri>>? = null
fun getIntentChooser(
filePathCallback: ValueCallback<Array<Uri>>,
acceptTypes: Array<String>
): Intent {
this.filePathCallback?.onReceiveValue(null)
this.filePathCallback = filePathCallback
return Intent(Intent.ACTION_GET_CONTENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "*/*"
putExtra(Intent.EXTRA_MIME_TYPES, getMimeTypesFromAcceptTypes(acceptTypes))
}
}
fun handleResult(requestCode: Int, resultCode: Int, data: Intent?) {
val uri = data?.data
if (requestCode == WEB_VIEW_FILE_UPLOAD_REQUEST_CODE &&
resultCode == AppCompatActivity.RESULT_OK &&
uri != null) {
filePathCallback?.onReceiveValue(arrayOf(uri))
} else {
filePathCallback?.onReceiveValue(null)
}
filePathCallback = null
}
private fun getMimeTypesFromAcceptTypes(acceptTypes: Array<String>): Array<String> {
return (acceptTypes
.mapNotNull { acceptType ->
when {
acceptType.startsWith(".") ->
MimeTypeMap.getSingleton()
.getMimeTypeFromExtension(acceptType.substring(1))
else -> acceptType
}
}
.filter { it.isNotBlank() }
.takeIf { it.isNotEmpty() }
?: listOf("*/*"))
.toTypedArray()
}
}
\`\`\`
### 4️⃣ 파일 다운로드 기능 구현
\`\`\`kotlin
class WebViewDownloadListener(
private val context: Context,
) : DownloadListener {
override fun onDownloadStart(
url: String?,
userAgent: String?,
contentDisposition: String?,
mimetype: String?,
contentLength: Long
) {
val uri = Uri.parse(url)
val fileName = URLUtil.guessFileName(url, contentDisposition, mimetype)
val request = DownloadManager.Request(uri)
val cookie = CookieManager.getInstance().getCookie(url)
request.addRequestHeader("Cookie", cookie)
request.addRequestHeader("User-Agent", userAgent)
request.setTitle(fileName)
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName)
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
val downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as? DownloadManager
downloadManager?.enqueue(request)
}
}
// WebView에 다운로드 리스너 설정
webView.setDownloadListener(WebViewDownloadListener(this))
\`\`\``;
}
function getIOSWebViewSettings() {
return `### 1️⃣ WKWebView 기본 설정
\`\`\`swift
import UIKit
import WebKit
import Photos
class ViewController: UIViewController, WKNavigationDelegate, WKUIDelegate {
var webView: WKWebView!
var url: String = "YOUR_WEB_URL"
override func loadView() {
let config = WKWebViewConfiguration()
// 인라인 미디어 재생 허용
config.allowsInlineMediaPlaybook = true
webView = WKWebView(frame: .zero, configuration: config)
webView.uiDelegate = self
webView.navigationDelegate = self
view = webView
}
override func viewDidLoad() {
super.viewDidLoad()
let myURL = URL(string: self.url)
let myRequest = URLRequest(url: myURL!)
self.webView.load(myRequest)
}
}
\`\`\`
### 2️⃣ 새 탭 처리 (_blank target)
\`\`\`swift
func webView(
_ webView: WKWebView,
createWebViewWith configuration: WKWebViewConfiguration,
for navigationAction: WKNavigationAction,
windowFeatures: WKWindowFeatures
) -> WKWebView? {
// target="_blank" 링크를 현재 웹뷰에서 로드
if navigationAction.targetFrame == nil {
webView.load(navigationAction.request)
}
return nil
}
\`\`\`
### 3️⃣ 파일 다운로드 처리
\`\`\`swift
func webView(
_ webView: WKWebView,
decidePolicyFor navigationResponse: WKNavigationResponse,
decisionHandler: (WKNavigationResponsePolicy) -> Void
) {
guard
let response = navigationResponse.response as? HTTPURLResponse,
let url = response.url,
url.pathComponents.contains(where: { $0 == "pri-file"}),
response.statusCode == 200,
let mimeType = response.mimeType?.split(separator: "/").first,
let contentDisposition = response.allHeaderFields["Content-Disposition"] as? String,
contentDisposition.contains("attachment;")
else {
decisionHandler(.allow)
return
}
let fileExtension = contentDisposition.components(separatedBy: ".").last ?? ""
let fileFormat = FileFormat(rawValue: String(mimeType))
self.downloadFile(from: url, fileExtension: String(fileExtension)) { [weak self] result in
switch result {
case .success(let url):
switch fileFormat {
case .image, .video:
self?.saveToPhotoLibrary(fileURL: url, format: fileFormat) { [weak self] _ in
self?.hideActivityIndicator()
}
case .file:
DispatchQueue.main.async { [weak self] in
let controller = UIActivityViewController(
activityItems: [url],
applicationActivities: nil
)
self?.present(controller, animated: true)
}
}
case .failure(let error):
print("파일 다운로드 실패: \\(error.localizedDescription)")
}
if #available(iOS 14.5, *) {
decisionHandler(.download)
} else {
decisionHandler(.allow)
}
}
}
\`\`\``;
}
function getJavaScriptSDKSettings() {
return `웹뷰에서 로드되는 HTML 페이지에 채널톡 SDK를 추가합니다:
\`\`\`html
<!-- 채널톡 SDK 로드 -->
<script>
(function(){var w=window;if(w.ChannelIO){return w.console.error("ChannelIO script included twice.");}var ch=function(){ch.c(arguments);};ch.q=[];ch.c=function(args){ch.q.push(args);};w.ChannelIO=ch;function l(){if(w.ChannelIOInitialized){return;}w.ChannelIOInitialized=true;var s=document.createElement("script");s.type="text/javascript";s.async=true;s.src="https://cdn.channel.io/plugin/ch-plugin-web.js";var x=document.getElementsByTagName("script")[0];if(x.parentNode){x.parentNode.insertBefore(s,x);}}if(document.readyState==="complete"){l();}else{w.addEventListener("DOMContentLoaded",l);w.addEventListener("load",l);}})();
</script>
<!-- 채널톡 초기화 -->
<script>
ChannelIO('boot', {
"pluginKey": "YOUR_PLUGIN_KEY" // 실제 플러그인 키로 교체
});
</script>
\`\`\``;
}
function getWebViewChecklist() {
return `### 안드로이드
- [ ] WebChromeClient 새 탭 처리 설정
- [ ] 파일 업로드를 위한 allowFileAccess 활성화
- [ ] WebViewAttachmentModule 구현
- [ ] 파일 다운로드 리스너 설정
- [ ] 권한 설정 (WRITE_EXTERNAL_STORAGE 등)
### iOS
- [ ] allowsInlineMediaPlayback 설정
- [ ] target="_blank" 처리 구현
- [ ] 파일 다운로드 로직 구현
- [ ] 사진 라이브러리 접근 권한 설정
- [ ] Info.plist 권한 설정
### 공통
- [ ] 플러그인 키 설정
- [ ] JavaScript SDK 로드 스크립트 추가
- [ ] 채널톡 부트 코드 구현
- [ ] 테스트 및 검증`;
}
function getWebViewComparison() {
return `| 기능 | 웹뷰 (JavaScript) | 네이티브 SDK |
|------|------------------|-------------|
| 푸시 알림 | ❌ 지원 안함 | ✅ 완전 지원 |
| 구현 복잡도 | 🔶 중간 | 🟢 간단 |
| 파일 처리 | 🔶 별도 구현 필요 | 🟢 자동 처리 |
| 성능 | 🔶 웹뷰 의존 | 🟢 네이티브 성능 |
**권장사항**: 푸시 알림이 중요한 기능이라면 네이티브 SDK 사용을 강력히 권장합니다.`;
}
//# sourceMappingURL=get-webview-guide.tool.js.map