UNPKG

@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
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: @escaping (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