├── .dockerignore ├── asterisk ├── .dockerignore ├── configs │ ├── ccss.conf │ ├── cdr.conf │ ├── cel.conf │ ├── features.conf │ ├── acl.conf │ ├── stasis.conf │ ├── pjproject.conf │ ├── pjsip_notify.conf │ ├── respoke.conf │ ├── udptl.conf │ ├── ari.conf.j2 │ ├── extensions.conf.j2 │ ├── rtp.conf.j2 │ ├── extconfig.conf │ ├── pjsip.conf.j2 │ ├── sorcery.conf │ ├── musiconhold.conf │ ├── http.conf │ ├── asterisk.conf │ ├── logger.conf │ ├── modules.conf │ ├── manager.conf │ └── indications.conf ├── keys │ ├── ca.cfg │ ├── asterisk.csr │ ├── asterisk.key │ ├── asterisk.crt │ ├── ca.crt │ ├── asterisk.pem │ └── ca.key ├── docker-entrypoint.sh ├── Dockerfile ├── Makefile └── build-asterisk.sh ├── awesome-conference ├── .dockerignore ├── .gitignore ├── src │ ├── lib │ │ ├── serializers │ │ │ ├── channel.js │ │ │ ├── bridge.js │ │ │ ├── index.js │ │ │ ├── req.js │ │ │ └── err.js │ │ └── log.js │ ├── public │ │ ├── index.html │ │ ├── index.css │ │ └── conference.js │ ├── services │ │ ├── respoke.js │ │ ├── conference.js │ │ └── asterisk.js │ └── bootstrap.js ├── config │ ├── custom-environment-variables.js │ └── default.js ├── Dockerfile ├── app.js └── package.json ├── awesome-conference.png ├── up.sh ├── docker-compose.yml ├── README.md └── LICENSE /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | -------------------------------------------------------------------------------- /asterisk/.dockerignore: -------------------------------------------------------------------------------- 1 | .git -------------------------------------------------------------------------------- /asterisk/configs/ccss.conf: -------------------------------------------------------------------------------- 1 | [general] 2 | -------------------------------------------------------------------------------- /asterisk/configs/cdr.conf: -------------------------------------------------------------------------------- 1 | [general] 2 | 3 | enable=No 4 | -------------------------------------------------------------------------------- /asterisk/configs/cel.conf: -------------------------------------------------------------------------------- 1 | [general] 2 | 3 | enable=No 4 | -------------------------------------------------------------------------------- /awesome-conference/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .git 3 | -------------------------------------------------------------------------------- /awesome-conference/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | -------------------------------------------------------------------------------- /asterisk/configs/features.conf: -------------------------------------------------------------------------------- 1 | [general] 2 | 3 | [applicationmap] 4 | -------------------------------------------------------------------------------- /asterisk/configs/acl.conf: -------------------------------------------------------------------------------- 1 | ; Empty config file so that Asterisk doesn't complain -------------------------------------------------------------------------------- /asterisk/configs/stasis.conf: -------------------------------------------------------------------------------- 1 | ; Empty config file so that Asterisk doesn't complain -------------------------------------------------------------------------------- /asterisk/configs/pjproject.conf: -------------------------------------------------------------------------------- 1 | ; Empty config file so that Asterisk doesn't complain -------------------------------------------------------------------------------- /asterisk/configs/pjsip_notify.conf: -------------------------------------------------------------------------------- 1 | [digium-check-cfg] 2 | Event=>check-sync 3 | 4 | -------------------------------------------------------------------------------- /asterisk/configs/respoke.conf: -------------------------------------------------------------------------------- 1 | [webrtc] 2 | type=transport 3 | protocol=socket.io 4 | -------------------------------------------------------------------------------- /asterisk/configs/udptl.conf: -------------------------------------------------------------------------------- 1 | [general] 2 | 3 | udptlstart=4000 4 | udptlend=4999 5 | -------------------------------------------------------------------------------- /awesome-conference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt-jordan/awesome-conference/HEAD/awesome-conference.png -------------------------------------------------------------------------------- /asterisk/configs/ari.conf.j2: -------------------------------------------------------------------------------- 1 | [general] 2 | enabled=True 3 | allowed_origins=* 4 | websocket_write_timeout=2000 5 | 6 | [asterisk] 7 | type=user 8 | read_only=no 9 | password={{ ARI_PASSWORD }} 10 | -------------------------------------------------------------------------------- /asterisk/keys/ca.cfg: -------------------------------------------------------------------------------- 1 | [req] 2 | distinguished_name = req_distinguished_name 3 | prompt = no 4 | 5 | [req_distinguished_name] 6 | CN=Asterisk Private CA 7 | O=Asterisk 8 | 9 | [ext] 10 | basicConstraints=CA:TRUE 11 | -------------------------------------------------------------------------------- /up.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # If you aren't running Linux, use an alternative means to get the primary 4 | # address of your machine 5 | export EXTERNAL_IP=$(ip route get 8.8.8.8 | head -1 | cut -d' ' -f8) 6 | docker-compose up --build 7 | -------------------------------------------------------------------------------- /asterisk/configs/extensions.conf.j2: -------------------------------------------------------------------------------- 1 | [globals] 2 | 3 | APPLICATION_NAME={{ APPLICATION_NAME }} 4 | 5 | [inbound] 6 | 7 | exten => {{ RESPOKE_ENDPOINT }},1,NoOp() 8 | same => n,Stasis(${APPLICATION_NAME}) 9 | same => n,Hangup() 10 | -------------------------------------------------------------------------------- /asterisk/configs/rtp.conf.j2: -------------------------------------------------------------------------------- 1 | {%- set RTP_START_RANGE = RTP_START_RANGE|default('5000') -%} 2 | {%- set RTP_END_RANGE = RTP_END_RANGE|default('31000') -%} 3 | 4 | [general] 5 | 6 | rtpstart={{ RTP_START_RANGE }} 7 | rtpend={{ RTP_END_RANGE }} 8 | stunaddr=stun.l.google.com:19302 9 | -------------------------------------------------------------------------------- /awesome-conference/src/lib/serializers/channel.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function channelSerializer(channel) { 4 | if (!channel) { 5 | return channel; 6 | } 7 | 8 | return { 9 | id: channel.id, 10 | name: channel.name 11 | }; 12 | }; 13 | -------------------------------------------------------------------------------- /asterisk/configs/extconfig.conf: -------------------------------------------------------------------------------- 1 | [settings] 2 | ps_endpoints => odbc,asterisk 3 | ps_auths => odbc,asterisk 4 | ps_aors => odbc,asterisk 5 | ps_domain_aliases => odbc,asterisk 6 | ps_endpoint_id_ips => odbc,asterisk 7 | ps_contacts => odbc,asterisk 8 | ps_registrations => odbc,asterisk 9 | ps_systems => odbc,asterisk 10 | -------------------------------------------------------------------------------- /awesome-conference/src/lib/serializers/bridge.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function bridgeSerializer(bridge) { 4 | if (!bridge) { 5 | return bridge; 6 | } 7 | 8 | return { 9 | id: bridge.id, 10 | name: bridge.name, 11 | video_source: bridge.video_source_id 12 | }; 13 | }; 14 | -------------------------------------------------------------------------------- /awesome-conference/src/lib/serializers/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const err = require('./err'); 3 | const req = require('./req'); 4 | const channel = require('./channel'); 5 | const bridge = require('./bridge'); 6 | const bunyan = require('bunyan'); 7 | 8 | module.exports = { 9 | err, 10 | req, 11 | channel, 12 | bridge, 13 | res: bunyan.stdSerializers.res 14 | }; 15 | -------------------------------------------------------------------------------- /awesome-conference/src/lib/serializers/req.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function reqSerializer(req) { 4 | if (!req) { 5 | return req; 6 | } 7 | 8 | return { 9 | id: req.id, 10 | url: req.url, 11 | method: req.method, 12 | headers: req.headers, 13 | ip: req.ip, 14 | protocol: req.protocol || 'http' 15 | }; 16 | }; 17 | -------------------------------------------------------------------------------- /asterisk/docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | # Process templates 6 | find /etc/asterisk -name '*.j2' | while read template; do 7 | # replace templated values with environment variables of the same name 8 | j2 ${template} > $(dirname ${template})/$(basename ${template} .j2) 9 | # remove the template once processed 10 | rm -f ${template} 11 | done 12 | 13 | set -x 14 | 15 | exec "$@" 16 | -------------------------------------------------------------------------------- /asterisk/configs/pjsip.conf.j2: -------------------------------------------------------------------------------- 1 | {%- set PJSIP_PORT = PJSIP_PORT|default('5060') -%} 2 | {%- set PJSIP_USER_AGENT = PJSIP_USER_AGENT|default('Asterisk') -%} 3 | 4 | [global] 5 | type=global 6 | debug=False 7 | user_agent={{ PJSIP_USER_AGENT }} 8 | 9 | [transport-udp] 10 | type=transport 11 | protocol=udp 12 | bind=0.0.0.0:{{ PJSIP_PORT }} 13 | 14 | [transport-tcp] 15 | type=transport 16 | protocol=tcp 17 | bind=0.0.0.0:{{ PJSIP_PORT }} 18 | 19 | [transport-ws] 20 | type=transport 21 | protocol=ws 22 | bind=0.0.0.0 -------------------------------------------------------------------------------- /asterisk/configs/sorcery.conf: -------------------------------------------------------------------------------- 1 | [res_pjsip] 2 | endpoint=astdb,ps_endpoints 3 | auth=astdb,ps_auths 4 | aor=astdb,ps_aors 5 | domain_alias=astdb,ps_domain_aliases 6 | contact=astdb,ps_contacts 7 | system=astdb,ps_systems 8 | 9 | [res_pjsip_endpoint_identifier_ip] 10 | identify=astdb,ps_endpoint_id_ips 11 | 12 | [res_pjsip_outbound_registration] 13 | registration=astdb,ps_registrations 14 | 15 | [res_respoke] 16 | endpoint/cache=memory_cache,maximum_objects=150,expire_on_reload=yes,object_lifetime_maximum=3600 17 | endpoint=astdb,rs_endpoints 18 | app=astdb,rs_apps 19 | -------------------------------------------------------------------------------- /awesome-conference/config/custom-environment-variables.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | port: 'PORT', 5 | environment: 'NODE_ENV', 6 | log: { 7 | level: 'LOG_LEVEL' 8 | }, 9 | asterisk: { 10 | host: 'HOST', 11 | username: 'ARI_USERNAME', 12 | password: 'ARI_PASSWORD', 13 | application: 'APPLICATION' 14 | }, 15 | respoke: { 16 | app_secret: 'APP_SECRET', 17 | app_id: 'APP_ID', 18 | endpoint: 'RESPOKE_ENDPOINT', 19 | endpoint_role_id: 'ENDPOINT_ROLE_ID' 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /awesome-conference/config/default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | port: 3000, 5 | environment: 'development', 6 | log: { 7 | level: 'debug', 8 | logUncaughtException: true, 9 | pretty: true 10 | }, 11 | asterisk: { 12 | host: 'localhost', 13 | username: 'asterisk', 14 | password: 'asterisk', 15 | application: 'conference' 16 | }, 17 | respoke: { 18 | app_secret: 'XXX', 19 | app_id: 'YYY', 20 | endpoint: 'webrtc', 21 | endpoint_role_id: 'ZZZ' 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /asterisk/keys/asterisk.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIIBazCB1QIBADAsMRcwFQYDVQQDEw5tam9yZGFuLWxhcHRvcDERMA8GA1UEChMI 3 | QXN0ZXJpc2swgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAO151bZIuBmH7hW6 4 | /xqoXBvTztMP9suv7MKvQ4m3M4pLJpLJI477NRdHyNqswCTK8Mq9wxkhg9cNsNcK 5 | AhsdrJG5tjPEbIUpo6Stj3jObbvWNXExG2zvjsGZp3ttNfFYF97XziIppZ5arcpA 6 | PeJLWhtpPPTAOw3yoj71wpzBn297AgMBAAGgADANBgkqhkiG9w0BAQsFAAOBgQDm 7 | d0nMFgUGcNiC2YrjcgjIgHfjkP0x5F02WRO9HwTiExqnVR/gRnahUnUO0cBBimJ0 8 | eH9Pm5gbBmyK6zR+fJ2ACcuGk+wb8sRbSFYnXIC2nShXbsRu/GfXZej8DZnk1MV5 9 | 8wNydGxBRF/+IkW47DGSn9u5yYGWv/bi5ZQsb82JyA== 10 | -----END CERTIFICATE REQUEST----- 11 | -------------------------------------------------------------------------------- /awesome-conference/src/lib/serializers/err.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const bunyan = require('bunyan'); 3 | 4 | module.exports = function errSerializer(err) { 5 | if (typeof err === 'string') { 6 | return { message: err }; 7 | } 8 | 9 | const result = bunyan.stdSerializers.err(err); 10 | 11 | // log any enumerable properties not grabbed by bunyan 12 | if (err && (typeof err === 'object')) { 13 | Object.keys(err).forEach(key => { 14 | if (key !== 'error@context' && {}.hasOwnProperty.call(result, key)) { 15 | result[key] = err[key]; 16 | } 17 | }); 18 | } 19 | 20 | return result; 21 | }; 22 | -------------------------------------------------------------------------------- /awesome-conference/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:6 2 | 3 | RUN useradd --system --uid 202 asterisk && \ 4 | mkdir -p /home/asterisk && \ 5 | mkdir -p /usr/src/app && \ 6 | chown -R asterisk:asterisk /home/asterisk && \ 7 | chown -R asterisk:asterisk /usr/src/app && \ 8 | wget -O /usr/local/bin/dumb-init \ 9 | https://github.com/Yelp/dumb-init/releases/download/v1.1.3/dumb-init_1.1.3_amd64 && \ 10 | chmod +x /usr/local/bin/dumb-init 11 | 12 | USER asterisk 13 | WORKDIR /usr/src/app 14 | 15 | COPY package.json /usr/src/app/ 16 | RUN npm install 17 | 18 | COPY . /usr/src/app 19 | 20 | EXPOSE 3000 21 | 22 | ENTRYPOINT [ "/usr/local/bin/dumb-init", "--"] 23 | CMD [ "node", "app.js" ] 24 | -------------------------------------------------------------------------------- /awesome-conference/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const https = require('https'); 3 | const config = require('config'); 4 | const log = require('./src/lib/log'); 5 | const bootstrap = require('./src/bootstrap'); 6 | const fs = require('fs'); 7 | const privateKey = fs.readFileSync('/etc/asterisk/keys/asterisk.key', 'utf8'); 8 | const certificate = fs.readFileSync('/etc/asterisk/keys/asterisk.crt', 'utf8'); 9 | 10 | const credentials = {key: privateKey, cert: certificate}; 11 | 12 | bootstrap.boot().then(app => { 13 | https.createServer(credentials, app).listen(config.port, () => { 14 | log.info(`server listening on port ${config.port}`); 15 | }); 16 | }).catch(err => { 17 | process.nextTick(() => { throw err; }); 18 | }); 19 | -------------------------------------------------------------------------------- /asterisk/keys/asterisk.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICXgIBAAKBgQDtedW2SLgZh+4Vuv8aqFwb087TD/bLr+zCr0OJtzOKSyaSySOO 3 | +zUXR8jarMAkyvDKvcMZIYPXDbDXCgIbHayRubYzxGyFKaOkrY94zm271jVxMRts 4 | 747Bmad7bTXxWBfe184iKaWeWq3KQD3iS1obaTz0wDsN8qI+9cKcwZ9vewIDAQAB 5 | AoGATB+R8iy9VvHL0AtxWwo8/G6ZDe9AHWr3ajDkZXDkhKAybTssutir3BqWuAJI 6 | VYL4TQUnVRb5PCaXWVdL9YU9m5riXNchGrygUL2eYMNmDWlkOp/C2Wt/YUF4c+Pr 7 | jVOrvaXq2bAi9IZrPPIPzWOtZr+xxPNbDSbJ0PBYdW+GjwECQQD7FU6J1MmZexYX 8 | zxqfeRHx8EYijGBGR6BKhuXPLystH0kTIB7b392NPeum3ZOx3ScXIi54sX7JFHbC 9 | UEUYFVXTAkEA8iBQWTqDPlR1Q3vBywvbCCATBspnifmR/ko+UD0oDc88D3IpMJE/ 10 | navhXJWKm+Qpx1/u92c83HRXYe5GaEWuuQJBAMT4yR09zk3WjtVsxrD2lMpdOVgf 11 | EePwiuIMg7qOgWurkHMtjqflLnSAcB7+0sAFA4aVbSmH7TOoFyaHPD0Rd7sCQQCM 12 | pVKnEkMskLjxM1FWFhPse10yA57zWeKBWTUrPOsoYmnb+w+MI8NVXoNxWXiDp5zR 13 | MJvKYNDMqlQcEeIaLegZAkEA5ujL1HXihts4Cwwh9tzIujMRVnhWPY7hK7cXbwFL 14 | dRhK6nIvEv+5HvvPFw5iTO5LF+lKqls0lyHD0f792Qk5zA== 15 | -----END RSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: '2' 3 | services: 4 | asterisk: 5 | image: asterisk-iot/asterisk 6 | build: 7 | context: asterisk 8 | environment: 9 | APPLICATION_NAME: super-conference 10 | ARI_PASSWORD: zomgnotsafe! 11 | RESPOKE_ENDPOINT: webrtc 12 | volumes: 13 | - astdb:/var/lib/asterisk 14 | - keys:/etc/asterisk/keys 15 | network_mode: host 16 | 17 | awesome-conference: 18 | image: asterisk-iot/awesome-conference 19 | build: 20 | context: awesome-conference 21 | environment: 22 | PORT: 3000 23 | HOST: ${EXTERNAL_IP} 24 | RESPOKE_ENDPOINT: webrtc 25 | ARI_PASSWORD: zomgnotsafe! 26 | APPLICATION: super-conference 27 | APP_SECRET: XXXX 28 | APP_ID: YYYY 29 | ENDPOINT_ROLE_ID: ZZZZ 30 | depends_on: 31 | - asterisk 32 | networks: 33 | - common 34 | ports: 35 | - "3000:3000" 36 | volumes: 37 | - keys:/etc/asterisk/keys 38 | restart: always 39 | 40 | volumes: 41 | astdb: {} 42 | keys: {} 43 | 44 | networks: 45 | common: 46 | driver: bridge 47 | -------------------------------------------------------------------------------- /awesome-conference/src/lib/log.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const bunyan = require('bunyan'); 3 | const name = require('../../package.json').name; 4 | const bformat = require('bunyan-format'); 5 | const serializers = require('./serializers'); 6 | const config = require('config'); 7 | 8 | function createLogger() { 9 | const { level, pretty, filePath: path } = config.log; 10 | 11 | if (level === 'silent') { 12 | return bunyan.createLogger({ name, streams: [] }); 13 | } 14 | 15 | if (path) { 16 | return bunyan.createLogger({ 17 | name, 18 | streams: [{ level, path }], 19 | serializers 20 | }); 21 | } 22 | 23 | const stream = pretty ? 24 | bformat({ outputMode: 'short' }) : 25 | process.stdout; 26 | 27 | return bunyan.createLogger({ 28 | name, 29 | streams: [{ level, stream }], 30 | serializers 31 | }); 32 | } 33 | 34 | const log = createLogger(); 35 | 36 | if (config.log.logUncaughtException) { 37 | process.on('uncaughtException', err => { 38 | log.fatal({ err }, 'Uncaught exception'); 39 | process.exit(1); 40 | }); 41 | } 42 | 43 | module.exports = log; 44 | -------------------------------------------------------------------------------- /awesome-conference/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "awesome-conference", 3 | "version": "0.0.1", 4 | "description": "A super awesome conference for IoT shenaniganry", 5 | "main": "app.js", 6 | "scripts": { 7 | "start": "node app.js", 8 | "lint": "eslint .", 9 | "test": "npm run lint && mocha", 10 | "mocha": "mocha", 11 | "cover": "istanbul cover --report html _mocha" 12 | }, 13 | "engines": { 14 | "node": "^6.2.2", 15 | "npm": "^3.9.6" 16 | }, 17 | "license": "MIT", 18 | "dependencies": { 19 | "ari-client": "^1.1.0", 20 | "bunyan": "^1.4.0", 21 | "bunyan-format": "^0.2.1", 22 | "bunyan-middleware": "^0.2.1", 23 | "chai": "^3.2.0", 24 | "chance": "^1.0.4", 25 | "config": "^1.21.0", 26 | "env-test": "^1.0.0", 27 | "eslint": "^3.3.0", 28 | "eslint-config-airbnb-base": "^5.0.2", 29 | "eslint-plugin-import": "^1.13.0", 30 | "express": "^4.14.0", 31 | "express-session": "^1.14.2", 32 | "fs": "0.0.1-security", 33 | "https": "^1.0.0", 34 | "lodash": "^4.13.1", 35 | "mocha": "^2.2.5", 36 | "node-uuid": "^1.4.3", 37 | "request": "^2.78.0", 38 | "respoke-admin": "^1.9.0", 39 | "uuid": "^2.0.3" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /awesome-conference/src/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 |
15 |
16 |
17 | 18 |
19 |
20 | Powered by Asterisk and Respoke! 21 |
22 |
23 | 24 | 25 | -------------------------------------------------------------------------------- /asterisk/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:jessie 2 | 3 | ARG PJSIP_PORT=5060 4 | 5 | RUN useradd --system --uid 202 asterisk 6 | 7 | RUN apt-get update -qq && \ 8 | DEBIAN_FRONTEND=noninteractive \ 9 | apt-get install -y --no-install-recommends \ 10 | autoconf \ 11 | build-essential \ 12 | libedit-dev \ 13 | libsrtp-dev \ 14 | libjansson-dev \ 15 | libogg-dev \ 16 | libsqlite3-dev \ 17 | libssl-dev \ 18 | libxml2-dev \ 19 | libxslt1-dev \ 20 | uuid \ 21 | uuid-dev \ 22 | binutils-dev \ 23 | libpopt-dev \ 24 | libspandsp-dev \ 25 | libvorbis-dev \ 26 | libresample1-dev \ 27 | libcurl4-openssl-dev \ 28 | vim \ 29 | curl \ 30 | wget \ 31 | xmlstarlet \ 32 | python-dev \ 33 | python-pip \ 34 | && \ 35 | pip install j2cli && \ 36 | apt-get purge -y --auto-remove && rm -rf /var/lib/apt/lists/* 37 | 38 | ENV ASTERISK_VERSION=14.2.0-rc1 39 | ENV RESPOKE_VERSION=v1.3.3 40 | 41 | COPY build-asterisk.sh /usr/src/build/ 42 | COPY configs /usr/src/build/etc-asterisk/ 43 | COPY keys /usr/src/build/keys 44 | COPY docker-entrypoint.sh / 45 | 46 | RUN /usr/src/build/build-asterisk.sh 47 | 48 | EXPOSE ${PJSIP_PORT}/udp 8088 5038 49 | 50 | ENTRYPOINT ["/docker-entrypoint.sh"] 51 | 52 | CMD ["/usr/sbin/asterisk", "-f"] 53 | -------------------------------------------------------------------------------- /asterisk/keys/asterisk.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDTTCCATUCAQEwDQYJKoZIhvcNAQELBQAwMTEcMBoGA1UEAxMTQXN0ZXJpc2sg 3 | UHJpdmF0ZSBDQTERMA8GA1UEChMIQXN0ZXJpc2swHhcNMTYxMTAxMTkxMDMzWhcN 4 | MTcxMTAxMTkxMDMzWjAsMRcwFQYDVQQDEw5tam9yZGFuLWxhcHRvcDERMA8GA1UE 5 | ChMIQXN0ZXJpc2swgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAO151bZIuBmH 6 | 7hW6/xqoXBvTztMP9suv7MKvQ4m3M4pLJpLJI477NRdHyNqswCTK8Mq9wxkhg9cN 7 | sNcKAhsdrJG5tjPEbIUpo6Stj3jObbvWNXExG2zvjsGZp3ttNfFYF97XziIppZ5a 8 | rcpAPeJLWhtpPPTAOw3yoj71wpzBn297AgMBAAEwDQYJKoZIhvcNAQELBQADggIB 9 | AB8ogB+RWEDbs9Ikr2oNhkqCijZ7CfKe41nyMNFZfm+oLEbXvQ5m9COwBPLssS22 10 | NVsSD+v3qAFulEmAiSlqrYhD11uWapr1UZlTvzhZGrRKVKatqgZQH7AxTBxwSnHG 11 | q848UK8vyKbwSc2dgIfaVoGR5PYvIOc8so6izwR8NZPAsZD8huUVYQKOMxDYgbzh 12 | 4c6gL2prpkC8QC1FfTQBYFEEgaVntCYid6qQT2/zkMNFPrBzfJ+9fS7KXcbuI8uI 13 | pBXtmvqKXt/9ligiKO10VSIV2noMTz1eBXbYcP/d87873M/4XLEVCar1OyuNHhr9 14 | PENMvfarzEu4eo9G3hLmDqsuriG/zH2JTiSYc/Zdg1m2lkhxwh5c5VUbXXlh7uL7 15 | 3tsUwFcBTWBEGlbXFWF+H3z+GzI3FWgR6l0ATvHtbfp5ykyxNZj4bzPBZZpXSgLC 16 | KCntspPJNiEFR3B/SmGt7BsIv/iNdBcbPxKWGkk5TYKDEyrJhJGr3akRKsPLQmHH 17 | 7BHDImm/OTnk7NxonaOVzw+5l9VWW2fJ+Gncw1nn1TVCJ4o2GMNkPAS5Ocrrmyyi 18 | OJVqR69BkKMiU7F8Rr7ZoI9WZuto0frAL9XC8pExvc4xZJbiMuuVk/6t+fUH2S7n 19 | Om9uSEahsoNDx7oqkmEnfGOcsjF5oFldxg/drjW8a+2w 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /asterisk/Makefile: -------------------------------------------------------------------------------- 1 | IMAGE_NAME := asterisk-iot 2 | DOCKER_REPO_NAME := $(IMAGE_NAME) 3 | 4 | GIT_BRANCH_NAME := $(shell git rev-parse --abbrev-ref HEAD | sed "sX/X-Xg") 5 | GIT_TAG_NAME := $(shell git describe --always HEAD) 6 | 7 | TAGS := $(GIT_BRANCH_NAME) $(GIT_TAG_NAME) 8 | 9 | ifeq ($(GIT_BRANCH_NAME),master) 10 | TAGS += latest 11 | endif 12 | 13 | # 14 | # default build target 15 | # 16 | all: build 17 | .PHONY: all 18 | 19 | # 20 | # docker pull latest dependencies. 21 | # 22 | pull: 23 | docker pull $(shell sed -n 's/^FROM // p' Dockerfile) 24 | .PHONY: pull 25 | 26 | # 27 | # docker build the image 28 | # 29 | build: 30 | docker build -t $(DOCKER_REPO_NAME) . 31 | .PHONY: build 32 | 33 | # 34 | # docker tag the build 35 | # 36 | tag: build 37 | echo $(TAGS) | xargs -n 1 docker tag $(DOCKER_REPO_NAME) 38 | .PHONY: tag 39 | 40 | # 41 | # docker push the tags to the respoke registry 42 | # 43 | push: tag 44 | echo $(TAGS) | xargs -n 1 docker push 45 | .PHONY: push 46 | 47 | # 48 | # quick cleanup; leaves things that are expensive to remove or rebuild 49 | # 50 | clean: 51 | rm -rf build/ 52 | rm -f version.txt 53 | .PHONY: clean 54 | 55 | # 56 | # remove any images generated by this build 57 | # 58 | clean-images: 59 | docker images -f 'label=cirrus.name=$(IMAGE_NAME)' \ 60 | --format="{{.Repository}} {{.Tag}} {{.ID}}" | \ 61 | awk '{ \ 62 | if ($$2=="") { \ 63 | print $$3 \ 64 | } else { \ 65 | print $$1 ":" $$2 \ 66 | } \ 67 | }' | \ 68 | xargs docker rmi 69 | .PHONY: clean-images 70 | 71 | # 72 | # clean up even the stuff that's expensive to build 73 | # 74 | really-clean: clean clean-images 75 | .PHONY: really-clean 76 | 77 | # 78 | # bamboo build process 79 | # 80 | bamboo: pull build tag push 81 | echo $(GIT_TAG_NAME) > version.txt 82 | .PHONY: bamboo -------------------------------------------------------------------------------- /awesome-conference/src/services/respoke.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Respoke = require('respoke-admin'); 4 | const log = require('../lib/log'); 5 | 6 | class RespokeWrapper { 7 | 8 | constructor(config) { 9 | this.app_id = config.respoke.app_id; 10 | this.app_secret = config.respoke.app_secret; 11 | this.endpoint_role_id = config.respoke.endpoint_role_id; 12 | this.client = null; 13 | } 14 | 15 | connect() { 16 | let that = this; 17 | 18 | if (this.client) { 19 | return Promise.reject('Client already connected'); 20 | } 21 | 22 | log.info('Connecting to Respoke'); 23 | 24 | return new Promise(resolve => { 25 | that.client = new Respoke({ 26 | appId: that.app_id, 27 | 'App-Secret': that.app_secret, 28 | autoreconnect: true 29 | }); 30 | that.client.auth.connect({ endpointId: 'backend' }); 31 | that.client.on('connect', () => { 32 | log.info('Connected to Respoke'); 33 | resolve(that.client); 34 | }); 35 | }); 36 | } 37 | 38 | register_endpoint(endpointId) { 39 | const that = this; 40 | 41 | if (!this.client) { 42 | return Promise.reject('Client not connected'); 43 | } 44 | 45 | return this.client.auth.endpoint({ 46 | endpointId, 47 | roleId: that.endpoint_role_id 48 | }).then( authData => { 49 | log.info(`Providing auth for endpoint ${endpointId}`) 50 | return Promise.resolve({ 51 | appId: that.app_id, 52 | tokenId: authData.tokenId 53 | }); 54 | }); 55 | } 56 | } 57 | 58 | module.exports.RespokeWrapper = RespokeWrapper; 59 | -------------------------------------------------------------------------------- /asterisk/keys/ca.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIE3jCCAsYCCQCLLhOlYo+5+DANBgkqhkiG9w0BAQsFADAxMRwwGgYDVQQDExNB 3 | c3RlcmlzayBQcml2YXRlIENBMREwDwYDVQQKEwhBc3RlcmlzazAeFw0xNjExMDEx 4 | OTEwMzBaFw0xNzExMDExOTEwMzBaMDExHDAaBgNVBAMTE0FzdGVyaXNrIFByaXZh 5 | dGUgQ0ExETAPBgNVBAoTCEFzdGVyaXNrMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A 6 | MIICCgKCAgEA4QpYA7C3p2iX3M/zbBQRvrhSAgsu1ThniNkJZBko+2mbUYbw9D+I 7 | iCstGOhzcNgYPNYghubmLbV1Xfi30KoEHoiLxizlKTqp8wXEVbe2JbrPPAtBCphb 8 | gpZalWaX9RoVMDAwhtR9Y3FqLs0knC9ilKhVp1LTTlsjjvQjUimWFzCSSIR5vo18 9 | 7xgyt32yeC9ksi+nuJo5n8ZczUGnPoK+t1/ZcXX5kze2ap4PG4r0RJesfBFkTk1W 10 | vIyypHYCZ+ebfZ0+zHIBIH68hLc0gTA2bSxpTg04aO+tkOTLBs+HeLm7KFWqU8T/ 11 | HqnMhx+2wEp2CGrlwz0mJexMdJUBoidT0d5wuBGn3qF4zpBKd8IKfkFv0HsMCi4y 12 | q0c7BgxlzjZjt2jNLbOZwKUEFdCJuaXegFSLbhYys6fD6lkRqFFcdIewMi0oOQL5 13 | 4SAwq34U/heNsCrkZ3fk2GUHaBOKAni4xEJCIkb+9gypZu70SFn1qzTamR0SfeIM 14 | HWP3tcmbM+28qlMWPs5c5P8ZrNg1v7XV6c6xJHGvoC/IGY/2FnAkL+m3xTIgYFRc 15 | A+Alo+AfZbwhcYWRSEei5jr1OibrZYKCP/BZzQnFYDEKR7WQTzIR823jV2RqGu44 16 | EwEbHmmtqqEorm3jYVULLtcJJxWJsrCfB0oXZzf6//J7nycz8O2VQ/UCAwEAATAN 17 | BgkqhkiG9w0BAQsFAAOCAgEAxX0aIDNc+S1AJ1EgZFlSeTW/j3uB5eDf+RKhUXjK 18 | P05fjaJ/1OC72pZ+Ar1HVX44ITW255MXB5ny55MvDKkYOeQMkVOhuIVIB4qbTUGo 19 | t+/Wznp+VXFJqZIkTftUokYfyk3cg9AUiKCIa98pHU8zFSqK801suuxeFFtMviP4 20 | 0bLy4SrrtUBGtFeB+OpNw5O8sSGy87g1esPXuaM1gabuddjc/8kesbGqO86rvA/k 21 | 0PCURiTUkl35etsIsRl1t7zuZT6o7EtBnOLt60WpPtHLfDk0vxp6MsnMuGCDBgXm 22 | 7GoFGhzKr9NVrCq8bb0+cRvPCVnuIDQHMer+9JRwNSi9LHc4kXGbPsMuc0euqXxP 23 | jw88TpZjrkKNnHDmHBJxD0sVVBhJCtkfeOKRbGhzmYmR0d6Xqmjgz/XkO4JAb+Rh 24 | Y4zoWIqpkorubg11eq3gYEUtKhkTKQErZ6cUZ5mO0gyvuF/gUYfL8Z/iYLswcsDb 25 | oj7pf0M2qWHDc0toEiJrQ74II4mIPPD1qLXAxaNztGPu82NAH13R+FbeiF4P8eab 26 | 253BMuCMiLDmpZ6opgxHEkkguQ46ka6917l1V1Hq/MZyDYSb5iMdrA45fu1x2GFM 27 | +gBqk7mzYrLDlxZyQl5hFr83BZUji+2lWOsXrtD6uRGxjwOy6Bk+y7K+KRyF5z15 28 | /o8= 29 | -----END CERTIFICATE----- 30 | -------------------------------------------------------------------------------- /asterisk/keys/asterisk.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICXgIBAAKBgQDtedW2SLgZh+4Vuv8aqFwb087TD/bLr+zCr0OJtzOKSyaSySOO 3 | +zUXR8jarMAkyvDKvcMZIYPXDbDXCgIbHayRubYzxGyFKaOkrY94zm271jVxMRts 4 | 747Bmad7bTXxWBfe184iKaWeWq3KQD3iS1obaTz0wDsN8qI+9cKcwZ9vewIDAQAB 5 | AoGATB+R8iy9VvHL0AtxWwo8/G6ZDe9AHWr3ajDkZXDkhKAybTssutir3BqWuAJI 6 | VYL4TQUnVRb5PCaXWVdL9YU9m5riXNchGrygUL2eYMNmDWlkOp/C2Wt/YUF4c+Pr 7 | jVOrvaXq2bAi9IZrPPIPzWOtZr+xxPNbDSbJ0PBYdW+GjwECQQD7FU6J1MmZexYX 8 | zxqfeRHx8EYijGBGR6BKhuXPLystH0kTIB7b392NPeum3ZOx3ScXIi54sX7JFHbC 9 | UEUYFVXTAkEA8iBQWTqDPlR1Q3vBywvbCCATBspnifmR/ko+UD0oDc88D3IpMJE/ 10 | navhXJWKm+Qpx1/u92c83HRXYe5GaEWuuQJBAMT4yR09zk3WjtVsxrD2lMpdOVgf 11 | EePwiuIMg7qOgWurkHMtjqflLnSAcB7+0sAFA4aVbSmH7TOoFyaHPD0Rd7sCQQCM 12 | pVKnEkMskLjxM1FWFhPse10yA57zWeKBWTUrPOsoYmnb+w+MI8NVXoNxWXiDp5zR 13 | MJvKYNDMqlQcEeIaLegZAkEA5ujL1HXihts4Cwwh9tzIujMRVnhWPY7hK7cXbwFL 14 | dRhK6nIvEv+5HvvPFw5iTO5LF+lKqls0lyHD0f792Qk5zA== 15 | -----END RSA PRIVATE KEY----- 16 | -----BEGIN CERTIFICATE----- 17 | MIIDTTCCATUCAQEwDQYJKoZIhvcNAQELBQAwMTEcMBoGA1UEAxMTQXN0ZXJpc2sg 18 | UHJpdmF0ZSBDQTERMA8GA1UEChMIQXN0ZXJpc2swHhcNMTYxMTAxMTkxMDMzWhcN 19 | MTcxMTAxMTkxMDMzWjAsMRcwFQYDVQQDEw5tam9yZGFuLWxhcHRvcDERMA8GA1UE 20 | ChMIQXN0ZXJpc2swgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAO151bZIuBmH 21 | 7hW6/xqoXBvTztMP9suv7MKvQ4m3M4pLJpLJI477NRdHyNqswCTK8Mq9wxkhg9cN 22 | sNcKAhsdrJG5tjPEbIUpo6Stj3jObbvWNXExG2zvjsGZp3ttNfFYF97XziIppZ5a 23 | rcpAPeJLWhtpPPTAOw3yoj71wpzBn297AgMBAAEwDQYJKoZIhvcNAQELBQADggIB 24 | AB8ogB+RWEDbs9Ikr2oNhkqCijZ7CfKe41nyMNFZfm+oLEbXvQ5m9COwBPLssS22 25 | NVsSD+v3qAFulEmAiSlqrYhD11uWapr1UZlTvzhZGrRKVKatqgZQH7AxTBxwSnHG 26 | q848UK8vyKbwSc2dgIfaVoGR5PYvIOc8so6izwR8NZPAsZD8huUVYQKOMxDYgbzh 27 | 4c6gL2prpkC8QC1FfTQBYFEEgaVntCYid6qQT2/zkMNFPrBzfJ+9fS7KXcbuI8uI 28 | pBXtmvqKXt/9ligiKO10VSIV2noMTz1eBXbYcP/d87873M/4XLEVCar1OyuNHhr9 29 | PENMvfarzEu4eo9G3hLmDqsuriG/zH2JTiSYc/Zdg1m2lkhxwh5c5VUbXXlh7uL7 30 | 3tsUwFcBTWBEGlbXFWF+H3z+GzI3FWgR6l0ATvHtbfp5ykyxNZj4bzPBZZpXSgLC 31 | KCntspPJNiEFR3B/SmGt7BsIv/iNdBcbPxKWGkk5TYKDEyrJhJGr3akRKsPLQmHH 32 | 7BHDImm/OTnk7NxonaOVzw+5l9VWW2fJ+Gncw1nn1TVCJ4o2GMNkPAS5Ocrrmyyi 33 | OJVqR69BkKMiU7F8Rr7ZoI9WZuto0frAL9XC8pExvc4xZJbiMuuVk/6t+fUH2S7n 34 | Om9uSEahsoNDx7oqkmEnfGOcsjF5oFldxg/drjW8a+2w 35 | -----END CERTIFICATE----- 36 | -------------------------------------------------------------------------------- /awesome-conference/src/public/index.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif; 3 | font-size: 13px; 4 | font-weight: 600; 5 | text-align: center; 6 | background: #275480; 7 | padding-bottom: 50px; 8 | } 9 | 10 | /* keep the webcam snapper video hidden */ 11 | camera { 12 | display: none; 13 | } 14 | 15 | /* Person picture and video cards */ 16 | 17 | .main { 18 | height: 70vh; 19 | } 20 | 21 | .people { 22 | height: 20vh; 23 | overflow-x: auto; 24 | overflow-y: hidden; 25 | } 26 | 27 | .person { 28 | background: rgb(94, 94, 94); /* same as grayscale respoke logo */ 29 | color: #33699E; 30 | border: 0; 31 | border-bottom: 3px solid #1C364E; 32 | width: 160px; 33 | height: 120px; 34 | display: inline-block !important; 35 | margin: 10px; 36 | border-top-left-radius: 40px; 37 | border-bottom-left-radius: 5px; 38 | border-top-right-radius: 5px; 39 | border-bottom-right-radius: 40px; 40 | overflow-x: hidden; 41 | overflow-y: hidden; 42 | } 43 | 44 | .videoSource { 45 | background: rgb(94, 94, 94); /* same as grayscale respoke logo */ 46 | color: #33699E; 47 | outline-color: orange; 48 | outline-style: solid; 49 | border: 0; 50 | border-bottom: 3px solid #1C364E; 51 | width: 160px; 52 | height: 120px; 53 | display: inline-block !important; 54 | margin: 10px; 55 | border-top-left-radius: 40px; 56 | border-bottom-left-radius: 5px; 57 | border-top-right-radius: 5px; 58 | border-bottom-right-radius: 40px; 59 | overflow-x: hidden; 60 | overflow-y: hidden; 61 | } 62 | 63 | .img, video { 64 | margin: 0; 65 | padding: 0; 66 | } 67 | img { 68 | filter: grayscale(100%); 69 | -webkit-filter: grayscale(100%); 70 | } 71 | video { 72 | border-top-left-radius: 40px; 73 | border-bottom-left-radius: 5px; 74 | border-top-right-radius: 5px; 75 | border-bottom-right-radius: 40px; 76 | } 77 | 78 | /* Footer */ 79 | 80 | .respoke, .github, .instructions { 81 | position: fixed; 82 | bottom: 8px; 83 | color: #ccc !important; 84 | text-decoration: none; 85 | } 86 | .github { 87 | left: 13px; 88 | } 89 | .respoke { 90 | right: 13px; 91 | } 92 | .instructions { 93 | right: 50%; 94 | margin-right: -100px; 95 | width: 200px; 96 | font-weight: normal; 97 | } -------------------------------------------------------------------------------- /awesome-conference/src/bootstrap.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const config = require('config'); 3 | const express = require('express'); 4 | const session = require('express-session'); 5 | const path = require('path'); 6 | const uuid = require('uuid'); 7 | const asterisk = require('./services/asterisk'); 8 | const conference = require('./services/conference'); 9 | const respoke = require('./services/respoke'); 10 | 11 | function boot() { 12 | return new Promise(resolve => { 13 | const app = express(); 14 | let respokeWrapper; 15 | 16 | app.set('x-powered-by', false); 17 | app.set('trust proxy', true); 18 | 19 | app.use(express.static(path.join(__dirname, 'public'))); 20 | 21 | app.use(session({ 22 | secret: uuid.v4(), 23 | cookie: {}, 24 | resave: false, 25 | saveUninitialized: false 26 | })); 27 | 28 | app.get('/token', (req, res, next) => { 29 | if (!req.query.endpointId) { 30 | let err = new Error(`Missing required query param 'endpointId'`); 31 | err.status = 400; 32 | next(err); 33 | } 34 | 35 | if (respokeWrapper) { 36 | respokeWrapper.register_endpoint(req.query.endpointId) 37 | .then( authToken => { 38 | res.json(authToken); 39 | }); 40 | } else { 41 | res.sendStatus(500); 42 | } 43 | }); 44 | 45 | app.use((req, res, next) => { 46 | let err = new Error(`Not Found: ${req.url}`); 47 | err.status = 404; 48 | next(err); 49 | }); 50 | 51 | const asteriskWrapper = new asterisk.AsteriskWrapper(config); 52 | asteriskWrapper.connect() 53 | .then(() => { 54 | return asteriskWrapper.provision(); 55 | }) 56 | .then(() => { 57 | respokeWrapper = new respoke.RespokeWrapper(config); 58 | let respokeClient; 59 | 60 | return respokeWrapper.connect() 61 | .then((_respokeClient) => { 62 | respokeClient = _respokeClient; 63 | 64 | return asteriskWrapper.connectConferenceApp(); 65 | }) 66 | .then(ariClient => { 67 | const conf = new conference.Conference(respokeClient, ariClient); 68 | 69 | resolve(app); 70 | }); 71 | }); 72 | }); 73 | } 74 | 75 | module.exports = { boot }; 76 | -------------------------------------------------------------------------------- /asterisk/build-asterisk.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | PROGNAME=$(basename $0) 4 | SOURCE_DIR="/usr/src/build" 5 | 6 | MENUSELECT_DISABLE=( 7 | app_talkdetect app_adsiprog app_alarmreceiver 8 | app_amd app_chanisavail app_dictate app_externalivr app_festival 9 | app_getcpeid app_ices app_image app_minivm app_morsecode app_mp3 10 | app_nbscat app_sms app_test app_url app_waitforring app_waitforsilence 11 | app_zapateller cdr_custom cdr_manager cdr_syslog cdr_sqlite3_custom 12 | cel_custom cel_manager cel_sqlite3_custom chan_iax2 chan_alsa 13 | chan_console chan_mgcp chan_oss chan_phone chan_sip chan_skinny 14 | chan_unistim func_audiohookinherit pbx_ael pbx_dundi pbx_realtime 15 | res_fax res_ael_share res_fax_spandsp res_phoneprov 16 | res_pjsip_phoneprov_provider BUILD_NATIVE CORE-SOUNDS-EN-GSM 17 | ) 18 | 19 | MENUSELECT_ENABLE=( 20 | BETTER_BACKTRACES res_endpoint_stats res_mwi_external res_stasis_mailbox 21 | res_ari_mailboxes codec_opus CORE-SOUNDS-EN-WAV CORE-SOUNDS-EN-ULAW 22 | EXTRA-SOUNDS-EN-WAV EXTRA-SOUNDS-EN-ULAW 23 | ) 24 | 25 | if test -z ${ASTERISK_VERSION}; then 26 | echo "${PROGNAME}: ASTERISK_VERSION required" >&2 27 | exit 1 28 | fi 29 | 30 | set -ex 31 | 32 | mkdir -p ${SOURCE_DIR}/asterisk 33 | mkdir -p ${SOURCE_DIR}/cache 34 | cd ${SOURCE_DIR}/asterisk 35 | 36 | # Build Asterisk 37 | curl -vsL http://downloads.asterisk.org/pub/telephony/asterisk/releases/asterisk-${ASTERISK_VERSION}.tar.gz | 38 | tar --strip-components 1 -xz 39 | 40 | # 1.5 jobs per core works out okay 41 | : ${JOBS:=$(( $(nproc) + $(nproc) / 2 ))} 42 | 43 | ./configure --with-resample --with-pjproject-bundled --with-externals-cache=${SOURCE_DIR}/cache 44 | make menuselect/menuselect menuselect-tree menuselect.makeopts 45 | 46 | for i in "${MENUSELECT_DISABLE[@]}"; do 47 | menuselect/menuselect --disable $i menuselect.makeopts 48 | done 49 | 50 | for i in "${MENUSELECT_ENABLE[@]}"; do 51 | menuselect/menuselect --enable $i menuselect.makeopts 52 | done 53 | 54 | make -j ${JOBS} all 55 | make install 56 | 57 | mkdir chan_respoke 58 | cd chan_respoke 59 | # Build chan_respoke 60 | curl -vsL https://github.com/respoke/chan_respoke/releases/download/${RESPOKE_VERSION}/chan_respoke-${RESPOKE_VERSION}.tar.gz | 61 | tar --strip-components 1 -xz 62 | make -j ${JOBS} 63 | make install 64 | cd .. 65 | 66 | # copy config files into place 67 | mkdir -p /etc/asterisk/ 68 | mkdir -p /etc/asterisk/keys 69 | cp -a ${SOURCE_DIR}/etc-asterisk/* /etc/asterisk/ 70 | cp -a ${SOURCE_DIR}/keys/* /etc/asterisk/keys 71 | 72 | chown -R asterisk:asterisk /var/*/asterisk 73 | chown -R asterisk:asterisk /etc/asterisk 74 | chmod a+r /etc/asterisk/keys 75 | chown -R asterisk:asterisk /usr/sbin/asterisk 76 | chown -R asterisk:asterisk /var/lib/asterisk/sounds 77 | chmod -R 750 /var/spool/asterisk 78 | -------------------------------------------------------------------------------- /asterisk/configs/musiconhold.conf: -------------------------------------------------------------------------------- 1 | ; 2 | ; Music on Hold -- Sample Configuration 3 | ; 4 | ;[general] 5 | ;cachertclasses=yes ; use 1 instance of moh class for all users who are using it, 6 | ; decrease consumable cpu cycles and memory 7 | ; disabled by default 8 | 9 | 10 | ; valid mode options: 11 | ; files -- read files from a directory in any Asterisk supported 12 | ; media format 13 | ; quietmp3 -- default 14 | ; mp3 -- loud 15 | ; mp3nb -- unbuffered 16 | ; quietmp3nb -- quiet unbuffered 17 | ; custom -- run a custom application (See examples below) 18 | 19 | ; ========= 20 | ; File-based (native) music on hold 21 | ; ========= 22 | ; 23 | ; This plays files directly from the specified directory, no external 24 | ; processes are required. Files are played in normal sorting order 25 | ; (same as a sorted directory listing), and no volume or other 26 | ; sound adjustments are available. If the file is available in 27 | ; the same format as the channel's codec, then it will be played 28 | ; without transcoding (same as Playback would do in the dialplan). 29 | ; Files can be present in as many formats as you wish, and the 30 | ; 'best' format will be chosen at playback time. 31 | ; 32 | ; The path specified can be either an absolute path (starts with '/'), 33 | ; or a relative path; relative paths are interpreted as being relative 34 | ; to the 'astdatalibdir' in asterisk.conf, which defaults to 35 | ; /var/lib/asterisk. 36 | ; 37 | ; NOTE: 38 | ; If you are not using "autoload" in modules.conf, then you 39 | ; must ensure that the format modules for any formats you wish 40 | ; to use are loaded _before_ res_musiconhold. If you do not do 41 | ; this, res_musiconhold will skip the files it is not able to 42 | ; understand when it loads. 43 | ; 44 | 45 | [default] 46 | mode=files 47 | directory=moh 48 | ; 49 | ;[native-random] 50 | ;mode=files 51 | ;directory=moh 52 | ;digit=# ; If this option is set for a class, then when callers are 53 | ; ; listening to music on hold, they can press this digit, and 54 | ; ; they will switch to listening to this music class. 55 | ;announcement=queue-thankyou ;If this option is set for a class, then 56 | ; ; when callers get put on hold, the specified sound will be 57 | ; ; be played to them. Also, if using modes that Asterisk 58 | ; ; controls the playlist for (files, mp3, etc), the same 59 | ; ; sound will also be played between MOH songs. 60 | ;sort=random ; Sort the files in random order 61 | 62 | ;[native-alphabetical] 63 | ;mode=files 64 | ;directory=moh 65 | ;sort=alpha ; Sort the files in alphabetical order. If this option is 66 | ; ; not specified, the sort order is undefined. 67 | 68 | ; ========= 69 | ; Other (non-native) playback methods 70 | ; ========= 71 | 72 | ;[manual] 73 | ;mode=custom 74 | ; Note that with mode=custom, a directory is not required, such as when reading 75 | ; from a stream. 76 | ;directory=/var/lib/asterisk/mohmp3 77 | ;application=/usr/bin/mpg123 -q -r 8000 -f 8192 -b 2048 --mono -s 78 | 79 | ;[ulawstream] 80 | ;mode=custom 81 | ;application=/usr/bin/streamplayer 192.168.100.52 888 82 | ;format=ulaw 83 | 84 | ; mpg123 on Solaris does not always exit properly; madplay may be a better 85 | ; choice 86 | ;[solaris] 87 | ;mode=custom 88 | ;directory=/var/lib/asterisk/mohmp3 89 | ;application=/site/sw/bin/madplay -Q -o raw:- --mono -R 8000 -a -12 90 | -------------------------------------------------------------------------------- /asterisk/keys/ca.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | Proc-Type: 4,ENCRYPTED 3 | DEK-Info: DES-EDE3-CBC,1155DE3A44A4E2CC 4 | 5 | LjO6P6vjBKeoGi/7SY6BYwiqCKgv2l5JKTvg/07Hsz8drsTj4VxtD5K3jrKETzqv 6 | rj/ahHPrI1UWa4UPCZkhgoaVmeIQDAA7N+7XNI8Z/U7lX6+BcchnGpUdEI7+Gt5T 7 | aMSOx+QUcUAxmTKo51IhORaMeu+BUKeShAmtn/14WS0ccaLLsRHpBszqRs0yxsSC 8 | RLpHwxMJSYzdCu70AlAt/LAa7nZLfYrcj8lbrTcWYGYtVSxnpKvK4s2so1HYsPVw 9 | s1Zj5LHSCSX08Bj6PAkVrSUPSKgvMI5JWmLiSZqBblNuQ2N73ypoxU0o7NWibER6 10 | BdVaxVmr9y35WZ7ljC5BaWTHAvYjv2SHue+LX+tqo20kmxX0nEpmtHwe/ZAPXVar 11 | 59TXFd6XTCqbHYcen3qGfAI/7nRCxvL+AsvenRoKFjq0g0kLzdDl3m60d/fFS6eJ 12 | mipcz3i41m2JAj9rUuYzaqjyvLzs0ZWYIMIOSxMOMZg9PZemD7HlCwhv51N3Zib8 13 | lllWMbhDABNB4Y1wwq0dTqbhV9I6O5vNe7/0AdlC7b1r49nXtWnVWA9n5TsIdcaO 14 | WnvM9jlru1ReOVx/FG2PgAGO9sU7t8wB+RHJN3c62wtWYQ4W0uSun8ZobF8Em1i/ 15 | AW44A03dz1k6tbnxPCZpVbPHk5pDjoRjk3+jzVkNUw+xwi7QkQxMP1HEMpGIoTaY 16 | AqO9b3mGicS+bWIo6++3Q7Nttb0S3iqbS9p3Z/2RWKnXYSyjPXKndeMBz0zYeUQM 17 | PWXYZIjIq8B7thrKsqUHHEaL2f7en3pzckRSK8F+GC8wp2JY47eTaR9AG5s6fd8m 18 | RI6rPq1JW0ysZvTymD6yrBjLltGV6O9W6D6kxjXfleUEbQmC42j860xUacEHJXWM 19 | dG1RtUr2rDOtLj2mzR7+EWNdt5SACd76XMTiIGTpsf/D4j7m9oqXEjLIkAtw5hrP 20 | bhj26/MaF922wIgmPD+JbEkL7it8hy05kYcjpMe8P0TCsPhFFMo2H/hf4fQ0viWY 21 | oWVt99e0iiumSGYFJluxwGx0tljCVkmBnERPUmvhRmMLwF/k5NrcMm7dphli+gTD 22 | gi/NXBh5Ccan8CXQFTbJT11rnTUthQO0/EtBt1zk0Q0knVWJjggmMC7alpkmnJMP 23 | X+R/jLZE1nK++FgC2Majjk+GE9eoDaVdEJpUK68Or+tUyN2pcWwgrcudQXkQzkIi 24 | 1LXq+Uk16naO/tP0yFoaJHLRIGujsCeOhL9z1BMxoP/cwuwFb9BhItoW4uElvzej 25 | uGCGfLlISTtZ91ZLZJW8KQk21UOJF/EfCW7ZrAWWc8PezjtXGIWbu/XTnlsrIgaP 26 | dOzKv8yfhK+e3sArRA0Wj8HTTAvk00ghE1713+mH12BienQih6lVYYRWWSFx24qZ 27 | OeztCLZtrPAK6c7+UtLH7VvqwqgmOrWxT8cz2B+bGOHs8QpKYgsR3hrOmTiFn6sy 28 | G/6eG9Ph1kydVr7OLpPqso/av8+5Wl94ocnmkHTCRNm22BhKKyntbOcA75LW/5VO 29 | zFXiK2M9gT89RXaP2eYikcp5mHN7Kqo0iG6atka7dL3eUvVeGTtYm1yrbrwHw5n8 30 | nmOVccxgbtF1E5ziUxx4soZ3PNSc13/zaIHqu7KkNnSftnH0R4d0d0KxKbHp0d4Z 31 | ZGZUd6WF81Gz2N4ut5+GZW2WoszwI0jp8yI04ZX4jqPWWwGPQjRtbCIqqo+gOoNB 32 | 3z22pDFHfF2BJiQ7lVZfJVJTKSy9aLxpykBLXb8e/IP5zkS7ut0bThF9zepiZaym 33 | sUKO/yXTG8Is82WvckdrWwJv1ld0nMwnQHlnls9aHBds3OkYYn3Zhmg0RygVmQZD 34 | Iv/P/1AULB9gBxAxXF+RA0fgfH6UU81Ao2lcPGxwW6YXWV0lbb/U/tlafhTZpMcA 35 | wvrg9djx+ua1GvTtRg/MNhOaHgwM7nhf1Pga73aROpZRZJs/nH1CxtWOpzaLRG74 36 | 4aHW9v0zzYXHf428FZBCh0CbT18GNFbGjBeq05DpqHjJRrJb3E1XKh9x+NmnmV4o 37 | UgVGNgOf3A0zcSBwC4dK5Mpzy17WvSkscZbpjgj6ApzajkjRbHVsqOaWZl1NblR/ 38 | fC1kMUupYLvkP2AssC1NGMt/HF4eAB2FMnnc8SkPoPnYPXALUtwEEKLKVsrS+Xo4 39 | gymzQAJkO0gxU7gtdKTQE71jaCq05WMTdBQyRcJ+bIY3+97Ri1x7BF9R3Fli0VPl 40 | 7xUS/CWHbmCNzQ5VRxlEGk93ztEgO+ibKu05kDkBCbU0En4zKmmvZkXCas3tjgLs 41 | +Xk2h4Du/wNToCNoUI8pP8rOiYy/+l6PzNNKTeVfbpyAqwKBd+ZvaJx1Q1v0mpCm 42 | gcM+Gl8t291txyrIVtMpTv/0oaNLYwbjMlIYxv+nDWYctJvzHPqyCKfx6gqb+EPc 43 | Ii8Qv/8j+ozgZJIIeoVfDQYtw6FpC7+UkOMxmq9WGjvOf4jRJSFL8NiGpQICDg3r 44 | ai7T22ei/9IO6eL8QyQTDpTzP6FzQXPOrLsJjn4a+8HbaahWrYSSm5DUSTbnwXb0 45 | Uzec4vxyY4IEaMxkcVGY558+1vvJFYebZVOjCzYVmj4i43DwgVJCMRnJD7njceF4 46 | D1k0BMPEt50Cn/aNKCOZZAiOiEuMMRBYqFkM87bDLRRakxCpSzpe9248ueJD7YD0 47 | 8qoIIjj+FKinu/JhVcWOL4NPfp1VqLkGQciv4IpO2vGMQnEjKtaxWKdAoIR9fzCQ 48 | NivT9+nlogo0xXMSJWDyq7G5oBsldE8fv8rBZcMepsehesUJ7ReiNPwWK55LCOGb 49 | DaqOxlY10ViB2F/jUZ6EdIqfmn4tGI1sH1jVFdH5tpMyH8+g3w7Oi68A+OVWoiw/ 50 | AA24Ii3a1QaSserDvD+Z9lrm0F0yprd94c7HBMuNmUabwuZ8XygzEK9yiYafE3Ae 51 | n9hN6gYvhovVyeX0zjmklNUBQTfr4+Dia6V73q6TrSLmcsKfz80vf/AFS0M5tth8 52 | Xusxw+fYz22zd6slowRbJlh9GjPJO1D7sb1VId5DIrLED6WB3avRcszXz+1ne6Cj 53 | Cxb8hL3Seogm0+rXFhD1kGMFU8+QBsUphEFqy38Wz+vAbNrp6tfKLkHI7nJEN+/q 54 | -----END RSA PRIVATE KEY----- 55 | -------------------------------------------------------------------------------- /asterisk/configs/http.conf: -------------------------------------------------------------------------------- 1 | ; 2 | ; Asterisk Builtin mini-HTTP server 3 | ; 4 | ; 5 | ; Note about Asterisk documentation: 6 | ; If Asterisk was installed from a tarball, then the HTML documentation should 7 | ; be installed in the static-http/docs directory which is 8 | ; (/var/lib/asterisk/static-http/docs) on linux by default. If the Asterisk 9 | ; HTTP server is enabled in this file by setting the "enabled", "bindaddr", 10 | ; and "bindport" options, then you should be able to view the documentation 11 | ; remotely by browsing to: 12 | ; http://:/static/docs/index.html 13 | ; 14 | [general] 15 | ; 16 | ; Whether HTTP/HTTPS interface is enabled or not. Default is no. 17 | ; This also affects manager/rawman/mxml access (see manager.conf) 18 | ; 19 | enabled=yes 20 | ; 21 | ; Address to bind to, both for HTTP and HTTPS. You MUST specify 22 | ; a bindaddr in order for the HTTP server to run. There is no 23 | ; default value. 24 | ; 25 | bindaddr=0.0.0.0 26 | ; 27 | ; Port to bind to for HTTP sessions (default is 8088) 28 | ; 29 | bindport=8088 30 | ; 31 | ; Prefix allows you to specify a prefix for all requests 32 | ; to the server. The default is blank. If uncommented 33 | ; all requests must begin with /asterisk 34 | ; 35 | ;prefix=asterisk 36 | ; 37 | ; sessionlimit specifies the maximum number of httpsessions that will be 38 | ; allowed to exist at any given time. (default: 100) 39 | ; 40 | ;sessionlimit=100 41 | ; 42 | ; session_inactivity specifies the number of milliseconds to wait for 43 | ; more data over the HTTP connection before closing it. 44 | ; 45 | ; Default: 30000 46 | ;session_inactivity=30000 47 | ; 48 | ; session_keep_alive specifies the number of milliseconds to wait for 49 | ; the next HTTP request over a persistent connection. 50 | ; 51 | ; Set to 0 to disable persistent HTTP connections. 52 | ; Default: 15000 53 | ;session_keep_alive=15000 54 | ; 55 | ; Whether Asterisk should serve static content from static-http 56 | ; Default is no. 57 | ; 58 | enablestatic=yes 59 | ; 60 | ; Redirect one URI to another. This is how you would set a 61 | ; default page. 62 | ; Syntax: redirect= 63 | ; For example, if you are using the Asterisk-gui, 64 | ; it is convenient to enable the following redirect: 65 | ; 66 | ;redirect = / /static/config/index.html 67 | ; 68 | ; HTTPS support. In addition to enabled=yes, you need to 69 | ; explicitly enable tls, define the port to use, 70 | ; and have a certificate somewhere. 71 | ;tlsenable=yes ; enable tls - default no. 72 | ;tlsbindaddr=0.0.0.0:8089 ; address and port to bind to - default is bindaddr and port 8089. 73 | ; 74 | ;tlscertfile= ; path to the certificate file (*.pem) only. 75 | ;tlsprivatekey= ; path to private key file (*.pem) only. 76 | ; If no path is given for tlscertfile or tlsprivatekey, default is to look in current 77 | ; directory. If no tlsprivatekey is given, default is to search tlscertfile for private key. 78 | ; 79 | ; To produce a certificate you can e.g. use openssl. This places both the cert and 80 | ; private in same .pem file. 81 | ; openssl req -new -x509 -days 365 -nodes -out /tmp/foo.pem -keyout /tmp/foo.pem 82 | ; 83 | ; The post_mappings section maps URLs to real paths on the filesystem. If a 84 | ; POST is done from within an authenticated manager session to one of the 85 | ; configured POST mappings, then any files in the POST will be placed in the 86 | ; configured directory. 87 | ; 88 | ;[post_mappings] 89 | ; 90 | ; NOTE: You need a valid HTTP AMI mansession_id cookie with the manager 91 | ; config permission to POST files. 92 | ; 93 | ; In this example, if the prefix option is set to "asterisk", then using the 94 | ; POST URL: /asterisk/uploads will put files in /var/lib/asterisk/uploads/. 95 | ;uploads = /var/lib/asterisk/uploads/ 96 | ; 97 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Asterisk "Awesome Conference" 2 | 3 | An awesome conference, consisting of: 4 | * An [Asterisk](http://asterisk.org)/ARI driven video conferencing application 5 | * A [Respoke](https://www.respoke.io) driven WebRTC client 6 | 7 | To run the awesome conference, you will need a valid [Respoke](https://www.respoke.io) account. 8 | 9 | ![A picture of people in an awesome conference. And me with a beard. No shave november, yo.](https://github.com/matt-jordan/awesome-conference/blob/master/awesome-conference.png) 10 | 11 | ## Asterisk/ARI 12 | 13 | Asterisk is deployed in a Docker container bound to the host network. The configuration of Asterisk is static, and all relevant configuration bits as well as control of the ARI application is done by the remote application implemented in `awesome-conference`. 14 | 15 | The ARI application will toss all inbound Respoke WebRTC channels into a mixing bridge. The mixing bridge, by default, will relay the active speaker's video stream to the other participants. If a signal is received from a participant to lock onto a particular video source, then the video stream in the bridge will be set to that participant. 16 | 17 | ## Respoke 18 | 19 | The Respoke client displays the video stream from the conference, along with small pictures of all of the participants in the conference. The pictures of the participants update periodically. If a picture is selected, the video stream is switched to that participant. 20 | 21 | # Configuration 22 | 23 | Because Docker is used to build and run the services, all configuration of the services is done via environment variables passed into the Docker container. Configuration for your deployment should be done in the `docker-compose.yml` file. 24 | 25 | ## Mandatory Configuration 26 | 27 | ### `awesome-conference` 28 | 29 | * `APP_SECRET`: Your Respoke App Secret 30 | * `APP_ID`: Your Respoke App ID 31 | * `ENDPOINT_ROLE_ID`: A role ID that you've created for you application. The role should have full Global Group Permissions and full Event permissions. 32 | 33 | ## Optional Configuration 34 | 35 | ### `asterisk` 36 | 37 | * `APPLICATION_NAME`: The name of the Respoke application. 38 | * `ARI_PASSWORD`: Our ARI password. Change this if you want to be secure, leave it as is to live dangerously. 39 | * `RESPOKE_ENDPOINT`: The name of the Respoke endpoint Asterisk uses that other clients will establish a call to. 40 | 41 | ### `awesome-conference` 42 | 43 | * `PORT`: The port the conference runs on. Defaults to `3000`. 44 | * `HOST`: The external IP address that we advertise. This should be provided automatically by the `up.sh` script. 45 | * `APPLICATION_NAME`: The name of the Respoke application. 46 | * `ARI_PASSWORD`: Our ARI password. Change this if you want to be secure, leave it as is to live dangerously. 47 | * `RESPOKE_ENDPOINT`: The name of the Respoke endpoint Asterisk uses that other clients will establish a call to. 48 | 49 | # Building/Running 50 | 51 | This project uses [Docker-Compose](https://docs.docker.com/compose/) for fun, profit, and/or pain. Because we have to pass in the determined external IP address, it is recommended that you use the `up.sh` script to invoke the services. 52 | 53 | ``` 54 | $ ./up.sh 55 | ``` 56 | 57 | # Notes 58 | 59 | * The project assumes your Respoke application is in development mode, despite asking for a `ENDPOINT_ROLE_ID`. That should probably be fixed. 60 | * My CSS skills are abysmal. In my defense, I copied most of it from Jeff Parrish's awesome [YoDude](https://github.com/respoke/yodude). 61 | * It should go without saying that this is a demo application. Take everything with a healthy dose of skepticism. You should also assume that just about everything is not as secure as it should be. 62 | * The certificate used is self-signed. 63 | * New participants won't be informed of the current video source. Making that function is an exercise for the reader. 64 | * If your picture box remains gray, then for some reason, the client application couldn't grab the webcam. Refresh. 65 | * No video is provided when there is only one participant. 66 | -------------------------------------------------------------------------------- /awesome-conference/src/services/conference.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const log = require('../lib/log'); 4 | 5 | /** 6 | * Our super awesome conference 7 | */ 8 | class Conference { 9 | 10 | constructor(respokeClient, ariClient) { 11 | let endpointsToChannels = {}; 12 | let channelsToEndpoints = {}; 13 | 14 | respokeClient.groups.join({groupId: 'conference'}); 15 | respokeClient.on('pubsub', (message) => { 16 | if (message.message.messageType !== 'updateVideoSource') { 17 | return; 18 | } 19 | 20 | log.debug({respokeMessage: message.message}, 'Received'); 21 | 22 | const endpointId = message.message.endpointId; 23 | const channel = endpointsToChannels[endpointId]; 24 | 25 | if (!channel) { 26 | log.warn({endpointId}, 'Unable to find channel'); 27 | return; 28 | } 29 | 30 | ariClient.bridges.list() 31 | .then(bridges => { 32 | const bridge = bridges.filter(candidate => candidate.name === 'awesome-conference'); 33 | 34 | if (!bridge) { 35 | return Promise.reject(); 36 | } 37 | 38 | // There should be only one 39 | return bridge[0].setVideoSource({channelId: channel}); 40 | }); 41 | }); 42 | 43 | function bridge_video_source_change(event, bridge) { 44 | const endpointId = channelsToEndpoints[bridge.video_source_id]; 45 | 46 | if (!endpointId) { 47 | return; 48 | } 49 | 50 | respokeClient.groups.publish({ 51 | groupId: 'conference', 52 | message: { messageType: 'videoSourceSet', endpointId } 53 | }); 54 | log.info({bridge, endpointId}, `Video source is now ${endpointId} (${bridge.video_source_id})`); 55 | } 56 | 57 | function stasis_start_handler(event, channel) { 58 | channel.answer() 59 | .then(() => { 60 | return channel.getChannelVar({ 61 | variable: 'CHANNEL(remote)' 62 | }).then(variable => { 63 | endpointsToChannels[variable.value] = channel.id; 64 | channelsToEndpoints[channel.id] = variable.value; 65 | log.info(`Participant ${variable.value} is ${channel.id}`); 66 | }); 67 | }) 68 | .then(() => { 69 | // Create or obtain the one and only conference bridge 70 | return ariClient.bridges.list(); 71 | }) 72 | .then(bridges => { 73 | const bridge = bridges.filter(candidate => candidate.name === 'awesome-conference'); 74 | 75 | if (bridge.length === 0) { 76 | log.info('Creating our awesome conference'); 77 | 78 | return ariClient.bridges.create({ 79 | type: 'mixing,dtmf_events', 80 | name: 'awesome-conference' 81 | }); 82 | } 83 | return Promise.resolve(bridge[0]); 84 | }) 85 | .then(bridge => { 86 | // Join the bridge! 87 | log.info({ channel, bridge }, 'Joining bridge'); 88 | return bridge.addChannel({ channel: channel.id }); 89 | }); 90 | } 91 | 92 | function stasis_end_handler(event, channel) { 93 | log.info({channel}, 'Left app/bridge'); 94 | const endpointId = channelsToEndpoints[channel.id]; 95 | delete channelsToEndpoints[channel.id]; 96 | delete endpointsToChannels[endpointId]; 97 | } 98 | 99 | ariClient.on('StasisStart', stasis_start_handler); 100 | ariClient.on('StasisEnd', stasis_end_handler); 101 | ariClient.on('BridgeVideoSourceChanged', bridge_video_source_change); 102 | } 103 | } 104 | 105 | module.exports.Conference = Conference; 106 | -------------------------------------------------------------------------------- /asterisk/configs/asterisk.conf: -------------------------------------------------------------------------------- 1 | [directories](!) 2 | astetcdir => /etc/asterisk 3 | astmoddir => /usr/lib/asterisk/modules 4 | astvarlibdir => /var/lib/asterisk 5 | astdbdir => /var/lib/asterisk 6 | astkeydir => /var/lib/asterisk 7 | astdatadir => /var/lib/asterisk 8 | astagidir => /var/lib/asterisk/agi-bin 9 | astspooldir => /var/spool/asterisk 10 | astrundir => /var/run/asterisk 11 | astlogdir => /var/log/asterisk 12 | astsbindir => /usr/sbin 13 | 14 | [options] 15 | ;verbose = 3 16 | ;debug = 3 17 | ;alwaysfork = yes ; Same as -F at startup. 18 | ;nofork = yes ; Same as -f at startup. 19 | ;quiet = yes ; Same as -q at startup. 20 | ;timestamp = yes ; Same as -T at startup. 21 | ;execincludes = yes ; Support #exec in config files. 22 | ;console = yes ; Run as console (same as -c at startup). 23 | ;highpriority = yes ; Run realtime priority (same as -p at 24 | ; startup). 25 | ;initcrypto = yes ; Initialize crypto keys (same as -i at 26 | ; startup). 27 | ;nocolor = yes ; Disable console colors. 28 | ;dontwarn = yes ; Disable some warnings. 29 | ;dumpcore = yes ; Dump core on crash (same as -g at startup). 30 | ;languageprefix = yes ; Use the new sound prefix path syntax. 31 | ;systemname = my_system_name ; Prefix uniqueid with a system name for 32 | ; Global uniqueness issues. 33 | autosystemname = yes ; Automatically set systemname to hostname, 34 | ; uses 'localhost' on failure, or systemname if 35 | ; set. 36 | ;mindtmfduration = 80 ; Set minimum DTMF duration in ms (default 80 ms) 37 | ; If we get shorter DTMF messages, these will be 38 | ; changed to the minimum duration 39 | ;maxcalls = 10 ; Maximum amount of calls allowed. 40 | ;maxload = 0.9 ; Asterisk stops accepting new calls if the 41 | ; load average exceed this limit. 42 | ;maxfiles = 1000 ; Maximum amount of openfiles. 43 | ;minmemfree = 1 ; In MBs, Asterisk stops accepting new calls if 44 | ; the amount of free memory falls below this 45 | ; watermark. 46 | ;cache_record_files = yes ; Cache recorded sound files to another 47 | ; directory during recording. 48 | ;record_cache_dir = /tmp ; Specify cache directory (used in conjunction 49 | ; with cache_record_files). 50 | ;transmit_silence = yes ; Transmit silence while a channel is in a 51 | ; waiting state, a recording only state, or 52 | ; when DTMF is being generated. Note that the 53 | ; silence internally is generated in raw signed 54 | ; linear format. This means that it must be 55 | ; transcoded into the native format of the 56 | ; channel before it can be sent to the device. 57 | ; It is for this reason that this is optional, 58 | ; as it may result in requiring a temporary 59 | ; codec translation path for a channel that may 60 | ; not otherwise require one. 61 | ;transcode_via_sln = yes ; Build transcode paths via SLINEAR, instead of 62 | ; directly. 63 | runuser = asterisk ; The user to run as. 64 | rungroup = asterisk ; The group to run as. 65 | ;lightbackground = yes ; If your terminal is set for a light-colored 66 | ; background. 67 | ;forceblackbackground = yes ; Force the background of the terminal to be 68 | ; black, in order for terminal colors to show 69 | ; up properly. 70 | defaultlanguage = en ; Default language 71 | documentation_language = en_US ; Set the language you want documentation 72 | ; displayed in. Value is in the same format as 73 | ; locale names. 74 | ;hideconnect = yes ; Hide messages displayed when a remote console 75 | ; connects and disconnects. 76 | ;lockconfdir = no ; Protect the directory containing the 77 | ; configuration files (/etc/asterisk) with a 78 | ; lock. 79 | ;stdexten = gosub ; How to invoke the extensions.conf stdexten. 80 | ; macro - Invoke the stdexten using a macro as 81 | ; done by legacy Asterisk versions. 82 | ; gosub - Invoke the stdexten using a gosub as 83 | ; documented in extensions.conf.sample. 84 | ; Default gosub. 85 | ;live_dangerously = no ; Enable the execution of 'dangerous' dialplan 86 | ; functions from external sources (AMI, 87 | ; etc.) These functions (such as SHELL) are 88 | ; considered dangerous because they can allow 89 | ; privilege escalation. 90 | ; Default no 91 | 92 | ; Changing the following lines may compromise your security. 93 | ;[files] 94 | ;astctlpermissions = 0660 95 | ;astctlowner = root 96 | ;astctlgroup = apache 97 | ;astctl = asterisk.ctl 98 | -------------------------------------------------------------------------------- /awesome-conference/src/services/asterisk.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const log = require('../lib/log'); 4 | const ari = require('ari-client'); 5 | 6 | /** 7 | * Class for managing an Asterisk instance 8 | */ 9 | class AsteriskWrapper { 10 | 11 | /** 12 | * Manage our Asterisk instance 13 | * 14 | * @param {object} [config] 15 | */ 16 | constructor(config) { 17 | this.url = `http://${config.asterisk.host}:8088`; 18 | this.username = config.asterisk.username; 19 | this.password = config.asterisk.password; 20 | this.appname = config.asterisk.application; 21 | this.app_secret = config.respoke.app_secret; 22 | this.endpoint = config.respoke.endpoint; 23 | this.client = undefined; 24 | } 25 | 26 | /** 27 | * Connect to the Asterisk instance 28 | * 29 | * @returns {Promise.} 30 | */ 31 | connect() { 32 | const that = this; 33 | 34 | if (this.client) { 35 | return Promise.reject('Client already connected'); 36 | } 37 | 38 | log.debug({ url: this.url }, 'Connecting to Asterisk'); 39 | 40 | return ari.connect(this.url, this.username, this.password) 41 | .then(_client => { 42 | that.client = _client; 43 | return _client; 44 | }); 45 | } 46 | 47 | /** 48 | * Set up the Asterisk instance's Respoke information 49 | * 50 | * @returns {Promise} 51 | */ 52 | provision() { 53 | if (!this.client) { 54 | return Promise.reject('Not connected to Asterisk!'); 55 | } 56 | 57 | return this.client.asterisk.updateObject({ 58 | configClass: 'res_respoke', 59 | objectType: 'app', 60 | id: `${this.appname}`, 61 | fields: [ 62 | { attribute: 'app_secret', value: `${this.app_secret}`} 63 | ] 64 | }).then(() => { 65 | return this.client.asterisk.updateObject({ 66 | configClass: 'res_respoke', 67 | objectType: 'endpoint', 68 | id: `${this.endpoint}`, 69 | fields: [ 70 | { attribute: 'app', value: `${this.appname}` }, 71 | { attribute: 'context', value: 'inbound' }, 72 | { attribute: 'transport', value: `${this.endpoint}` }, 73 | { attribute: 'turn', value: 'true' }, 74 | { attribute: 'allow', value: '!all,opus,ulaw,h264' }, 75 | { attribute: 'dtls_verify', value: 'no' }, 76 | { attribute: 'dtls_cert_file', value: '/etc/asterisk/keys/asterisk.pem' }, 77 | { attribute: 'dtls_ca_file', value: '/etc/asterisk/keys/ca.crt' }, 78 | { attribute: 'dtls_setup', value: 'actpass' } 79 | ]}); 80 | }).then(() => { 81 | return this.client.asterisk.updateObject({ 82 | configClass: 'res_respoke', 83 | objectType: 'endpoint', 84 | id: 'anonymous', 85 | fields: [ 86 | { attribute: 'app', value: `${this.appname}` }, 87 | { attribute: 'context', value: 'inbound' }, 88 | { attribute: 'transport', value: `${this.endpoint}` }, 89 | { attribute: 'turn', value: 'true' }, 90 | { attribute: 'allow', value: '!all,opus,ulaw,h264' }, 91 | { attribute: 'dtls_verify', value: 'no' }, 92 | { attribute: 'dtls_cert_file', value: '/etc/asterisk/keys/asterisk.pem' }, 93 | { attribute: 'dtls_ca_file', value: '/etc/asterisk/keys/ca.crt' }, 94 | { attribute: 'dtls_setup', value: 'actpass' }, 95 | { attribute: 'register', value: 'false' } 96 | ]}); 97 | }); 98 | } 99 | 100 | /** 101 | * Connect the Conferencing Application 102 | * 103 | * @returns {Promise} 104 | */ 105 | connectConferenceApp() { 106 | const that = this; 107 | 108 | if (!this.client) { 109 | return Promise.reject('Not connected to Asterisk!'); 110 | } 111 | 112 | this.client.on('WebSocketMaxRetries', err => { 113 | log.fatal({ err }, 'ARI Disconnected'); 114 | // Exit so that the whole process will reconnect 115 | process.exit(1); 116 | }); 117 | this.client.on('WebSocketReconnecting', err => { 118 | log.warn({ err }, 'WebSocket Reconnecting'); 119 | }); 120 | this.client.on('WebSocketConnected', err => { 121 | log.info({ err }, 'WebSocket Connected'); 122 | }); 123 | 124 | return this.client.start([ this.appname ]) 125 | .then(() => { 126 | log.info(`ARI application ${this.appname} registered`); 127 | 128 | return this.client; 129 | }); 130 | } 131 | 132 | } 133 | 134 | module.exports.AsteriskWrapper = AsteriskWrapper; 135 | -------------------------------------------------------------------------------- /asterisk/configs/logger.conf: -------------------------------------------------------------------------------- 1 | ; 2 | ; Logging Configuration 3 | ; 4 | ; In this file, you configure logging to files or to 5 | ; the syslog system. 6 | ; 7 | ; "logger reload" at the CLI will reload configuration 8 | ; of the logging system. 9 | 10 | [general] 11 | ; 12 | ; Customize the display of debug message time stamps 13 | ; this example is the ISO 8601 date format (yyyy-mm-dd HH:MM:SS) 14 | ; 15 | ; see strftime(3) Linux manual for format specifiers. Note that there is also 16 | ; a fractional second parameter which may be used in this field. Use %1q 17 | ; for tenths, %2q for hundredths, etc. 18 | ; 19 | ;dateformat=%F %T ; ISO 8601 date format 20 | ;dateformat=%F %T.%3q ; with milliseconds 21 | ; 22 | ; 23 | ; This makes Asterisk write callids to log messages 24 | ; (defaults to yes) 25 | ;use_callids = no 26 | ; 27 | ; This appends the hostname to the name of the log files. 28 | ;appendhostname = yes 29 | ; 30 | ; This determines whether or not we log queue events to a file 31 | ; (defaults to yes). 32 | ;queue_log = no 33 | ; 34 | ; Determines whether the queue_log always goes to a file, even 35 | ; when a realtime backend is present (defaults to no). 36 | ;queue_log_to_file = yes 37 | ; 38 | ; Set the queue_log filename 39 | ; (defaults to queue_log) 40 | ;queue_log_name = queue_log 41 | ; 42 | ; When using realtime for the queue log, use GMT for the timestamp 43 | ; instead of localtime. The default of this option is 'no'. 44 | ;queue_log_realtime_use_gmt = yes 45 | ; 46 | ; Log rotation strategy: 47 | ; none: Do not perform any logrotation at all. You should make 48 | ; very sure to set up some external logrotate mechanism 49 | ; as the asterisk logs can get very large, very quickly. 50 | ; sequential: Rename archived logs in order, such that the newest 51 | ; has the highest sequence number [default]. When 52 | ; exec_after_rotate is set, ${filename} will specify 53 | ; the new archived logfile. 54 | ; rotate: Rotate all the old files, such that the oldest has the 55 | ; highest sequence number [this is the expected behavior 56 | ; for Unix administrators]. When exec_after_rotate is 57 | ; set, ${filename} will specify the original root filename. 58 | ; timestamp: Rename the logfiles using a timestamp instead of a 59 | ; sequence number when "logger rotate" is executed. 60 | ; When exec_after_rotate is set, ${filename} will 61 | ; specify the new archived logfile. 62 | ;rotatestrategy = rotate 63 | ; 64 | ; Run a system command after rotating the files. This is mainly 65 | ; useful for rotatestrategy=rotate. The example allows the last 66 | ; two archive files to remain uncompressed, but after that point, 67 | ; they are compressed on disk. 68 | ; 69 | ; exec_after_rotate=gzip -9 ${filename}.2 70 | ; 71 | ; 72 | ; For each file, specify what to log. 73 | ; 74 | ; For console logging, you set options at start of 75 | ; Asterisk with -v for verbose and -d for debug 76 | ; See 'asterisk -h' for more information. 77 | ; 78 | ; Directory for log files is configures in asterisk.conf 79 | ; option astlogdir 80 | ; 81 | [logfiles] 82 | ; 83 | ; Format is "filename" and then "levels" of debugging to be included: 84 | ; debug 85 | ; notice 86 | ; warning 87 | ; error 88 | ; verbose() 89 | ; dtmf 90 | ; fax 91 | ; security 92 | ; 93 | ; Special filename "console" represents the root console 94 | ; 95 | ; Filenames can either be relative to the standard Asterisk log directory 96 | ; (see 'astlogdir' in asterisk.conf), or absolute paths that begin with 97 | ; '/'. 98 | ; 99 | ; Verbose takes an optional argument, in the form of an integer level. 100 | ; Verbose messages with higher levels will not be logged to the file. If 101 | ; the verbose level is not specified, it will log verbose messages following 102 | ; the current level of the root console. 103 | ; 104 | ; Special level name "*" means all levels, even dynamic levels registered 105 | ; by modules after the logger has been initialized (this means that loading 106 | ; and unloading modules that create/remove dynamic logger levels will result 107 | ; in these levels being included on filenames that have a level name of "*", 108 | ; without any need to perform a 'logger reload' or similar operation). 109 | ; Note that there is no value in specifying both "*" and specific level names 110 | ; for a filename; the "*" level means all levels. The only exception is if 111 | ; you need to specify a specific verbose level. e.g, "verbose(3),*". 112 | ; 113 | ; We highly recommend that you DO NOT turn on debug mode if you are simply 114 | ; running a production system. Debug mode turns on a LOT of extra messages, 115 | ; most of which you are unlikely to understand without an understanding of 116 | ; the underlying code. Do NOT report debug messages as code issues, unless 117 | ; you have a specific issue that you are attempting to debug. They are 118 | ; messages for just that -- debugging -- and do not rise to the level of 119 | ; something that merit your attention as an Asterisk administrator. Debug 120 | ; messages are also very verbose and can and do fill up logfiles quickly; 121 | ; this is another reason not to have debug mode on a production system unless 122 | ; you are in the process of debugging a specific issue. 123 | ; 124 | ;debug => debug 125 | ;security => security 126 | console => notice,warning,error 127 | ;console => notice,warning,error,debug 128 | messages => notice,warning,error 129 | ;full => notice,warning,error,debug,verbose,dtmf,fax 130 | 131 | ;syslog keyword : This special keyword logs to syslog facility 132 | ; 133 | ;syslog.local0 => notice,warning,error 134 | ; 135 | -------------------------------------------------------------------------------- /asterisk/configs/modules.conf: -------------------------------------------------------------------------------- 1 | ; 2 | ; Asterisk configuration file 3 | ; 4 | ; Module Loader configuration file 5 | ; 6 | 7 | [modules] 8 | 9 | ; NOTE: 10 | ; We are commenting out autoload=yes here, as it tends to make a system 11 | ; less "clean". If you want more modules, uncomment this. 12 | ; autoload=yes 13 | 14 | ; Respoke 15 | load => chan_respoke 16 | load => res_respoke 17 | load => res_respoke_session 18 | load => res_socket_io 19 | load => res_socket_io_websocket 20 | load => res_respoke_endpoint_identifier_from 21 | load => res_respoke_endpoint_identifier_anonymous 22 | load => func_respoke_endpoint 23 | load => func_respoke_metadata 24 | 25 | 26 | ; Applications 27 | load => app_playback 28 | load => app_dial 29 | load => app_echo 30 | load => app_exec 31 | load => app_read 32 | load => app_stack 33 | load => app_stasis 34 | load => app_userevent 35 | load => app_verbose 36 | load => res_agi 37 | 38 | ; Bridges 39 | load => bridge_builtin_features 40 | load => bridge_builtin_interval_features 41 | load => bridge_holding 42 | load => bridge_native_rtp 43 | load => bridge_simple 44 | load => bridge_softmix 45 | 46 | ; Channel Drivers 47 | load => chan_bridge_media 48 | load => chan_pjsip 49 | 50 | ; Codecs 51 | load => codec_a_mu 52 | load => codec_adpcm 53 | load => codec_alaw 54 | load => codec_g722 55 | load => codec_g726 56 | load => codec_gsm 57 | load => codec_ilbc 58 | load => codec_lpc10 59 | load => codec_resample 60 | load => codec_ulaw 61 | load => codec_opus 62 | 63 | ; Formats 64 | load => format_g719 65 | load => format_g723 66 | load => format_g726 67 | load => format_g729 68 | load => format_gsm 69 | load => format_h263 70 | load => format_h264 71 | load => format_ilbc 72 | load => format_pcm 73 | load => format_siren14 74 | load => format_siren7 75 | load => format_sln 76 | load => format_wav 77 | load => format_wav_gsm 78 | load => format_ogg_opus 79 | 80 | ; Functions 81 | load => func_base64 82 | load => func_callerid 83 | load => func_channel 84 | load => func_config 85 | ;load => func_curl 86 | load => func_cut 87 | load => func_db 88 | load => func_devstate 89 | load => func_dialplan 90 | load => func_env 91 | load => func_extstate 92 | load => func_global 93 | load => func_groupcount 94 | load => func_hangupcause 95 | load => func_jitterbuffer 96 | load => func_logic 97 | load => func_math 98 | load => func_md5 99 | load => func_module 100 | load => func_periodic_hook 101 | load => func_pjsip_aor 102 | load => func_pjsip_contact 103 | load => func_pjsip_endpoint 104 | load => func_presencestate 105 | load => func_rand 106 | load => func_realtime 107 | load => func_sha1 108 | load => func_sorcery 109 | load => func_sprintf 110 | load => func_srv 111 | load => func_strings 112 | load => func_sysinfo 113 | load => func_talkdetect 114 | load => func_uri 115 | load => func_version 116 | load => func_volume 117 | 118 | ; PBX 119 | load => pbx_config 120 | 121 | ; Resources: ARI/Stasis 122 | load => res_ari 123 | load => res_ari_applications 124 | load => res_ari_asterisk 125 | load => res_ari_bridges 126 | load => res_ari_channels 127 | load => res_ari_device_states 128 | load => res_ari_endpoints 129 | load => res_ari_events 130 | load => res_ari_mailboxes 131 | load => res_ari_model 132 | load => res_ari_playbacks 133 | load => res_ari_recordings 134 | load => res_ari_sounds 135 | load => res_stasis 136 | load => res_stasis_answer 137 | load => res_stasis_device_state 138 | load => res_stasis_mailbox 139 | load => res_stasis_playback 140 | load => res_stasis_recording 141 | load => res_stasis_snoop 142 | load => res_timing_timerfd 143 | 144 | ; Resources: PJSIP 145 | load => res_pjproject 146 | load => res_pjsip 147 | load => res_pjsip_acl 148 | load => res_pjsip_authenticator_digest 149 | load => res_pjsip_caller_id 150 | load => res_pjsip_dialog_info_body_generator 151 | load => res_pjsip_diversion 152 | load => res_pjsip_dtmf_info 153 | load => res_pjsip_empty_info 154 | load => res_pjsip_endpoint_identifier_anonymous 155 | load => res_pjsip_endpoint_identifier_ip 156 | load => res_pjsip_endpoint_identifier_user 157 | load => res_pjsip_exten_state 158 | load => res_pjsip_header_funcs 159 | load => res_pjsip_history 160 | load => res_pjsip_logger 161 | load => res_pjsip_messaging 162 | load => res_pjsip_mwi 163 | load => res_pjsip_mwi_body_generator 164 | load => res_pjsip_nat 165 | load => res_pjsip_notify 166 | load => res_pjsip_outbound_authenticator_digest 167 | load => res_pjsip_outbound_publish 168 | load => res_pjsip_outbound_registration 169 | load => res_pjsip_path 170 | load => res_pjsip_pidf_body_generator 171 | load => res_pjsip_pidf_digium_body_supplement 172 | load => res_pjsip_pidf_eyebeam_body_supplement 173 | load => res_pjsip_publish_asterisk 174 | load => res_pjsip_pubsub 175 | load => res_pjsip_refer 176 | load => res_pjsip_registrar_expire 177 | load => res_pjsip_registrar 178 | load => res_pjsip_rfc3326 179 | load => res_pjsip_sdp_rtp 180 | load => res_pjsip_send_to_voicemail 181 | load => res_pjsip_session 182 | load => res_pjsip_sips_contact 183 | load => res_pjsip_transport_websocket 184 | load => res_pjsip_xpidf_body_generator 185 | 186 | ; Resources: Other 187 | load => res_clioriginate 188 | load => res_format_attr_celt 189 | load => res_format_attr_h263 190 | load => res_format_attr_h264 191 | load => res_format_attr_opus 192 | load => res_format_attr_silk 193 | load => res_http_websocket 194 | load => res_musiconhold 195 | load => res_mutestream 196 | load => res_mwi_external 197 | load => res_rtp_asterisk 198 | load => res_security_log 199 | load => res_sorcery_astdb 200 | load => res_sorcery_config 201 | load => res_sorcery_memory 202 | load => res_sorcery_memory_cache 203 | load => res_sorcery_realtime 204 | load => res_speech 205 | load => res_srtp 206 | -------------------------------------------------------------------------------- /asterisk/configs/manager.conf: -------------------------------------------------------------------------------- 1 | ; 2 | ; AMI - The Asterisk Manager Interface 3 | ; 4 | ; Third party application call management support and PBX event supervision 5 | ; 6 | ; Use the "manager show commands" at the CLI to list available manager commands 7 | ; and their authorization levels. 8 | ; 9 | ; "manager show command " will show a help text. 10 | ; 11 | ; ---------------------------- SECURITY NOTE ------------------------------- 12 | ; Note that you should not enable the AMI on a public IP address. If needed, 13 | ; block this TCP port with iptables (or another FW software) and reach it 14 | ; with IPsec, SSH, or SSL vpn tunnel. You can also make the manager 15 | ; interface available over http/https if Asterisk's http server is enabled in 16 | ; http.conf and if both "enabled" and "webenabled" are set to yes in 17 | ; this file. Both default to no. httptimeout provides the maximum 18 | ; timeout in seconds before a web based session is discarded. The 19 | ; default is 60 seconds. 20 | ; 21 | [general] 22 | enabled = no 23 | ;webenabled = yes 24 | 25 | ;port = 5038 26 | ;bindaddr = 0.0.0.0 27 | 28 | ; Parameters that control AMI over TLS. ("enabled" must be set too). 29 | ; You can open a connection to this socket with e.g. 30 | ; 31 | ; openssl s_client -connect my_host:5039 32 | ; 33 | ;tlsenable=no ; set to YES to enable it 34 | ;tlsbindaddr=0.0.0.0:5039 ; address and port to bind to, default to bindaddr and port 5039 35 | ;tlscertfile=/tmp/asterisk.pem ; path to the certificate. 36 | ;tlsprivatekey=/tmp/private.pem ; path to the private key, if no private given, 37 | ; if no tlsprivatekey is given, default is to search 38 | ; tlscertfile for private key. 39 | ;tlscipher= ; string specifying which SSL ciphers to use or not use 40 | ; 41 | ;allowmultiplelogin = yes ; IF set to no, rejects manager logins that are already in use. 42 | ; ; The default is yes. 43 | ; 44 | ;displayconnects = yes 45 | ; 46 | ; Add a Unix epoch timestamp to events (not action responses) 47 | ; 48 | ;timestampevents = yes 49 | 50 | ;brokeneventsaction = yes ; Restore previous behavior that caused the events 51 | ; action to not return a response in certain 52 | ; circumstances. Defaults to 'no'. 53 | 54 | ; 55 | ; Display certain channel variables every time a channel-oriented 56 | ; event is emitted: 57 | ; 58 | ;channelvars = var1,var2,var3 59 | 60 | ; debug = on ; enable some debugging info in AMI messages (default off). 61 | ; Also accessible through the "manager debug" CLI command. 62 | 63 | ; authtimeout specifies the maximum number of seconds a client has to 64 | ; authenticate. If the client does not authenticate beofre this timeout 65 | ; expires, the client will be disconnected. (default: 30 seconds) 66 | 67 | ;authtimeout = 30 68 | 69 | ; authlimit specifies the maximum number of unauthenticated sessions that will 70 | ; be allowed to connect at any given time. 71 | 72 | ;authlimit = 50 73 | 74 | ;httptimeout = 60 75 | ; a) httptimeout sets the Max-Age of the http cookie 76 | ; b) httptimeout is the amount of time the webserver waits 77 | ; on a action=waitevent request (actually its httptimeout-10) 78 | ; c) httptimeout is also the amount of time the webserver keeps 79 | ; a http session alive after completing a successful action 80 | 81 | [asterisk] 82 | secret=asterisk 83 | read=all 84 | write=all 85 | 86 | ;[mark] 87 | ;secret = mysecret 88 | ;deny=0.0.0.0/0.0.0.0 89 | ;permit=209.16.236.73/255.255.255.0 90 | ;acl=named_acl_example ; use a named ACL from acl.conf 91 | ; 92 | ; 93 | ;setvar=PBXACCOUNT=edvina 94 | ; The setvar option defines channel variables that will be set when this account 95 | ; originates a call. You can define multiple setvar= commands for one manager 96 | ; user. 97 | ; 98 | ;eventfilter=Event: Newchannel 99 | ;eventfilter=Channel: (PJ)?SIP/(james|jim|john)- 100 | ;eventfilter=!Channel: DAHDI/ 101 | ; The eventfilter option is used to whitelist or blacklist events per user. 102 | ; A filter consists of an (unanchored) regular expression that is run on the 103 | ; entire event data. If the first character of the filter is an exclamation 104 | ; mark (!), the filter is appended to the blacklist instead of the whitelist. 105 | ; After first checking the read access below, the regular expression filters 106 | ; are processed as follows: 107 | ; - If no filters are configured all events are reported as normal. 108 | ; - If there are white filters only: implied black all filter processed first, 109 | ; then white filters. 110 | ; - If there are black filters only: implied white all filter processed first, 111 | ; then black filters. 112 | ; - If there are both white and black filters: implied black all filter processed 113 | ; first, then white filters, and lastly black filters. 114 | 115 | ; 116 | ; If the device connected via this user accepts input slowly, 117 | ; the timeout for writes to it can be increased to keep it 118 | ; from being disconnected (value is in milliseconds) 119 | ; 120 | ; writetimeout = 100 121 | ; 122 | ;displayconnects = yes ; Display on CLI user login/logoff 123 | ; 124 | ; Authorization for various classes 125 | ; 126 | ; Read authorization permits you to receive asynchronous events, in general. 127 | ; Write authorization permits you to send commands and get back responses. The 128 | ; following classes exist: 129 | ; 130 | ; all - All event classes below (including any we may have missed). 131 | ; system - General information about the system and ability to run system 132 | ; management commands, such as Shutdown, Restart, and Reload. 133 | ; call - Information about channels and ability to set information in a 134 | ; running channel. 135 | ; log - Logging information. Read-only. (Defined but not yet used.) 136 | ; verbose - Verbose information. Read-only. (Defined but not yet used.) 137 | ; agent - Information about queues and agents and ability to add queue 138 | ; members to a queue. 139 | ; user - Permission to send and receive UserEvent. 140 | ; config - Ability to read and write configuration files. 141 | ; command - Permission to run CLI commands. Write-only. 142 | ; dtmf - Receive DTMF events. Read-only. 143 | ; reporting - Ability to get information about the system. 144 | ; cdr - Output of cdr_manager, if loaded. Read-only. 145 | ; dialplan - Receive NewExten and VarSet events. Read-only. 146 | ; originate - Permission to originate new calls. Write-only. 147 | ; agi - Output AGI commands executed. Input AGI command to execute. 148 | ; cc - Call Completion events. Read-only. 149 | ; aoc - Permission to send Advice Of Charge messages and receive Advice 150 | ; - Of Charge events. 151 | ; test - Ability to read TestEvent notifications sent to the Asterisk Test 152 | ; Suite. Note that this is only enabled when the TEST_FRAMEWORK 153 | ; compiler flag is defined. 154 | ; security - Security Events. Read-only. 155 | ; message - Permissions to send out of call messages. Write-only 156 | ; 157 | ;read = system,call,log,verbose,agent,user,config,dtmf,reporting,cdr,dialplan 158 | ;write = system,call,agent,user,config,command,reporting,originate,message 159 | 160 | -------------------------------------------------------------------------------- /awesome-conference/src/public/conference.js: -------------------------------------------------------------------------------- 1 | /* global angular */ 2 | /* global respoke */ 3 | /* global Webcam */ 4 | 'use strict'; 5 | 6 | var GROUP_NAME = 'conference'; 7 | 8 | var conference = angular.module('conference', ['ngResource']); 9 | 10 | function noop() {} 11 | 12 | function uid() { 13 | return Math.random().toString(36).substring(2, 6); 14 | } 15 | 16 | function resetVideos() { 17 | var vids = document.querySelectorAll('video'); 18 | var vid; 19 | var newVid; 20 | var parent; 21 | for (var i = 0; i < vids.length; i++) { 22 | vid = vids.item(i); 23 | if (vid.id) { 24 | newVid = document.createElement('video'); 25 | newVid.id = vid.id; 26 | parent = vid.parentNode; 27 | parent.removeChild(vid); 28 | parent.appendChild(newVid); 29 | } 30 | } 31 | } 32 | 33 | conference.controller('AppController', ['$rootScope', function ($rootScope) { 34 | $rootScope.people = {}; 35 | $rootScope.videoSource = null; 36 | $rootScope.people[$rootScope.myEndpoint] = ''; 37 | 38 | $rootScope.startCall = function () { 39 | $rootScope.activeCall = $rootScope.client.startVideoCall({ 40 | endpointId: 'webrtc', 41 | videoRemoteElement: document.getElementById('video-remote'), 42 | onConnect: function () { 43 | console.log('Connected!'); 44 | }, 45 | onHangup: function () { 46 | console.log('Hungup'); 47 | $rootScope.activeCall = null; 48 | resetVideos(); 49 | }, 50 | onError: function (err) { 51 | console.error('Error: ' + err); 52 | $rootScope.activeCall = null; 53 | resetVideos(); 54 | } 55 | }) 56 | }; 57 | 58 | $rootScope.setVideoSource = function (endpointId) { 59 | var group = $rootScope.client.getGroup({ id: GROUP_NAME }); 60 | if (!group) { return; } // wha... 61 | 62 | group.sendMessage({ 63 | message: { messageType: 'updateVideoSource', endpointId: endpointId }, 64 | onError: function (err) { console.error(err); } 65 | }); 66 | }; 67 | 68 | $rootScope.getPersonClass = function (endpointId) { 69 | if (endpointId !== $rootScope.videoSource) { 70 | return 'person'; 71 | } 72 | return 'videoSource'; 73 | }; 74 | }]); 75 | 76 | conference.directive('pic', [function () { 77 | return { 78 | scope: { 79 | base64: '=' 80 | }, 81 | template: '' 82 | }; 83 | }]); 84 | 85 | conference.directive('camera', ['$rootScope', '$timeout', function ($rootScope, $timeout) { 86 | return { 87 | link: function (/*scope, el*/) { 88 | Webcam.set({ 89 | width: 160, 90 | height: 120, 91 | dest_width: 160, 92 | dest_height: 120, 93 | image_format: 'jpeg', 94 | jpeg_quality: 32, 95 | force_flash: false, 96 | flip_horiz: false 97 | }); 98 | Webcam.attach('my-camera'); 99 | Webcam.on('live', function () { 100 | setInterval(function () { 101 | 102 | var group = $rootScope.client.getGroup({ id: GROUP_NAME }); 103 | if (!group) { return; } // respoke not connected 104 | 105 | Webcam.snap(function (dataUri) { 106 | $rootScope.people[$rootScope.myEndpoint] = dataUri; 107 | group.sendMessage({ 108 | message: { messageType: 'imageUpdate', image: dataUri}, 109 | onError: function (err) { console.error(err); } 110 | }); 111 | }); 112 | 113 | $timeout(noop); 114 | }, 2500); 115 | }); 116 | Webcam.on('error', function (err) { 117 | console.error('Webcamjs Error:', err); 118 | }); 119 | }, 120 | template: '
' 121 | }; 122 | }]); 123 | 124 | conference.run(['$rootScope', '$timeout', "$resource", function ($rootScope, $timeout, $resource) { 125 | // respoke.log.setLevel('debug'); 126 | 127 | var myEndpoint = uid(); 128 | $rootScope.myEndpoint = myEndpoint; 129 | localStorage.setItem('endpointId', myEndpoint); 130 | 131 | var Tokens = $resource('/token'); 132 | var token = Tokens.get({endpointId: myEndpoint}, function (authData) { 133 | console.log(authData); 134 | 135 | var client = respoke.createClient({ 136 | developmentMode: true, 137 | appId: authData.appId 138 | }); 139 | 140 | client.listen('error', function (err) { 141 | console.error(err); 142 | }); 143 | 144 | client.listen('message', function (data) { 145 | var imageContents = data.message.message.image; 146 | var endpointId = data.message.endpointId; 147 | var messageType = data.message.message.messageType; 148 | 149 | if (messageType === 'imageUpdate' && endpointId !== 'backend') { 150 | if (!imageContents || !endpointId) { 151 | console.error('No endpointId or imageContents!'); 152 | return; 153 | } 154 | 155 | $rootScope.people[endpointId] = imageContents; 156 | } else if (messageType === 'videoSourceSet') { 157 | $rootScope.videoSource = data.message.message.endpointId; 158 | console.log('Video source is now: ' + $rootScope.videoSource); 159 | } 160 | 161 | $timeout(noop); 162 | }); 163 | 164 | client.listen('connect', function () { 165 | client.join({ 166 | id: GROUP_NAME, 167 | onSuccess: function (group) { 168 | console.log('Joined', group); 169 | group.getMembers().then(function (connections) { 170 | connections.forEach(function (member) { 171 | console.log('Already here', member); 172 | if (member.endpointId !== 'backend') { 173 | $rootScope.people[member.endpointId] = ''; 174 | } 175 | }); 176 | $timeout(noop); 177 | }); 178 | }, 179 | onJoin: function (data) { 180 | var endpointId = data.connection.endpointId; 181 | 182 | if (endpointId === 'backend') { 183 | return; 184 | } 185 | 186 | $rootScope.people[endpointId] = ''; 187 | $timeout(noop); 188 | }, 189 | onLeave: function (data) { 190 | var endpointId = data.connection.endpointId; 191 | 192 | if (endpointId === 'backend') { 193 | return; 194 | } 195 | 196 | $rootScope.people[endpointId] = null; 197 | delete $rootScope.people[endpointId]; 198 | $timeout(noop); 199 | } 200 | }); 201 | $rootScope.startCall(); 202 | }); 203 | 204 | client.connect({ 205 | endpointId: myEndpoint 206 | }); 207 | 208 | $rootScope.client = window.client = client; 209 | 210 | }); 211 | 212 | }]); -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /asterisk/configs/indications.conf: -------------------------------------------------------------------------------- 1 | ; 2 | ; indications.conf 3 | ; 4 | ; Configuration file for location specific tone indications 5 | ; 6 | 7 | ; 8 | ; NOTE: 9 | ; When adding countries to this file, please keep them in alphabetical 10 | ; order according to the 2-character country codes! 11 | ; 12 | ; The [general] category is for certain global variables. 13 | ; All other categories are interpreted as location specific indications 14 | ; 15 | 16 | [general] 17 | country=us ; default location 18 | 19 | 20 | ; [example] 21 | ; description = string 22 | ; The full name of your country, in English. 23 | ; ringcadence = num[,num]* 24 | ; List of durations the physical bell rings. 25 | ; dial = tonelist 26 | ; Set of tones to be played when one picks up the hook. 27 | ; busy = tonelist 28 | ; Set of tones played when the receiving end is busy. 29 | ; congestion = tonelist 30 | ; Set of tones played when there is some congestion (on the network?) 31 | ; callwaiting = tonelist 32 | ; Set of tones played when there is a call waiting in the background. 33 | ; dialrecall = tonelist 34 | ; Not well defined; many phone systems play a recall dial tone after hook 35 | ; flash. 36 | ; record = tonelist 37 | ; Set of tones played when call recording is in progress. 38 | ; info = tonelist 39 | ; Set of tones played with special information messages (e.g., "number is 40 | ; out of service") 41 | ; 'name' = tonelist 42 | ; Every other variable will be available as a shortcut for the "PlayList" command 43 | ; but will not be used automatically by Asterisk. 44 | ; 45 | ; 46 | ; The tonelist itself is defined by a comma-separated sequence of elements. 47 | ; Each element consist of a frequency (f) with an optional duration (in ms) 48 | ; attached to it (f/duration). The frequency component may be a mixture of two 49 | ; frequencies (f1+f2) or a frequency modulated by another frequency (f1*f2). 50 | ; The implicit modulation depth is fixed at 90%, though. 51 | ; If the list element starts with a !, that element is NOT repeated, 52 | ; therefore, only if all elements start with !, the tonelist is time-limited, 53 | ; all others will repeat indefinitely. 54 | ; 55 | ; concisely: 56 | ; element = [!]freq[+|*freq2][/duration] 57 | ; tonelist = element[,element]* 58 | ; 59 | 60 | [at] 61 | description = Austria 62 | ringcadence = 1000,5000 63 | ; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf 64 | dial = 420 65 | busy = 420/400,0/400 66 | ring = 420/1000,0/5000 67 | congestion = 420/200,0/200 68 | callwaiting = 420/40,0/1960 69 | dialrecall = 420 70 | ; RECORDTONE - not specified 71 | record = 1400/80,0/14920 72 | info = 950/330,1450/330,1850/330,0/1000 73 | stutter = 380+420 74 | 75 | [au] 76 | description = Australia 77 | ; Reference http://www.acif.org.au/__data/page/3303/S002_2001.pdf 78 | ; Normal Ring 79 | ringcadence = 400,200,400,2000 80 | ; Distinctive Ring 1 - Forwarded Calls 81 | ; 400,400,200,200,400,1400 82 | ; Distinctive Ring 2 - Selective Ring 2 + Operator + Recall 83 | ; 400,400,200,2000 84 | ; Distinctive Ring 3 - Multiple Subscriber Number 1 85 | ; 200,200,400,2200 86 | ; Distinctive Ring 4 - Selective Ring 1 + Centrex 87 | ; 400,2600 88 | ; Distinctive Ring 5 - Selective Ring 3 89 | ; 400,400,200,400,200,1400 90 | ; Distinctive Ring 6 - Multiple Subscriber Number 2 91 | ; 200,400,200,200,400,1600 92 | ; Distinctive Ring 7 - Multiple Subscriber Number 3 + Data Privacy 93 | ; 200,400,200,400,200,1600 94 | ; Tones 95 | dial = 413+438 96 | busy = 425/375,0/375 97 | ring = 413+438/400,0/200,413+438/400,0/2000 98 | ; XXX Congestion: Should reduce by 10 db every other cadence XXX 99 | congestion = 425/375,0/375,420/375,0/375 100 | callwaiting = 425/200,0/200,425/200,0/4400 101 | dialrecall = 413+438 102 | ; Record tone used for Call Intrusion/Recording or Conference 103 | record = !425/1000,!0/15000,425/360,0/15000 104 | info = 425/2500,0/500 105 | ; Other Australian Tones 106 | ; The STD "pips" indicate the call is not an untimed local call 107 | std = !525/100,!0/100,!525/100,!0/100,!525/100,!0/100,!525/100,!0/100,!525/100 108 | ; Facility confirmation tone (eg. Call Forward Activated) 109 | facility = 425 110 | ; Message Waiting "stutter" dialtone 111 | stutter = 413+438/100,0/40 112 | ; Ringtone for calls to Telstra mobiles 113 | ringmobile = 400+450/400,0/200,400+450/400,0/2000 114 | 115 | [bg] 116 | ; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf 117 | description = Bulgaria 118 | ringcadence = 1000,4000 119 | ; 120 | dial = 425 121 | busy = 425/500,0/500 122 | ring = 425/1000,0/4000 123 | congestion = 425/250,0/250 124 | callwaiting = 425/150,0/150,425/150,0/4000 125 | dialrecall = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425 126 | record = 1400/425,0/15000 127 | info = 950/330,1400/330,1800/330,0/1000 128 | stutter = 425/1500,0/100 129 | 130 | [br] 131 | description = Brazil 132 | ringcadence = 1000,4000 133 | dial = 425 134 | busy = 425/250,0/250 135 | ring = 425/1000,0/4000 136 | congestion = 425/250,0/250,425/750,0/250 137 | callwaiting = 425/50,0/1000 138 | ; Dialrecall not used in Brazil standard (using UK standard) 139 | dialrecall = 350+440 140 | ; Record tone is not used in Brazil, use busy tone 141 | record = 425/250,0/250 142 | ; Info not used in Brazil standard (using UK standard) 143 | info = 950/330,1400/330,1800/330 144 | stutter = 350+440 145 | 146 | [be] 147 | description = Belgium 148 | ; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf 149 | ringcadence = 1000,3000 150 | dial = 425 151 | busy = 425/500,0/500 152 | ring = 425/1000,0/3000 153 | congestion = 425/167,0/167 154 | callwaiting = 1400/175,0/175,1400/175,0/3500 155 | ; DIALRECALL - not specified 156 | dialrecall = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440 157 | ; RECORDTONE - not specified 158 | record = 1400/500,0/15000 159 | info = 900/330,1400/330,1800/330,0/1000 160 | stutter = 425/1000,0/250 161 | 162 | [ch] 163 | description = Switzerland 164 | ; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf 165 | ringcadence = 1000,4000 166 | dial = 425 167 | busy = 425/500,0/500 168 | ring = 425/1000,0/4000 169 | congestion = 425/200,0/200 170 | callwaiting = 425/200,0/200,425/200,0/4000 171 | ; DIALRECALL - not specified 172 | dialrecall = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425 173 | ; RECORDTONE - not specified 174 | record = 1400/80,0/15000 175 | info = 950/330,1400/330,1800/330,0/1000 176 | stutter = 425+340/1100,0/1100 177 | 178 | [cl] 179 | description = Chile 180 | ; According to specs from Telefonica CTC Chile 181 | ringcadence = 1000,3000 182 | dial = 400 183 | busy = 400/500,0/500 184 | ring = 400/1000,0/3000 185 | congestion = 400/200,0/200 186 | callwaiting = 400/250,0/8750 187 | dialrecall = !400/100,!0/100,!400/100,!0/100,!400/100,!0/100,400 188 | record = 1400/500,0/15000 189 | info = 950/333,1400/333,1800/333,0/1000 190 | stutter = !400/100,!0/100,!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,400 191 | 192 | [cn] 193 | description = China 194 | ; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf 195 | ringcadence = 1000,4000 196 | dial = 450 197 | busy = 450/350,0/350 198 | ring = 450/1000,0/4000 199 | congestion = 450/700,0/700 200 | callwaiting = 450/400,0/4000 201 | dialrecall = 450 202 | record = 950/400,0/10000 203 | info = 450/100,0/100,450/100,0/100,450/100,0/100,450/400,0/400 204 | ; STUTTER - not specified 205 | stutter = 450+425 206 | 207 | [cz] 208 | description = Czech Republic 209 | ; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf 210 | ringcadence = 1000,4000 211 | dial = 425/330,0/330,425/660,0/660 212 | busy = 425/330,0/330 213 | ring = 425/1000,0/4000 214 | congestion = 425/165,0/165 215 | callwaiting = 425/330,0/9000 216 | ; DIALRECALL - not specified 217 | dialrecall = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425/330,0/330,425/660,0/660 218 | ; RECORDTONE - not specified 219 | record = 1400/500,0/14000 220 | info = 950/330,0/30,1400/330,0/30,1800/330,0/1000 221 | ; STUTTER - not specified 222 | stutter = 425/450,0/50 223 | 224 | [de] 225 | description = Germany 226 | ; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf 227 | ringcadence = 1000,4000 228 | dial = 425 229 | busy = 425/480,0/480 230 | ring = 425/1000,0/4000 231 | congestion = 425/240,0/240 232 | callwaiting = !425/200,!0/200,!425/200,!0/5000,!425/200,!0/200,!425/200,!0/5000,!425/200,!0/200,!425/200,!0/5000,!425/200,!0/200,!425/200,!0/5000,!425/200,!0/200,!425/200,0 233 | ; DIALRECALL - not specified 234 | dialrecall = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425 235 | ; RECORDTONE - not specified 236 | record = 1400/80,0/15000 237 | info = 950/330,1400/330,1800/330,0/1000 238 | stutter = 425+400 239 | 240 | [dk] 241 | description = Denmark 242 | ; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf 243 | ringcadence = 1000,4000 244 | dial = 425 245 | busy = 425/500,0/500 246 | ring = 425/1000,0/4000 247 | congestion = 425/200,0/200 248 | callwaiting = !425/200,!0/600,!425/200,!0/3000,!425/200,!0/200,!425/200,0 249 | ; DIALRECALL - not specified 250 | dialrecall = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425 251 | ; RECORDTONE - not specified 252 | record = 1400/80,0/15000 253 | info = 950/330,1400/330,1800/330,0/1000 254 | ; STUTTER - not specified 255 | stutter = 425/450,0/50 256 | 257 | [ee] 258 | description = Estonia 259 | ; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf 260 | ringcadence = 1000,4000 261 | dial = 425 262 | busy = 425/300,0/300 263 | ring = 425/1000,0/4000 264 | congestion = 425/200,0/200 265 | ; CALLWAIT not in accordance to ITU 266 | callwaiting = 950/650,0/325,950/325,0/30,1400/1300,0/2600 267 | ; DIALRECALL - not specified 268 | dialrecall = 425/650,0/25 269 | ; RECORDTONE - not specified 270 | record = 1400/500,0/15000 271 | ; INFO not in accordance to ITU 272 | info = 950/650,0/325,950/325,0/30,1400/1300,0/2600 273 | ; STUTTER not specified 274 | stutter = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425 275 | 276 | [es] 277 | description = Spain 278 | ringcadence = 1500,3000 279 | dial = 425 280 | busy = 425/200,0/200 281 | ring = 425/1500,0/3000 282 | congestion = 425/200,0/200,425/200,0/200,425/200,0/600 283 | callwaiting = 425/175,0/175,425/175,0/3500 284 | dialrecall = !425/200,!0/200,!425/200,!0/200,!425/200,!0/200,425 285 | record = 1400/500,0/15000 286 | info = 950/330,0/1000 287 | dialout = 500 288 | ; STUTTER not specified 289 | stutter = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425 290 | 291 | 292 | [fi] 293 | description = Finland 294 | ringcadence = 1000,4000 295 | dial = 425 296 | busy = 425/300,0/300 297 | ring = 425/1000,0/4000 298 | congestion = 425/200,0/200 299 | callwaiting = 425/150,0/150,425/150,0/8000 300 | dialrecall = 425/650,0/25 301 | record = 1400/500,0/15000 302 | info = 950/650,0/325,950/325,0/30,1400/1300,0/2600 303 | stutter = 425/650,0/25 304 | 305 | [fr] 306 | description = France 307 | ; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf 308 | ringcadence = 1500,3500 309 | ; Dialtone can also be 440+330 310 | dial = 440 311 | busy = 440/500,0/500 312 | ring = 440/1500,0/3500 313 | ; CONGESTION - not specified 314 | congestion = 440/250,0/250 315 | callwait = 440/300,0/10000 316 | ; DIALRECALL - not specified 317 | dialrecall = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440 318 | ; RECORDTONE - not specified 319 | record = 1400/500,0/15000 320 | info = !950/330,!1400/330,!1800/330 321 | stutter = !440/100,!0/100,!440/100,!0/100,!440/100,!0/100,!440/100,!0/100,!440/100,!0/100,!440/100,!0/100,440 322 | 323 | [gr] 324 | description = Greece 325 | ringcadence = 1000,4000 326 | dial = 425/200,0/300,425/700,0/800 327 | busy = 425/300,0/300 328 | ring = 425/1000,0/4000 329 | congestion = 425/200,0/200 330 | callwaiting = 425/150,0/150,425/150,0/8000 331 | dialrecall = 425/650,0/25 332 | record = 1400/400,0/15000 333 | info = !950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,0 334 | stutter = 425/650,0/25 335 | 336 | [hu] 337 | description = Hungary 338 | ; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf 339 | ringcadence = 1250,3750 340 | dial = 425 341 | busy = 425/300,0/300 342 | ring = 425/1250,0/3750 343 | congestion = 425/300,0/300 344 | callwaiting = 425/40,0/1960 345 | dialrecall = 425+450 346 | ; RECORDTONE - not specified 347 | record = 1400/400,0/15000 348 | info = !950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,0 349 | stutter = 350+375+400 350 | 351 | [il] 352 | description = Israel 353 | ringcadence = 1000,3000 354 | dial = 414 355 | busy = 414/500,0/500 356 | ring = 414/1000,0/3000 357 | congestion = 414/250,0/250 358 | callwaiting = 414/100,0/100,414/100,0/100,414/600,0/3000 359 | dialrecall = !414/100,!0/100,!414/100,!0/100,!414/100,!0/100,414 360 | record = 1400/500,0/15000 361 | info = 1000/330,1400/330,1800/330,0/1000 362 | stutter = !414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,!414/160,!0/160,414 363 | 364 | 365 | [in] 366 | description = India 367 | ringcadence = 400,200,400,2000 368 | dial = 400*25 369 | busy = 400/750,0/750 370 | ring = 400*25/400,0/200,400*25/400,0/2000 371 | congestion = 400/250,0/250 372 | callwaiting = 400/200,0/100,400/200,0/7500 373 | dialrecall = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440 374 | record = 1400/500,0/15000 375 | info = !950/330,!1400/330,!1800/330,0/1000 376 | stutter = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,400*25 377 | 378 | [it] 379 | description = Italy 380 | ; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf 381 | ringcadence = 1000,4000 382 | dial = 425/200,0/200,425/600,0/1000 383 | busy = 425/500,0/500 384 | ring = 425/1000,0/4000 385 | congestion = 425/200,0/200 386 | callwaiting = 425/400,0/100,425/250,0/100,425/150,0/14000 387 | dialrecall = 470/400,425/400 388 | record = 1400/400,0/15000 389 | info = !950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,0 390 | stutter = 470/400,425/400 391 | 392 | [lt] 393 | description = Lithuania 394 | ringcadence = 1000,4000 395 | dial = 425 396 | busy = 425/350,0/350 397 | ring = 425/1000,0/4000 398 | congestion = 425/200,0/200 399 | callwaiting = 425/150,0/150,425/150,0/4000 400 | ; DIALRECALL - not specified 401 | dialrecall = 425/500,0/50 402 | ; RECORDTONE - not specified 403 | record = 1400/500,0/15000 404 | info = !950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,0 405 | ; STUTTER - not specified 406 | stutter = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425 407 | 408 | [jp] 409 | description = Japan 410 | ringcadence = 1000,2000 411 | dial = 400 412 | busy = 400/500,0/500 413 | ring = 400+15/1000,0/2000 414 | congestion = 400/500,0/500 415 | callwaiting = 400+16/500,0/8000 416 | dialrecall = !400/200,!0/200,!400/200,!0/200,!400/200,!0/200,400 417 | record = 1400/500,0/15000 418 | info = !950/330,!1400/330,!1800/330,0 419 | stutter = !400/100,!0/100,!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,400 420 | 421 | [mx] 422 | description = Mexico 423 | ringcadence = 2000,4000 424 | dial = 425 425 | busy = 425/250,0/250 426 | ring = 425/1000,0/4000 427 | congestion = 425/250,0/250 428 | callwaiting = 425/200,0/600,425/200,0/10000 429 | dialrecall = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440 430 | record = 1400/500,0/15000 431 | info = 950/330,0/30,1400/330,0/30,1800/330,0/1000 432 | stutter = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,425 433 | 434 | [my] 435 | description = Malaysia 436 | ringcadence = 2000,4000 437 | dial = 425 438 | busy = 425/500,0/500 439 | ring = 425/400,0/200,425/400,0/2000 440 | congestion = 425/500,0/500 441 | ; STUTTER - not specified 442 | stutter = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425 443 | 444 | [nl] 445 | description = Netherlands 446 | ; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf 447 | ringcadence = 1000,4000 448 | ; Most of these 425's can also be 450's 449 | dial = 425 450 | busy = 425/500,0/500 451 | ring = 425/1000,0/4000 452 | congestion = 425/250,0/250 453 | callwaiting = 425/500,0/9500 454 | ; DIALRECALL - not specified 455 | dialrecall = 425/500,0/50 456 | ; RECORDTONE - not specified 457 | record = 1400/500,0/15000 458 | info = 950/330,1400/330,1800/330,0/1000 459 | stutter = 425/500,0/50 460 | 461 | [no] 462 | description = Norway 463 | ringcadence = 1000,4000 464 | dial = 425 465 | busy = 425/500,0/500 466 | ring = 425/1000,0/4000 467 | congestion = 425/200,0/200 468 | callwaiting = 425/200,0/600,425/200,0/10000 469 | dialrecall = 470/400,425/400 470 | record = 1400/400,0/15000 471 | info = !950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,0 472 | stutter = 470/400,425/400 473 | 474 | [nz] 475 | description = New Zealand 476 | ; Reference = http://www.telepermit.co.nz/TNA102.pdf 477 | ringcadence = 400,200,400,2000 478 | dial = 400 479 | busy = 400/500,0/500 480 | ring = 400+450/400,0/200,400+450/400,0/2000 481 | congestion = 400/250,0/250 482 | callwaiting = !400/200,!0/3000,!400/200,!0/3000,!400/200,!0/3000,!400/200 483 | dialrecall = !400/100,!0/100,!400/100,!0/100,!400/100,!0/100,400 484 | record = 1400/425,0/15000 485 | info = 400/750,0/100,400/750,0/100,400/750,0/100,400/750,0/400 486 | stutter = !400/100,!0/100,!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,!400/100,!0/100,400 487 | unobtainable = 400/75,0/100,400/75,0/100,400/75,0/100,400/75,0/400 488 | 489 | [ph] 490 | 491 | ; reference http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf 492 | 493 | description = Philippines 494 | ringcadence = 1000,4000 495 | dial = 425 496 | busy = 480+620/500,0/500 497 | ring = 425+480/1000,0/4000 498 | congestion = 480+620/250,0/250 499 | callwaiting = 440/300,0/10000 500 | ; DIALRECALL - not specified 501 | dialrecall = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440 502 | ; RECORDTONE - not specified 503 | record = 1400/500,0/15000 504 | ; INFO - not specified 505 | info = !950/330,!1400/330,!1800/330,0 506 | ; STUTTER - not specified 507 | stutter = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,425 508 | 509 | 510 | [pl] 511 | description = Poland 512 | ringcadence = 1000,4000 513 | dial = 425 514 | busy = 425/500,0/500 515 | ring = 425/1000,0/4000 516 | congestion = 425/500,0/500 517 | callwaiting = 425/150,0/150,425/150,0/4000 518 | ; DIALRECALL - not specified 519 | dialrecall = 425/500,0/50 520 | ; RECORDTONE - not specified 521 | record = 1400/500,0/15000 522 | ; 950/1400/1800 3x0.33 on 1.0 off repeated 3 times 523 | info = !950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000,!950/330,!1400/330,!1800/330,!0/1000 524 | ; STUTTER - not specified 525 | stutter = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425 526 | 527 | [pt] 528 | description = Portugal 529 | ringcadence = 1000,5000 530 | dial = 425 531 | busy = 425/500,0/500 532 | ring = 425/1000,0/5000 533 | congestion = 425/200,0/200 534 | callwaiting = 440/300,0/10000 535 | dialrecall = 425/1000,0/200 536 | record = 1400/500,0/15000 537 | info = 950/330,1400/330,1800/330,0/1000 538 | stutter = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425 539 | 540 | [ru] 541 | ; References: 542 | ; http://www.minsvyaz.ru/site.shtml?id=1806 543 | ; http://www.aboutphone.info/lib/gost/45-223-2001.html 544 | description = Russian Federation / ex Soviet Union 545 | ringcadence = 1000,4000 546 | dial = 425 547 | busy = 425/350,0/350 548 | ring = 425/1000,0/4000 549 | congestion = 425/175,0/175 550 | callwaiting = 425/200,0/5000 551 | record = 1400/400,0/15000 552 | info = 950/330,1400/330,1800/330,0/1000 553 | dialrecall = 425/400,0/40 554 | stutter = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425 555 | 556 | [se] 557 | description = Sweden 558 | ringcadence = 1000,5000 559 | dial = 425 560 | busy = 425/250,0/250 561 | ring = 425/1000,0/5000 562 | congestion = 425/250,0/750 563 | callwaiting = 425/200,0/500,425/200,0/9100 564 | dialrecall = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425 565 | record = 1400/500,0/15000 566 | info = !950/332,!0/24,!1400/332,!0/24,!1800/332,!0/2024,!950/332,!0/24,!1400/332,!0/24,!1800/332,!0/2024,!950/332,!0/24,!1400/332,!0/24,!1800/332,!0/2024,!950/332,!0/24,!1400/332,!0/24,!1800/332,!0/2024,!950/332,!0/24,!1400/332,!0/24,!1800/332,0 567 | stutter = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425 568 | ; stutter = 425/320,0/20 ; Real swedish standard, not used for now 569 | 570 | [sg] 571 | description = Singapore 572 | ; Singapore 573 | ; Reference: http://www.ida.gov.sg/idaweb/doc/download/I397/ida_ts_pstn1_i4r2.pdf 574 | ; Frequency specs are: 425 Hz +/- 20Hz; 24 Hz +/- 2Hz; modulation depth 100%; SIT +/- 50Hz 575 | ringcadence = 400,200,400,2000 576 | dial = 425 577 | ring = 425*24/400,0/200,425*24/400,0/2000 ; modulation should be 100%, not 90% 578 | busy = 425/750,0/750 579 | congestion = 425/250,0/250 580 | callwaiting = 425*24/300,0/200,425*24/300,0/3200 581 | stutter = !425/200,!0/200,!425/600,!0/200,!425/200,!0/200,!425/600,!0/200,!425/200,!0/200,!425/600,!0/200,!425/200,!0/200,!425/600,!0/200,425 582 | info = 950/330,1400/330,1800/330,0/1000 ; not currently in use acc. to reference 583 | dialrecall = 425*24/500,0/500,425/500,0/2500 ; unspecified in IDA reference, use repeating Holding Tone A,B 584 | record = 1400/500,0/15000 ; unspecified in IDA reference, use 0.5s tone every 15s 585 | ; additionally defined in reference 586 | nutone = 425/2500,0/500 587 | intrusion = 425/250,0/2000 588 | warning = 425/624,0/4376 ; end of period tone, warning 589 | acceptance = 425/125,0/125 590 | holdinga = !425*24/500,!0/500 ; followed by holdingb 591 | holdingb = !425/500,!0/2500 592 | 593 | [th] 594 | description = Thailand 595 | ringcadence = 1000,4000 596 | ; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf 597 | dial = 400*50 598 | busy = 400/500,0/500 599 | ring = 420/1000,0/5000 600 | congestion = 400/300,0/300 601 | callwaiting = 1000/400,10000/400,1000/400 602 | ; DIALRECALL - not specified - use special dial tone instead. 603 | dialrecall = 400*50/400,0/100,400*50/400,0/100 604 | ; RECORDTONE - not specified 605 | record = 1400/500,0/15000 606 | ; INFO - specified as an announcement - use special information tones instead 607 | info = 950/330,1400/330,1800/330 608 | ; STUTTER - not specified 609 | stutter = !400/200,!0/200,!400/600,!0/200,!400/200,!0/200,!400/600,!0/200,!400/200,!0/200,!400/600,!0/200,!400/200,!0/200,!400/600,!0/200,400 610 | 611 | [uk] 612 | description = United Kingdom 613 | ringcadence = 400,200,400,2000 614 | ; These are the official tones taken from BT SIN350. The actual tones 615 | ; used by BT include some volume differences so sound slightly different 616 | ; from Asterisk-generated ones. 617 | dial = 350+440 618 | ; Special dial is the intermittent dial tone heard when, for example, 619 | ; you have a divert active on the line 620 | specialdial = 350+440/750,440/750 621 | ; Busy is also called "Engaged" 622 | busy = 400/375,0/375 623 | ; "Congestion" is the Beep-bip engaged tone 624 | congestion = 400/400,0/350,400/225,0/525 625 | ; "Special Congestion" is not used by BT very often if at all 626 | specialcongestion = 400/200,1004/300 627 | unobtainable = 400 628 | ring = 400+450/400,0/200,400+450/400,0/2000 629 | callwaiting = 400/100,0/4000 630 | ; BT seem to use "Special Call Waiting" rather than just "Call Waiting" tones 631 | specialcallwaiting = 400/250,0/250,400/250,0/250,400/250,0/5000 632 | ; "Pips" used by BT on payphones. (Sounds wrong, but this is what BT claim it 633 | ; is and I've not used a payphone for years) 634 | creditexpired = 400/125,0/125 635 | ; These two are used to confirm/reject service requests on exchanges that 636 | ; don't do voice announcements. 637 | confirm = 1400 638 | switching = 400/200,0/400,400/2000,0/400 639 | ; This is the three rising tones Doo-dah-dee "Special Information Tone", 640 | ; usually followed by the BT woman saying an appropriate message. 641 | info = 950/330,0/15,1400/330,0/15,1800/330,0/1000 642 | ; Not listed in SIN350 643 | record = 1400/500,0/60000 644 | stutter = 350+440/750,440/750 645 | 646 | [us] 647 | description = United States / North America 648 | ringcadence = 2000,4000 649 | dial = 350+440 650 | busy = 480+620/500,0/500 651 | ring = 440+480/2000,0/4000 652 | congestion = 480+620/250,0/250 653 | callwaiting = 440/300,0/10000 654 | dialrecall = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440 655 | record = 1400/500,0/15000 656 | info = !950/330,!1400/330,!1800/330,0 657 | stutter = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440 658 | 659 | [us-old] 660 | description = United States Circa 1950/ North America 661 | ringcadence = 2000,4000 662 | dial = 600*120 663 | busy = 500*100/500,0/500 664 | ring = 420*40/2000,0/4000 665 | congestion = 500*100/250,0/250 666 | callwaiting = 440/300,0/10000 667 | dialrecall = !600*120/100,!0/100,!600*120/100,!0/100,!600*120/100,!0/100,600*120 668 | record = 1400/500,0/15000 669 | info = !950/330,!1400/330,!1800/330,0 670 | stutter = !600*120/100,!0/100,!600*120/100,!0/100,!600*120/100,!0/100,!600*120/100,!0/100,!600*120/100,!0/100,!600*120/100,!0/100,600*120 671 | 672 | [tw] 673 | description = Taiwan 674 | ; http://nemesis.lonestar.org/reference/telecom/signaling/dialtone.html 675 | ; http://nemesis.lonestar.org/reference/telecom/signaling/busy.html 676 | ; http://www.iproducts.com.tw/ee/kylink/06ky-1000a.htm 677 | ; http://www.pbx-manufacturer.com/ky120dx.htm 678 | ; http://www.nettwerked.net/tones.txt 679 | ; http://www.cisco.com/univercd/cc/td/doc/product/tel_pswt/vco_prod/taiw_sup/taiw2.htm 680 | ; 681 | ; busy tone 480+620Hz 0.5 sec. on ,0.5 sec. off 682 | ; reorder tone 480+620Hz 0.25 sec. on,0.25 sec. off 683 | ; ringing tone 440+480Hz 1 sec. on ,2 sec. off 684 | ; 685 | ringcadence = 1000,4000 686 | dial = 350+440 687 | busy = 480+620/500,0/500 688 | ring = 440+480/1000,0/2000 689 | congestion = 480+620/250,0/250 690 | callwaiting = 350+440/250,0/250,350+440/250,0/3250 691 | dialrecall = 300/1500,0/500 692 | record = 1400/500,0/15000 693 | info = !950/330,!1400/330,!1800/330,0 694 | stutter = !350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,!350+440/100,!0/100,350+440 695 | 696 | [ve] 697 | ; Tone definition source for ve found on 698 | ; Reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf 699 | description = Venezuela / South America 700 | ringcadence = 1000,4000 701 | dial = 425 702 | busy = 425/500,0/500 703 | ring = 425/1000,0/4000 704 | congestion = 425/250,0/250 705 | callwaiting = 400+450/300,0/6000 706 | dialrecall = 425 707 | record = 1400/500,0/15000 708 | info = !950/330,!1440/330,!1800/330,0/1000 709 | ; STUTTER - not specified 710 | stutter = !425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,!425/100,!0/100,425 711 | 712 | 713 | [za] 714 | description = South Africa 715 | ; http://www.cisco.com/univercd/cc/td/doc/product/tel_pswt/vco_prod/safr_sup/saf02.htm 716 | ; (definitions for other countries can also be found there) 717 | ; Note, though, that South Africa uses two switch types in their network -- 718 | ; Alcatel switches -- mainly in the Western Cape, and Siemens elsewhere. 719 | ; The former use 383+417 in dial, ringback etc. The latter use 400*33 720 | ; I've provided both, uncomment the ones you prefer 721 | ringcadence = 400,200,400,2000 722 | ; dial/ring/callwaiting for the Siemens switches: 723 | dial = 400*33 724 | ring = 400*33/400,0/200,400*33/400,0/2000 725 | callwaiting = 400*33/250,0/250,400*33/250,0/250,400*33/250,0/250,400*33/250,0/250 726 | ; dial/ring/callwaiting for the Alcatel switches: 727 | ; dial = 383+417 728 | ; ring = 383+417/400,0/200,383+417/400,0/2000 729 | ; callwaiting = 383+417/250,0/250,383+417/250,0/250,383+417/250,0/250,383+417/250,0/250 730 | congestion = 400/250,0/250 731 | busy = 400/500,0/500 732 | dialrecall = 350+440 733 | ; XXX Not sure about the RECORDTONE 734 | record = 1400/500,0/10000 735 | info = 950/330,1400/330,1800/330,0/330 736 | stutter = !400*33/100,!0/100,!400*33/100,!0/100,!400*33/100,!0/100,!400*33/100,!0/100,!400*33/100,!0/100,!400*33/100,!0/100,400*33 737 | --------------------------------------------------------------------------------