Twilio-Conversation으로 실시간 채팅 앱을 만드는 방법은 무엇입니까?

May 03 2023
JavaScript SDK를 사용하여 확장 가능하고 안정적인 채팅 애플리케이션을 구축하는 Twilio 채팅의 기능을 살펴보겠습니다. 이 문서는 각도에서 Twilio의 대화 API와 채팅 앱을 통합하는 데 도움이 되지만 그게 전부는 아닙니다. 여기에서 언급한 더 많은 사용 사례 또는 예를 찾을 수 있습니다.

JavaScript SDK를 사용하여 확장 가능하고 안정적인 채팅 애플리케이션을 구축하는 Twilio 채팅의 기능을 살펴보겠습니다.

이 문서는 각도 에서 Twilio의 대화 API와 채팅 앱을 통합하는 데 도움이 되지만 그게 전부는 아닙니다. 여기 에서 언급한 더 많은 사용 사례 또는 예를 찾을 수 있습니다 .

이제 동일한 내용을 시연하기 위해 예제 채팅 앱을 시작하고 만들어 보겠습니다.

Angular와 Twilio-Conversation 통합

전제 조건

  • 노드 J
  • 패키지 관리자: npm
  • 동일한 계정을 사용하여 시작하려면 하나의 무료 Twilio 계정을 만드십시오.
  • 각도 CLI

백엔드 코드를 먼저 설정합시다

아래 언급된 명령어를 실행하여 새로운 NodeJS 프로젝트인 'chat-backend'를 생성합니다.

mkdir chat-backend
cd chat-backend
npm init

npm i express
npm i dotenv
npm i twilio

TWILIO_ACCOUNT_SID=
TWILIO_AUTH_TOKEN=
TWILIO_SERVICE_SID=
TWILIO_API_KEY=
TWILIO_API_SECRET=

index.js

require('dotenv').config();
const express = require("express");
var app = express();
const chat = require('./chat');

app.use(function(req, res, next) {
    res.setHeader('Access-Control-Allow-Origin', '*');
    res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
    res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
    res.setHeader('Access-Control-Allow-Credentials', true);
    next();
});

app.get("/", function (request, response) {
    response.send("Hello World!");
})

app.get("/getToken", function (request, response) {
    let token = chat.getToken(request.query.userName);
   response.send(JSON.stringify(token));
})

app.listen(3000, function () {
    console.log("Started application on port %d", 3000);
});

const accountSid = process.env.TWILIO_ACCOUNT_SID;
const authToken = process.env.TWILIO_AUTH_TOKEN;
const serviceSid = process.env.TWILIO_SERVICE_SID;
const apiKey = process.env.TWILIO_API_KEY;
const apiSecret = process.env.TWILIO_API_SECRET;
const client = require('twilio')(accountSid, authToken);

// Create a twilio access token
function getToken(identity) {
    const AccessToken = require('twilio').jwt.AccessToken;
    const ChatGrant = AccessToken.ChatGrant;

    const chatGrant = new ChatGrant({
        serviceSid: serviceSid,
    });
    const token = new AccessToken(
        accountSid,
        apiKey,
        apiSecret,
        { 
            identity: identity,
            ttl: 3600
        }
    );

    token.addGrant(chatGrant); 
    return token.toJwt();
}

module.exports = {getToken};

npm start

아래 언급된 명령을 실행하여 angular-cli로 새 각도 프로젝트를 만듭니다.

ng new twilio-chat-example
cd twilio-chat-example
ng serve

npm install --save @twilio/conversations

ng g c chats

connectTwilio() {
    this.isLoading = true;
    this.getToken().subscribe(token => {
      this.client = new Client(token);
      this.isLoading = false;
      this.listenToEvents();
    }, error => console.log(error));
  }

tokenAboutToExpiretokenExpired 이벤트를 수신하여 생성된 토큰을 갱신하고 Twilio 서비스 작업을 계속합니다.

