チャネルトークをNuxt3のECサイトへ導入した話

## はじめに

チャネルトークとは　

Customer satisfaction on G2で4.8点の評価を受けた
All in oneチャットソリューション
実店舗のような接客体験を売りにしているチャットサービス
とのこと

## 導入方法

今回はSDKを使用して導入
導入ガイド: https://channel.io/ja/guides

Plugin Keyの取得
下記のガイドの自社制作サイトに従ってPlugin Keyを取得

https://channel.io/ja/guides/installation/plug-in

## 実装
下記の開発者向けドキュメントを参照して実装
https://developers.channel.io/docs/web-quickstart

### 1.型ファイルの作成
Service for TypeScriptを参照に型ファイルを作成
```typeScript:types/channel_talk.ts
export interface IChannelIO {
  c?: (...args: any) => void
  q?: [methodName: string, ...args: any[]][]
  (...args: any): void
}

export interface Profile {
  [key: string]: string | number | boolean | null | undefined
}

export interface CallbackUser {
  alert: number
  avatarUrl: string
  id: string
  language: string
  memberId: string
  name?: string
  profile?: Profile | null
  tags?: string[] | null
  unsubscribeEmail: boolean
  unsubscribeTexting: boolean
}

export interface BootOption {
  appearance?: string
  customLauncherSelector?: string
  hideChannelButtonOnBoot?: boolean
  hidePopup?: boolean
  language?: string
  memberHash?: string
  memberId?: string
  pluginKey: string
  profile?: Profile
  trackDefaultEvent?: boolean
  trackUtmSource?: boolean
  unsubscribe?: boolean
  unsubscribeEmail?: boolean
  unsubscribeTexting?: boolean
  zIndex?: number
}

export interface Callback {
  (error: Error | null, user: CallbackUser | null): void
}

export interface UpdateUserInfo {
  language?: string
  profile?: Profile | null
  profileOnce?: Profile
  tags?: string[] | null
  unsubscribeEmail?: boolean
  unsubscribeTexting?: boolean
}

export interface FollowUpProfile {
  name?: string | null
  mobileNumber?: string | null
  email?: string | null
}

export interface EventProperty {
  [key: string]: string | number | boolean | null | undefined
}

export type Appearance = "light" | "dark" | "system" | null

```

### 2.windowに型を追加
IChannelIOは先ほどの型ファイルから読み込みます
```typeScript:types/window.ts
import { IChannelIO } from "~/types/channel_talk"
export declare global {
  interface Window {
    ChannelIO?: IChannelIO
  }
}

```

### 3.処理部分を作成
```typeScript:layout/default.vue
import {
  IChannelIO,
  BootOption,
  UpdateUserInfo,
  Callback,
  EventProperty,
  FollowUpProfile,
  Appearance,
} from "~/types/channel_talk"

export class ChannelService {
  constructor() {
    this.loadScript()
  }

  loadScript() {
    ;(function () {
      const w = window
      if (w.ChannelIO) return
      const ch: IChannelIO = function () {
        ch.c?.(arguments)
      }
      ch.q = []
      ch.c = function (args) {
        ch.q?.push(args)
      }
      w.ChannelIO = ch
      function l() {
        w.ChannelIOInitialized = true
        const s = document.createElement("script")
        s.type = "text/javascript"
        s.async = true
        s.src = "https://cdn.channel.io/plugin/ch-plugin-web.js"
        const 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)
      }
    })()
  }

  boot(option: BootOption, callback?: Callback) {
    window.ChannelIO?.("boot", option, callback)
  }

  shutdown() {
    window.ChannelIO?.("shutdown")
  }

  showMessenger() {
    window.ChannelIO?.("showMessenger")
  }

  hideMessenger() {
    window.ChannelIO?.("hideMessenger")
  }

  openChat(chatId?: string | number, message?: string) {
    window.ChannelIO?.("openChat", chatId, message)
  }

  track(eventName: string, eventProperty?: EventProperty) {
    window.ChannelIO?.("track", eventName, eventProperty)
  }

  onShowMessenger(callback: () => void) {
    window.ChannelIO?.("onShowMessenger", callback)
  }

  onHideMessenger(callback: () => void) {
    window.ChannelIO?.("onHideMessenger", callback)
  }

  onBadgeChanged(callback: (unread: number, alert: number) => void) {
    window.ChannelIO?.("onBadgeChanged", callback)
  }

  onChatCreated(callback: () => void) {
    window.ChannelIO?.("onChatCreated", callback)
  }

  onFollowUpChanged(callback: (profile: FollowUpProfile) => void) {
    window.ChannelIO?.("onFollowUpChanged", callback)
  }

  onUrlClicked(callback: (url: string) => void) {
    window.ChannelIO?.("onUrlClicked", callback)
  }

  clearCallbacks() {
    window.ChannelIO?.("clearCallbacks")
  }

  updateUser(userInfo: UpdateUserInfo, callback?: Callback) {
    window.ChannelIO?.("updateUser", userInfo, callback)
  }

  addTags(tags: string[], callback?: Callback) {
    window.ChannelIO?.("addTags", tags, callback)
  }

  removeTags(tags: string[], callback?: Callback) {
    window.ChannelIO?.("removeTags", tags, callback)
  }

  setPage(page: string) {
    window.ChannelIO?.("setPage", page)
  }

  resetPage() {
    window.ChannelIO?.("resetPage")
  }

  showChannelButton() {
    window.ChannelIO?.("showChannelButton")
  }

  hideChannelButton() {
    window.ChannelIO?.("hideChannelButton")
  }

  setAppearance(appearance: Appearance) {
    window.ChannelIO?.("setAppearance", appearance)
  }
}


```

### 4.C側に表示
```typeScript:layout/default.vue
import { ChannelService, ignoreChannelTalkPath } from "~/utils/channel_talk"

const channelTalkViewEvent = () => {
  const channel = new ChannelService()
  const pluginKey = プラグインキー
  if (window.ChannelIOInitialized) {
    channel.boot({
      pluginKey,
    })
  }
}
```

ボタンが配置されていたら実装完了！
![スクリーンショット 2023-12-14 13.19.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3626564/8f089b5a-09e5-8c2c-c8c8-0c31a1fd1443.png)

## まとめ
ガイドやドキュメントがシンプルで理解しやすく導入の難易度は低め

機能も充実しており、URLのブラックリストを作成して一部ページでは非表示にすることも可能などカスタマイズも幅広く行える

無料で扱える部分も多いので皆様是非使用してみてください