Asterisk & FastAGI with Node.js #2 hello-world

Pustinia·2020년 6월 25일
1
post-thumbnail

1. 개발전, 필요한 것들

  • VSCode: 다운로드 받아서 설치 후 기동 확인
  • node.js: 설치 확인 명령어, node.js 를 설치하면 npm도 같이 설치된다.
npm -v
node -v
  • Asterisk CLI 확인
asterisk -rvvvv

  • 이제 시작해보겠다는 마음 가짐이 있으면 OK !
    시작이 반이니 반은 끝냈군.. 휴..

2. 디렉토리 만들고 npm 초기화

node.js 로 프로그램을 만들 디렉토리가 필요하다. 당장 만들자 !!

> mkdir helloAgi

그 다음, npm 초기화를 진행한다. 대부분의 node.js 모듈은 npm 초기화로 시작한다.

> cd helloAgi
> npm init -y

npm 명령어는 따로 정리해야 한다. 생각보다 기능과 명령어가 많다. npm을 간단히 기억하려면, 다른 똑똑한 사람들이 만든 node.js 모듈을 가져다 쓰기 쉽게 도와주는 의존성 관리 툴 정도로 생각하면 된다. 마음에 드는 모듈 쇼핑이 가능하다!!!

init 옵션은 package.json파일을 자동으로 생성해주며 그 파일에 프로퍼티 값을 추가하는데, -y 옵션은 전부 Yes로 묻지도 따지지도 않고 넣어준다는 뜻이다.

이제 VSCode 에서 해당 폴더를 open 해보자. 아래의 그림처럼 되어 있을 것이다.

3. npm으로 ding-dong 모듈을 가지고 오자.

npm 명령어로 ding-dong 모듈을 가지고 오려면 아래와 같이 명령어를 수행하면 된다.

> npm install ding-dong

VSCode 를 이용한다면, 터미널에서 명령어를 날려도 된다.

ding-dong 모듈이 설치 되었고, package.json 파일의 dependencies 에 ding-dong 0.1.7 이 추가 되었다.

4. 이제 js 파일을 만들자 !!

VSCode의 왼쪽에서 새로운 파일을 추가하고, 그 파일 이름을 index.js 라고 한다.
아래의 소스를 작성 한다. 물론 npm ding-dong 에 가면 sample 소스는 존재 한다.
npm ding-dong 바로가기