listenToEvents() {
    this.client.on('initialized', () => {
      console.log('Client initialized');
      this.fetchUserChats();
    });

    this.client.on('initFailed', (error: any) => {
      console.log('Client initialization failed: ', error);
    });

    this.client.on('connectionStateChanged', (state: ConnectionState) => {
      console.log('Connection state change: ', state);
    });

    this.client.on('connectionError', (error: any) => {
      console.log('Connection error: ', error);
    });

    this.client.on('tokenAboutToExpire', () => {
      console.log('About to expire');
      this.getToken().subscribe(async (token) => {
        this.client = await this.client.updateToken(token);
      })
    });

    this.client.on('tokenExpired', () => {
      console.log('Token expired');
      this.client.removeAllListeners();
      this.connectTwilio();
    });

    this.client.on('conversationAdded', (conv: Conversation) => {
      setTimeout(async () => {
        if (conv.dateCreated && conv.dateCreated > this.currentTime) {
          console.log('Conversation added', conv);
          await conv.setAllMessagesUnread();
          let newChat = {
            chat: conv,
            unreadCount: 0,
            lastMessage: ''
          }
          this.chatList = [newChat,...this.chatList];
        }
      }, 500);
    });

    this.client.on('messageAdded', async (msg: Message) => {
      console.log('Message added', msg);
      if (this.currentConversation && this.currentConversation.sid === msg.conversation.sid) {
        this.messages.push(msg);
        await this.currentConversation.updateLastReadMessageIndex(msg.index);
        this.chatList = this.chatList.map(el => {
          if(el.chat.sid === this.currentConversation.sid){
            el.lastMessage = msg.body;
          }
          return el;
        });
      } else {
        this.chatList = this.chatList.map(el => {
          if(el.chat.sid === msg.conversation.sid){
            el.lastMessage = msg.body;
            el.unreadCount++;
          }
          return el;
        });
      }
    });

    this.client.on('typingStarted', (user: Participant) => {
      console.log('typing..', user);
      if (user.conversation.sid === this.currentConversation.sid) this.isTyping = true;
    });

    this.client.on('typingEnded', (user: Participant) => {
      console.log('typing end..', user);
      if (user.conversation.sid === this.currentConversation.sid) this.isTyping = false;
    });
  }

fetchUserChats() {
    this.isLoading = true;
    this.client.getSubscribedConversations().then(convs => {
      let chats:any = [...convs.items];
      chats.forEach( async (chat:Conversation) => {
        let obj = {
          chat: chat,
          unreadCount: await chat.getUnreadMessagesCount(),
          lastMessage: (await chat.getMessages()).items[chat.lastReadMessageIndex || 0].body
        }
        this.chatList.push(obj);
      })
      this.isLoading = false;
    }).catch(error => console.log(error));
  }

newChat() {
    this.isLoading = true;
    this.client.getUser(this.newUser).then(res => {
      this.client.createConversation({
        friendlyName: `${this.newUser}-${this.userName}`,
      }).then(async (channel: Conversation) => {
        channel.join().then(async () => {
          await channel.setAllMessagesUnread();
          channel.add(this.newUser).then(() => {
            this.currentConversation = channel;
            this.openChat(channel);
            this.scrollToBottom();
            this.isLoading = false;
          })
        });
      }).catch(error => console.log(error));
    }).catch(err => {
      this.isLoading = false;
      this.error = 'User not found in Twilio';
      setTimeout(() => {
        this.error = null;
      }, 2000);
      this.newUser = null;
    });
  }

sendMessage() {
    this.currentConversation.sendMessage(this.message).then(result => {
      this.message = '';
    }).catch(err => console.log(err));
  }

참고: getMessages가 한 번에 30개의 항목을 반환하고 사용자의 대화를 가져오기 위해 유사한 항목을 구현하므로 스크롤에 로드 메시지를 구현하십시오.

fetchMessages(skip?: number) {
    this.isLoading = true;
    this.currentConversation.getMessages(30, skip).then(async (result) => {
      this.messages = [...result.items, ...this.messages];
      if (!skip) {
        let resetTo = this.messages.length >= 1 ? this.messages[this.messages.length - 1].index : 0;
        await this.currentConversation.updateLastReadMessageIndex(resetTo);
        this.chatList = this.chatList.map( el => {
          if(el.chat.sid == this.currentConversation.sid){
            el.unreadCount = 0;
          }
          return el;
        })
      }
      this.isLoading = false;
    }).catch(error => {
      this.isLoading = false;
      console.log(error)
    });
  }

이것은 Twilio 대화로 채팅 앱을 만드는 기본적인 예입니다. 미디어 첨부, 채팅 종료(대화 상태 관리), 도달 가능성 표시기, 외부 채널 작업 등에 대한 공식 문서에서 기타 사항을 확인할 수 있습니다.

기억해야 할 사항

  • 오작동을 방지하기 위해 동일한 이벤트에 대해 여러 리스너를 추가하지 마십시오.
  • 사용 사례에 따라 논리적 흐름을 생성하기 전에 한 번 Twilio 대화 제한을 확인하십시오.
  • 유료 서비스인 Twilio의 요금제 를 살펴보세요 .

참조

선적 서류 비치:https://www.twilio.com/docs/conversations
예: 주어진 GitHub 예제 링크
(Frontend) 에서 예제 비디오를 확인하십시오.https://github.com/Khushbu-2112/Twilio-chat-frontend-Example
(백엔드)https://github.com/Khushbu-2112/Twilio-chat-backend-Example