const AGIServer = require('ding-dong');
const handler = (context) => {
    context.onEvent('variables')
	.then((vars) => {
            return context.streamFile('hello-world');
        })
        .then((result) => {
            return context.setVariable('RECOGNITION_RESULT', 'I\'m your father, Luc');
        })
        .then((result) => {
            return context.end();
        })
        .catch((error) => {
            context.end();
        });

};
const agi = new AGIServer(handler, { debug: true });
agi.start(5000);
[dialplan]
exten => s,1,NoOp(dialplan start====>)  ; log
 same => n,Answer()                     ; answer
 same => n,AGI(agi://127.0.0.1:5000)    ; AGI 호출
 same => n,Hangup()                     ; 전화 종료

npm ding-dong 예제 소스와 약간 다르지만 비슷한 기능을 수행한다. ES6 스타일을 따르도록 노력했다.
정말 짧은 소스지만 온전히 이해하기까지 수많은 삽질이 필요했다.

자, 여기까지 소스코드를 작성했다면 index.js 모듈을 실행하자.

> node index.js

콘솔에 아무것도 나타나지 않는다. 응????
자 그럼 5000번 port를 확인해 보자

netstat -an | grep 5000


좋다 !!! 5000번 port에 뭔가가 떠 있다. 성공이다.

Asterisk서버의 extentions.conf 파일에 위 dialplan을 추가한다.

Asterisk서버로 전화를 걸어 본다. 현재는 테스트를 위해 소프트폰으로 Zoiper5 를 사용하고 있다. 무료로 사용 가능하지만, 유료로도 사용하고 싶을 정도로 정말 조이퍼~~

전화를 걸면 "hello world" 라는 음성이 들리고 전화가 종료될 것이다.
위 AGI 흐름을 순서대로 표시하면 아래와 같다.

  1. 발신자가 전화를 건다. 착신번호는 1111.
  2. Asterisk는 dialplan 에 정의된 1111 이라는 exten을 찾아 아무일도 하지 않고 "dialplan start====>" 이라는 log를 남긴다.(NoOp() 실행)
  3. Answer() 를 수행한다. 즉, Asterisk 가 전화를 받았다.
  4. agi를 호출하여, dialplan에서 AGI서버로 제어를 넘기고 기다린다.
  5. AGI에서 "hello-world" 멘트 play를 Asterisk에게 요청한다.
  6. Asterisk는 발신자에게 "hello-world" 멘트를 play 한다.
  7. AGI에서 "RECOGNITION_RESULT"에 특정 값을 넣어 달라고 Asterisk에게 요청한다.
  8. AGI에서 end()를 요청한다. 제어는 다시 dialplan으로 돌아 왔다.
  9. dialplan에서 Hangup() 으로 전화를 종료한다.

만약 시나리오 DB 또는 API 서버가 별도로 있다면 아래와 같은 그림으로 서버 구성이 가능하다. 물론 Asterisk 로컬 서버에 AGI를 연결 하는 것도 가능하다.

5. 나머지 소스에서 혹시 궁금한 점?

  • debug: true 는 왜?
    - 이 값을 true로 주면, Asterik와 agi 사이에 tcp로 주고받는 json 내용을 log로 확인이 가능하다. 필수 값은 아님. 기본값은 false.
-------> {"command":"STREAM FILE \"hello-world\"\"#\""}
<------- {"err":null,"result":{"code":200,"result":"0 endpos=11234"}}
  • context.onEvent('variables') 는 무엇 ?
    - context에 event를 걸어두는데, 그 이름은 'variables' 이다.
    - 'variables' 이벤트는 Asterisk dialplan에서 AGI가 호출되는 시점에 발생하는 이벤트 이다. ( n,AGI(agi://127.0.0.1:5000) )
  • .then((vars) => 에서 vars는 뭐가 들어 있나요 ?
    - vars는 'variables' 이벤트 발생시 Asterisk로 부터 받은 JSON 규격의 AGI가 호출되기 직전까지의 채널 정보이다.
  • then((result) 에서 result 에는 뭐가 있나요 ?
    - result 에서는 이전 then 구문에서 수행한 함수의 결과값이 담겨져 있다.
<vars.....>
{
  agi_network: 'yes',
  agi_request: 'agi',
  agi_channel: 'SIP/6003-00000004',
  agi_language: 'en',
  agi_type: 'SIP',
  agi_uniqueid: '1593015578.8',
  agi_version: '16.4.1',
  agi_callerid: '2222',
  agi_calleridname: 'handphone',
  agi_callingpres: '0',
  agi_callingani2: '0',
  agi_callington: '0',
  agi_callingtns: '0',
  agi_dnid: '1111',
  agi_rdnis: 'unknown',
  agi_context: 'dialplan',
  agi_extension: 's',
  agi_priority: '3',
  agi_enhanced: '0.0',
  agi_accountcode: '',
  agi_threadid: '140333349705472',
}
  • .then()이 계속 필요한가요?
    - 코딩 스타일에 따라 다르다. 기본적으로 node는 비동기로 수행되기 때문에, then을 이용하여 순서를 보장한다.
    - 필요하다면, async/await 사용도 가능하다.

6. streamFile() 말고 다른 기능은 ?

물론, 가능하다. 아래에 ding-dong에서 사용가능한 API 목록 링크를 추가 한다.
ding-dong API

다음 글에서는 좀 더 다양한 AGI 기능들을 ding-dong으로 구현해 보면서 이야기해볼 생각이다.

profile
취미로 개발 하고 싶은 개발자

0개의 댓글