Git Product home page Git Product logo

innovateasterisk / browser-phone Goto Github PK

View Code? Open in Web Editor NEW
474.0 37.0 239.0 16.67 MB

A fully featured browser based WebRTC SIP phone for Asterisk

Home Page: https://www.innovateasterisk.com

License: GNU Affero General Public License v3.0

HTML 2.59% CSS 7.66% JavaScript 72.83% Less 8.40% SCSS 8.51%
webrtc browser-phone asterisk-pbx audio-calls video-calls voip asterisk asterisk-server asterisk-dialplan asterisk-webui

browser-phone's Introduction

Browser Phone

A fully featured browser based WebRTC SIP phone for Asterisk

Description

This web application is designed to work with Asterisk PBX. Once loaded application will connect to Asterisk PBX on its web socket, and register an extension. Calls are made between contacts, and a full call detail is saved. Audio Calls can be recorded. Video Calls can be recorded, and can be saved with 5 different recording layouts and 3 different quality settings. This application does not use any cloud systems or services, and is designed to be stand-alone. Additional libraries will be downloaded at run time (but can also be saved to the web server for a complete off-line solution).

"Buy Me A Coffee"

Hosted versions/samples

Buddy Stream

Features v0.3.x

  • SIP Audio Calling
  • SIP Video Calling
  • XMPP Messaging
  • Call Transfer (Both Blind & Attended)
  • 3rd Party Conference Call
  • Call Detail Records
  • Call Recording (Audio & Video)
  • Screen Share during Video Call
  • Scratchpad Share during Video Call
  • Video/Audio File Share during Video Call
  • SIP (text/plain) Messaging
  • SIP Message Accept Notification (not delivery)
  • Buddy (Contact) Management
  • Useful debug messages sent to console.
  • Works on: Chrome (all features work), Edge (same as Chrome), Opera (same as Chrome), Firefox (Most features work), Safari (Most feature work)
  • Asterisk SFU - Including talker notification and Caller ID
  • Dark Mode & Light Mode - System Setting Detects

XMPP Features v0.2.x

  • User Login & Auth (Use SIP credentials)
  • Buddy List (Roster) Saved on Server
  • Buddy vCard
  • Buddy Picture Upload
  • Message Typing Indication
  • Message Delivery & Read Notification
  • Offline Message History (If supported by server)
  • Tested to work with Openfire

Server; Requires

  • Asterisk PBX version 13|16|17|18 (with Websockets and Text Messaging, chan_sip or chan_pjsip)

Server; Optional

  • Openfire XMPP Server

JavaScript Dependencies

  • sip-0.20.0 : WebRTC and SIP signalling library
  • jquery-3.3.1 : JavaScript toolkit
  • jquery.md5 : Md5 Hash plug-in (unused)
  • Chart-2.7.2 : Graph and Chart UI
  • jquery-ui : Windowing & UI Library
  • fabric-2.4.6 : Canvas Editing Library
  • moment-2.24.0 : Date & Time Library
  • croppie-2.6.4 : Profile Picture Crop Library
  • strophe-1.4.1 : XMPP Library

Note: These files will load automatically from CDN.

StyleSheet Dependencies

  • normalize-v8.0.1 : CSS Normalising Stylesheet
  • roboto : Roboto Font
  • font-awesome-4.7 : Icon Font library
  • jquery-ui : For jQuery UI
  • croppie-2.6.4 : For Croppie

Note: These files will load automatically from CDN.

Lib Folder Download (Off-line)

You can download the lib folder containing all related library files: https://github.com/InnovateAsterisk/Browser-Phone/tree/master/lib

Note: These files are provided "as-is" for your convenience. Each library folder may contain its own licence and terms of use please refer to the original license holder for more details.

Step-by-step Guide

You can follow the How-to video to achieve the outcome for this project:

chan_sip:

View on YouTube

chan_pjsip (Part 2):

View on YouTube

Or follow these steps.

Preparing the SD Card with Raspbian

Flash the you SD card using the Raspberry Pi Imager from https://www.raspberrypi.org/downloads/.

Write a blank text file named ssh (no extension) to the boot directory of the SD card. On Mac use:

sudo nano /Voumes/boot/ssh

and on Windows, you can just use Notepad and save it as: D:/ssh

Insert the SD Card into your Raspberry Pi, connect a Network Cable and boot up.

Connect to the raspberry pi over the network using Terminal (on Mac), or Putty (on Windows), as:

The default password for raspberry pi is: raspberry

Initial Setup

You have to be root, so:

$ sudo su

Issue and update:

# apt-get update

Install a few essential applications:

# apt-get install samba ntp git

Configure Samba

Add pi username to samba

# smbpasswd -a pi

Edit the smb.conf file and add share:

# nano /etc/samba/smb.conf

Add the following at the bottom of the file

[InnovateAsterisk]
path = /
browseable = yes
writeable = yes
read only = no
create mask = 0755
directory mask = 0755
guest ok = no
security = user
write list = pi
force user = root

Restart samba service:

# service smbd restart

exit su:

# exit

Create a Certificate Authority

Note: The following steps will make both a CA certificate and a server certificate. The CA certificate will be self-signed, so you will need to copy that to your PC, and install (add) it to your Trust root CA certificate store.

Create some folders:

$ mkdir /home/pi/ca
$ mkdir /home/pi/certs
$ mkdir /home/pi/csr

Create a Root CA Key:

$ openssl genrsa -des3 -out /home/pi/ca/InnovateAsterisk-Root-CA.key 4096

(Remember the password you used) Create Root Certificate Authority Certificate:

$ openssl req -x509 -new -nodes -key /home/pi/ca/InnovateAsterisk-Root-CA.key -sha256 -days 3650 -out /home/pi/ca/InnovateAsterisk-Root-CA.crt

Something like this should be fine:

Country Name (2 letter code) [AU]: GB
State or Province Name (full name) [Some-State]: None
Locality Name (eg, city) []: None
Organization Name (eg, company) [Internet Widgits Pty Ltd]: Innovate Asterisk
Organizational Unit Name (eg, section) []: www.innovateasterisk.com
Common Name (e.g. server FQDN or YOUR name) []: Innovate Asterisk Root CA
Email Address []: youremailgoes@here

Generate Certificate Signing Request & Private Key:

$ openssl req -new -sha256 -nodes -out /home/pi/csr/raspberrypi.csr -newkey rsa:2048 -keyout /home/pi/certs/raspberrypi.key

Generate SSL V3 file:

$ nano /home/pi/csr/openssl-v3.cnf

And populate with:

authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names

[alt_names]
DNS.1 = raspberrypi.local

Generate Server Certificate:

$ openssl x509 -req -in /home/pi/csr/raspberrypi.csr -CA /home/pi/ca/InnovateAsterisk-Root-CA.crt -CAkey /home/pi/ca/InnovateAsterisk-Root-CA.key -CAcreateserial -out /home/pi/certs/raspberrypi.crt -days 365 -sha256 -extfile /home/pi/csr/openssl-v3.cnf

Generate PEM Combo Certificate:

$ cat /home/pi/certs/raspberrypi.crt /home/pi/certs/raspberrypi.key > /home/pi/certs/raspberrypi.pem

Set Permission to Key:

$ chmod a+r /home/pi/certs/raspberrypi.key

Install Asterisk from Source Code

Change to root:

$ sudo su

Install Opus dev files:

# apt-get install xmlstarlet libopus-dev libopusfile-dev

Exit root:

# exit

Wget the Asterisk source:

Note: chan_sip works fine on Asterisk 13, but chan_pjsip is rather broken. If you are using chan_pjsip, rather use Asterisk 16+, the guide is exactly the same. If you are on an x86 server, you can enable opus in make menuselect, or download it from the github project, otherwise take the opus codec out of the allow= section of the endpoint.

$ wget http://downloads.asterisk.org/pub/telephony/asterisk/asterisk-13-current.tar.gz
or
$ wget http://downloads.asterisk.org/pub/telephony/asterisk/asterisk-16-current.tar.gz

Untar the download:

$ tar -xvf asterisk-[tab]

Change to Asterisk folder

$ cd aster[tab]

Going to install again, so go back to root:

$ sudo su

Install the prerequisites:

# contrib/scripts/install_prereq install

Configure Asterisk:

# ./configure --with-pjproject-bundled

Enter menuselect, and turn off CDR, CEL, and change MOH to WAV:

# make menuselect

Call make

# make

Install the built code:

# make install 

Configure Asterisk to start automatically:

# make config

Exit root:

# exit

Configure Asterisk with Github files

Note: this section assumes you are following this guide and don't have any existing configurations in place. If you do, simply open the config files described below, and copy out the settings that you need.

Return to home folder:

$ cd ~

Clone the git project:

$ git clone https://github.com/InnovateAsterisk/Browser-Phone.git

Copy the config files:

$ sudo cp /home/pi/Browser-Phone/config/* /etc/asterisk/

Clear the existing files in static-http:

$ sudo rm /var/lib/asterisk/static-http/*

Copy the web pages:

Note: You can skip this step and simply use the hosted pages at: https://www.innovateasterisk.com/phone/ (contains a welcome screen). This page uses a Let's Encrypt Certificate, but you will still need to have a secure connection to your Asterisk box.

$ sudo cp /home/pi/Browser-Phone/Phone/* /var/lib/asterisk/static-http/

Set the file permissions:

$ sudo chmod 744 /var/lib/asterisk/static-http/*

Setup /etc/asterisk/http.conf with the following:

[general]
enabled=no ; HTTP
tlsenable=yes ; HTTPS
tlsbindaddr=0.0.0.0:443
tlscertfile=/home/pi/certs/raspberrypi.crt
tlsprivatekey=/home/pi/certs/raspberrypi.key
enablestatic=yes
sessionlimit=1000
redirect=/ /static/index.html

Note: If you are running asterisk as root (as this guide does), then you can specify port 443, if you are running as asterisk or something else, you will need to specify a port greater than 1024. You can test that this works by going to https://raspberrypi.local/httpstatus but in order to see this page, you have to download that Root CA certificate that you made earlier to your own PC. To install: On Mac, just double click it, then again double click the certificate, and select Trust Always. On windows you will need to Import it to the Certificate Manager. (If you are using Firefox Browser, you have to again install it to the Firefox Trusted Root certificates.)

Copy the Opus codec to modules:

$ sudo cp /home/pi/Browser-Phone/modules/ast-13/codec_opus_arm.so /usr/lib/asterisk/modules
or
$ sudo cp /home/pi/Browser-Phone/modules/ast-16/codec_opus_arm.so /usr/lib/asterisk/modules

Note: Asterisk 16 will check that the checksum of the .so files in modules folder matches the id gerenated at make menuselect, so you need to update the checksum in codec_opus_arm.so:

$ nano /home/pi/asterisk-16.*.0/include/asterisk/buildopts.h
Take note of the AST_BUILDOPT_SUM (copy the value)
$ sudo sed -i 's/1fb7f5c06d7a2052e38d021b3d8ca151/<value of AST_BUILDOPT_SUM>/g' /usr/lib/asterisk/modules/codec_opus_arm.so

Restart Asterisk and check the modules loaded:

$ sudo service asterisk restart
$ sudo asterisk -r
> [tab]
> exit

chan_sip or chan_pjsip?

The browser phone is compatible with both chan_sip and chan_pjsip. Follow the guide that suits your development. You will not be able to use both chan_sip and chan_pjsip in the same installation.

Note: As of writing, Asterisk 13 chan_pjsip always invites a call with m=video in the SDP (if the endpoint has any video codec) no matter what the SDP of the original inviting call has, this means that all calls appear as video calls and the "Answer with video" appears for both audio and video calls. I'm yet to find a solution.

chan_sip

Configure sip.conf

Open the original /etc/asterisk/sip.conf file and make the following changes:

websocket_enabled=yes
maxcallbitrate=5120

Add anywhere under [general]:

accept_outofcall_message=yes
auth_message_requests=no
outofcall_message_context=textmessages

Add to the bottom of /etc/asterisk/sip.conf:

; == Users

[User1](basic,webrtc)
callerid="Conrad de Wet" <100>
secret=1234

[User2](basic,webrtc)
callerid="User 2" <200>
secret=1234

[User3](basic,phones)
callerid="User 3" <300>
secret=1234

Disable chan_pjsip in /etc/asterisk/modules.conf

Its best to only use one channel driver

noload => res_pjsip.so
noload => res_pjsip_pubsub.so
noload => res_pjsip_session.so
noload => chan_pjsip.so
noload => res_pjsip_exten_state.so
noload => res_pjsip_log_forwarder.so

Configure extensions.conf

Update the /etc/asterisk/extensions.conf to the following:

[general]
static=yes
writeprotect=yes
priorityjumping=no
autofallthrough=no

[globals]
ATTENDED_TRANSFER_COMPLETE_SOUND=beep

[textmessages]
exten => 100,1,Gosub(send-text,s,1,(User1))
exten => 200,1,Gosub(send-text,s,1,(User2))
exten => 300,1,Gosub(send-text,s,1,(User3))
exten => e,1,Hangup()

[subscriptions]
exten => 100,hint,SIP/User1
exten => 200,hint,SIP/User2
exten => 300,hint,SIP/User3

[from-extensions]
; Feature Codes:
exten => *65,1,Gosub(moh,s,1)
; Extensions 
exten => 100,1,Gosub(dial-extension,s,1,(User1))
exten => 200,1,Gosub(dial-extension,s,1,(User2))
exten => 300,1,Gosub(dial-extension,s,1,(User3))
; Anything else, Hangup
exten => _[+*0-9].,1,NoOp(You called: ${EXTEN})
exten => _[+*0-9].,n,Hangup(1)
exten => e,1,Hangup()

[moh]
exten => s,1,NoOp(Music On Hold)
exten => s,n,Ringing()
exten => s,n,Wait(2)
exten => s,n,Answer()
exten => s,n,Wait(1)
exten => s,n,MusicOnHold()

[dial-extension]
exten => s,1,NoOp(Calling: ${ARG1})
exten => s,n,Dial(SIP/${ARG1},30)
exten => s,n,Hangup()
exten => e,1,Hangup()

[send-text]
exten => s,1,NoOp(Sending Text To: ${ARG1})
exten => s,n,Set(PEER=${CUT(CUT(CUT(MESSAGE(from),@,1),<,2),:,2)})
exten => s,n,Set(FROM=${SHELL(asterisk -rx 'sip show peer ${PEER}' | grep 'Callerid' | cut -d':' -f2- | sed 's/^\ *//' | tr -d '\n')})
exten => s,n,Set(CALLERID_NUM=${CUT(CUT(FROM,>,1),<,2)})
exten => s,n,Set(FROM_SIP=${STRREPLACE(MESSAGE(from),<sip:${PEER}@,<sip:${CALLERID_NUM}@)})
exten => s,n,MessageSend(sip:${ARG1},${FROM_SIP})
exten => s,n,Hangup()

Restart Asterisk or Reload SIP and Dialplan:

$ sudo asterisk -r
> sip reload
> dialplan reload

chan_pjsip

Configure pjsip.conf

Open the original /etc/asterisk/pjsip.conf file and make the following changes:

; == Users

[User1](basic_endpoint,webrtc_endpoint)
type=endpoint
callerid="Conrad de Wet" <100>
auth=User1
aors=User1
[User1](single_aor)
type=aor
mailboxes=User1@default
[User1](userpass_auth)
type=auth
username=User1
password=1234

[User2](basic_endpoint,webrtc_endpoint)
type=endpoint
callerid="User Two" <200>
auth=User2
aors=User2
[User2](single_aor)
type=aor
[User2](userpass_auth)
type=auth
username=User2
password=1234

[User3](basic_endpoint,phone_endpoint)
type=endpoint
callerid="User Three" <300>
auth=User3
aors=User3
[User3](single_aor)
type=aor
[User3](userpass_auth)
type=auth
username=User3
password=1234

Disable chan_sip in /etc/asterisk/modules.conf

It's best to only use one channel driver

noload => chan_sip.so

Configure extensions.conf

Update the /etc/asterisk/extensions.conf to the following:

[general]
static=yes
writeprotect=yes
priorityjumping=no
autofallthrough=no

[globals]
ATTENDED_TRANSFER_COMPLETE_SOUND=beep

[textmessages]
exten => 100,1,Gosub(send-text,s,1,(User1))
exten => 200,1,Gosub(send-text,s,1,(User2))
exten => 300,1,Gosub(send-text,s,1,(User3))

[subscriptions]
exten => 100,hint,PJSIP/User1
exten => 200,hint,PJSIP/User2
exten => 300,hint,PJSIP/User3

[from-extensions]
; Feature Codes:
exten => *65,1,Gosub(moh,s,1)
; Extensions
exten => 100,1,Gosub(dial-extension,s,1,(User1))
exten => 200,1,Gosub(dial-extension,s,1,(User2))
exten => 300,1,Gosub(dial-extension,s,1,(User3))
; Anything else, Hangup
exten => _[+*0-9].,1,NoOp(You called: ${EXTEN})
exten => _[+*0-9].,n,Hangup(1)

exten => e,1,Hangup()

[moh]
exten => s,1,NoOp(Music On Hold)
exten => s,n,Ringing()
exten => s,n,Wait(2)
exten => s,n,Answer()
exten => s,n,Wait(1)
exten => s,n,MusicOnHold()

[dial-extension]
exten => s,1,NoOp(Calling: ${ARG1})
exten => s,n,Set(JITTERBUFFER(adaptive)=default)
exten => s,n,Dial(PJSIP/${ARG1},30)
exten => s,n,Hangup()

exten => e,1,Hangup()

[send-text]
exten => s,1,NoOp(Sending Text To: ${ARG1})
exten => s,n,Set(PEER=${CUT(CUT(CUT(MESSAGE(from),@,1),<,2),:,2)})
exten => s,n,Set(FROM=${SHELL(asterisk -rx 'pjsip show endpoint ${PEER}' | grep 'callerid ' | cut -d':' -f2- | sed 's/^\ *//' | tr -d '\n')})
exten => s,n,Set(CALLERID_NUM=${CUT(CUT(FROM,>,1),<,2)})
exten => s,n,Set(FROM_SIP=${STRREPLACE(MESSAGE(from),<sip:${PEER}@,<sip:${CALLERID_NUM}@)})
exten => s,n,MessageSend(pjsip:${ARG1},${FROM_SIP})
exten => s,n,Hangup()

Restart Asterisk or Reload PJSIP and Dialplan:

$ sudo asterisk -r
> module reload res_pjsip.so
> dialplan reload

Screenshots

Audio Call with 3rd Party Conference

Audio Call with Transfer

Image of Main Interface

Mobile UI

Call Stats

Call Recording Format

Video Call Presenting Camera

Video Call Presenting Scratchpad

Video Call Presenting Video File

browser-phone's People

Contributors

amitiyer avatar brambruning avatar desmondsimcode avatar gxovano avatar innovateasterisk avatar wangzhenzhe avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

browser-phone's Issues

video calls between two webrtc peers

Hi,

I noticed that video calls between two webrtc peers are not working as expected.

First of all, when both peers use Firefox ESR there's no option to make a video call -- only audio calls from both ends.

When using Chrome, in my case I see that only one of the two peers can initiate a video call (when both have cameras). The "video icon" is missing in one of the two.

The webrtc extensions are defined in Asterisk as (pjsip):

[doctor]
type=aor
max_contacts=5
remove_existing=yes

[doctor]
type=auth
auth_type=userpass
username=doctor
password=doctor 

[doctor]
type=endpoint
aors=doctor
auth=doctor
dtls_auto_generate_cert=yes
webrtc=yes
context=default
disallow=all
allow=alaw,ulaw,opus,gsm,vp8,h264
rtp_symmetric=yes
force_rport=yes

[patient]
type=aor
max_contacts=5
remove_existing=yes

[patient]
type=auth
auth_type=userpass
username=patient
password=patient

[patient]
type=endpoint
aors=patient
auth=patient
dtls_auto_generate_cert=yes
webrtc=yes
context=default
disallow=all
allow=alaw,ulaw,opus,gsm,vp8,h264
rtp_symmetric=yes
force_rport=yes

Is this a bug, something I misconfigured, or a network-related issue?

Regards,

Vieri

DID length to decide if buddy is extension or contact

Hi,

I'd like to customize the way phone.js decides how to create a "buddy" from an incoming call. This patch is trivial, but at least it allows to enable video calls for certain contacts even if not "local".

--- phone.original.js   2020-06-08 13:24:31.741407729 +0200
+++ phone.js    2020-06-08 14:06:34.090022002 +0200
@@ -162,6 +162,8 @@
 var RecordingVideoFps = parseInt(getDbItem("RecordingVideoFps", 12));       // The Frame Per Second of the Video Track recording
 var RecordingLayout = getDbItem("RecordingLayout", "them-pnp");         // The Layout of the Recording Video Track (side-by-side | us-pnp | them-pnp | us-only | them-only)

+var DIDlength = parseInt(getDbItem("DIDlength", 6));  // DID length from which to decide if an incoming caller is a "contact" or an "extension".
+
 // Utilities
 // =========
 function uID(){
@@ -1648,7 +1650,7 @@
         var json = JSON.parse(localDB.getItem("UserBuddiesJson"));
         if(json == null) json = InitUserBuddies();

-        if(did.length > 6){
+        if(did.length > DIDlength){
             // Add Regular Contact
             var id = uID();
             var dateNow = utcDateNow();

I then call a custom "pre" script loaded before phone.js with:

customLocalDB.setItem("DIDlength", 7);

This is a quick fix for my specific setup, but it might be of use to others.

profileUserID and uID()

Hi,

I'd like to understand how profileUserID and uID() work.
The problem I'm facing is that unfortunately some users don't log out of their OS session, so basically several users might end up "sharing" an OS user session/profile even though I set up LDAP/AD authentication to access Browser-Phone. Authentication works fine, and each user registers with his/her Asterisk extension. The problem is that all "Browser-Phone users" share the same buddy list, among other parameters. So, in order to address this, I wonder if I just need to set myself the value of profileUserID to a value derived from the LDAP/AD user login (eg. username+random_string).

Any thoughts?

Voicemail Notification

If a user has unread voicemail, there should be an icon display, and audio response. Clicking the icon should dial the voicemail feature code (if set). Setting the voicemail feature code should be done in the settings section.

app view and screen scroll when in video call

Depending on screen resolution, etc., the text input section "Type your message here" can disappear way below the screen area when on a video call. Since there's no scroll, it is difficult to write text messages there.
Tested on a desktop PC with Firefox.

video calls and optional input

Hi,

Currently, it seems that for a one-on-one video call to succeed, both webrtc clients need to have a webcam + microphone available as input devices. Would it make sense to ignore an input web cam if it's not connected, thus allowing a video call with video only one way? For instance, browser A with a camera video calls browser B without a camera. Currently, if B "answers with video", the call fails and hangs up. If B answers "with audio only" then all's well.
So maybe, B could answer with video, and A would stream it's video source to B even though B wouldn't stream any video to A, just audio.

I don't know if it's hard to implement, but it's just one of those things that might come in handy.

Action URLs

Create a configurable set of Action URLs / Web Hooks.

Scratchpad Toolbar

Add a toolbar to the Scratchpad feature. Allow users to change colour, prush, add a picture, and clear the screen.

[WISHLIST] dialpad

Hi,

I don't know if it's a "hidden" feature or if it's simply not implemented, but is it possible to have a dialpad on the main screen or a simple text input field to write a number and press enter or click a dial button? Once the SIP extension is registered, it would be easier to just dial or type a number that Asterisk then takes care of. Requiring to "add a contact" before placing a call is a show stopper when you want to either quickly dial short dynamic Asterisk extensions or call out to, say, hundreds of different destinations that you will probably only use once.

Regards,

Vieri

Trim Buddy Stream

Rather than a full delete, the Delete option could offer a trim, to remove events in the stream up to a specific number or date.

setting for video calls in fullscreen

Hi,

Another interesting improvement would be to add yet another setting via setItem to decide, upon user login, if video calls should start in fullscreen mode or not.
That would be useful for specific patient profiles calling in: they would receive a URL via SMS, they click and open Browser-Phone which identifies them as patients, auto-launch freedial to enter an Asteisk queue right away, and in fullscreen.
Simple and with a clean trimmed down UI for no-hassle patients.

Scaling Up

Yes, I'm doing that now, but am facing a new problem. The idea of provisioning "buddies" is a great way to help people search through a large directory. Unfortunately, I have to provision hundreds of contacts/buddies (healthcare facility) even if I try to trim these down according to who logs in. The problem I see is that the browser becomes sluggish (too many contacts make the whole page slower). I also have a UI problem because some contact/extension names are long and wrap around in a second line. That causes overlapping with the next element (the hint/presence status of the next buddy is partially hidden). Anyway, I'm going to have to rethink how and if I'm going to provision buddies.
So at this point, I guess we can close this issue.
Thanks!

Originally posted by @vieridipaola in #37 (comment)

Busy Tone

Play an audible busy tone if call is hungup or call not answered.

Fix a dialplan issue in the send-text context

Hello,

Can you fix another typo in the send-text context (The FROM variable setting):
This is the original context:

[send-text]
exten => s,1,NoOp(Sending Text To: ${ARG1})
exten => s,n,Set(PEER=${CUT(CUT(CUT(MESSAGE(from),@,1),<,2),:,2)})
exten => s,n,Set(FROM=${SHELL(asterisk -rx 'sip show peer ${PEER}' | grep 'Callerid' | cut -d':' -f2- | sed /^\ *//' | tr -d '\n')})
exten => s,n,Set(CALLERID_NUM=${CUT(CUT(FROM,>,1),<,2)})
exten => s,n,Set(FROM_SIP=${STRREPLACE(MESSAGE(from),<sip:${PEER}@,<sip:${CALLERID_NUM}@)})
exten => s,n,MessageSend(sip:${ARG1},${FROM_SIP})
exten => s,n,Hangup()

Please fix to:

[send-text]
exten => s,1,NoOp(Sending Text To: ${ARG1})
exten => s,n,Set(PEER=${CUT(CUT(CUT(MESSAGE(from),@,1),<,2),:,2)})
exten => s,n,Set(FROM=${SHELL(asterisk -rx 'sip show peer ${PEER}' | grep 'Callerid' | cut -d':' -f2- | sed 's/^\ *//' | tr -d '\n')})
exten => s,n,Set(CALLERID_NUM=${CUT(CUT(FROM,>,1),<,2)})
exten => s,n,Set(FROM_SIP=${STRREPLACE(MESSAGE(from),<sip:${PEER}@,<sip:${CALLERID_NUM}@)})
exten => s,n,MessageSend(sip:${ARG1},${FROM_SIP})
exten => s,n,Hangup()

Thank you,

Daniel Friedman

http.conf is missing

Hello,

Can you update the configuration of the http.conf? it is confusing the users that install this project from your manual.

debugging remote devices

Hi,

I was wondering what would the best approach be to try to debug errors in remote handheld devices such as smartphones or tablets. Not having control over these end-user devices, whatever goes to console.log is basically unavailable. Could it be possible to make a function that sends each console message to a custom server via ajax calls? Something like consoleRemote(msg); when one would need to "setItem" a web service URL. That would allow admins to see at least some of the erro message that could occur.
Yet another workaround would be to open a window wth all the debugging info displayed in it, but only if the admin sets a specific "setItem" variable for a given user login. The user would then have to copy all of the on-screen text and manually send it somehow to the admin.

Do you have any other ways of debugging remote client-side issues?

I'm asking because I've had two cases of clients unable to call using this app, and it was impossible to get enough info from the users. Maybe issues detecting the microphone, no idea.

Thanks

Naming

Hi,

Thank you so much for developing and publishing this amazing project. It's really impressive what you have achieved. Thank you also for publishing it under a free software license.

Either knowingly or unknowingly, by choosing Asterisk as a backend, you created the most efficient browser softphone / web video conferencing application. This is because Asterisk is written in C, the most efficient programming language for server resources usage. Jitsi Meet is also an interesing web conferencing application but because the developers made the wrong choice when they used Java as programming language for the backend, Jitsi Meet will never surpass your application in performance and popularity. Thank you also for not using node.js and the related frameworks. I'm strongly convinced that node.js is a big failure: because of the wrong programming language it uses: JavaScript is only suitable for client-side programming, people shouldn't have 'stretched' it to use it for server-side programming. So, by using only HTML, JavaScript and CSS for the frontend and Asterisk as backend, you created the best architecture possible for this kind of application.

I think your application will be the only complete, free and open source, self-hosted, Skype replacement and also a self-hosted Zoom replacement. But for this to happen I think a new name, simple and easy to pronounce would fit better than 'Browser Phone'. What do you think about Sipspace or sipspace ?

post js script and user agent

Hi,

What is the best way to call custom code as soon as the user agent is ready and registered?

This is what I currently wrote in a custom js script loaded right after phone.js:

$(document).ready(function () {
    var onUserAgent = window.setInterval(function () {
        if ( userAgent != null && userAgent.isRegistered() == true ) {
            console.log("This clause should be called only once as soon as the user agent is ready.");
	    // do stuff...
            console.log('Removing setInterval');
            clearInterval(onUserAgent);
        }
    }, 4 * 1000);
});

However, I'm seeing the 2 console log messages twice instead of just once.
So, either clearInterval is not working as I expect it to, or I've misunderstood the whole logic.

provisioning upon authentication

Hi,

I was wondering if provisioning is contemplated somehow, or what would the best approach be.
The idea in my case is that a user needs to authenticate in order to open the "webphone". Each authenticated user would then have their account set up automatically, and probably even the contact list.
For instance, if I use Apache HTTP I can force LDAP authentication, just to name one, and then somehow override the window.localStorage items. This is not my domain, and I usually don't program in javascript, but is there an "elegant" way to override what phone.js does? Can I call localStorage.setItem() in another custom js and load it in the html?
I don't care if the user can then modify the account details -- I just need to provision the SIP login details on HTTP authentication (they would be overwritten on each Apache HTTP login).

Provisioning the contact list actually doesn't make much sense to me right now. However, allowing an LDAP search for contacts to be "imported" would be neat. The "Find someone" input text field could be linked to an LDAP query, and the user would then have the option of importing contacts to his/her "buddy list". That would be useful when there are hundreds of internal extensions. Then again, all this can be outsourced on an intranet site, just as long as Browser-Phone can receive a contact/phone number via GET, POST, whatever, and eg. ask the user to import it.
Is there a way to fire up the AddSomeoneWindow function on demand?

Regards,

Vieri

share a hi-res image

Hi,

I can see that Browser-Phone can share quite a few things (very nice!) during a call.
I was wondering what would the best approach be if I wanted a patient to share a high resolution picture with his/her doctor while on a video call. Suppose it's a session of dermatology teleconsultation and that the MD needs to see the patient's skin up close because he/she has a rash or whatever. The video call will not be enough. Sharing a video is OK but still not quite right. Sharing a picture taken by the patient with his/her camera on PC might be better (higher resolution, etc.) just as long as the doctor can actually zoom in and out (or simply download it -- but without the need to ask the patient to send it via e-mail with all the legal issues related to privacy). Ideally, it would all be done within the Browser-Phone app. I don't know if it would be easy to "share a picture from local hard disk" just like it is currently possible with videos.
Another possibility is that if the patient's webcam is good enough and configured to the max of its resolution in the video call, the doctor could be able to "capture" a video frame at any given moment and then analyze it on his/her PC (by simply downloading it and using local software to zoom in/out, take measurements, etc.).
Would it be possible to allow an endpoint to "capture" a high-res video frame and save it locally as a picture with just a click of a button?

I'm just asking to get an idea as to how far we can stretch this great piece of software...

Thanks

download recording

Is it possible for the user to download a call recording? The feature is really useful and elegantly displayed, but the user would rather save a copy in a server's directory (with backup support) instead of the browser's cache (which can easily be erased for many reasons).
Minor issue but useful.

Blacklists & Whitelists

Blacklists can prevent users being "bugged" based on caller ID
Whitelists can be used in combination with extreme blacklists.

allow disabling of specific elements

I currently disable a few things when a specific user logs in:

document.getElementById("BtnFreeDial").style.display = "none";
document.getElementById("BtnAddSomeone").style.display = "none";
document.getElementById("BtnCreateGroup").style.display = "none";
document.getElementById("txtFindBuddy").style.display = "none";

I would also like to remove the "Add someone" option from the profile menu, but that requires changing some js code.

In:
function ShowMyProfileMenu

the dhtmlx menu ids can be modified with:

menu.splice

as it is already the case for 'enabledGroupServices'.

Could another setting be created so that these elements can be hidden?

eg.

enabledAddSomeone
enabledFreeDial
enabledFindBuddy

Provisioning buddies and auto-dial

Hi,

I was wondering if we could add a simple way to automatically add a list of buddies/contacts. Some kind of provisioning (of a contact list)...

Here's what I do now. In my index file I include a 'custom_post.php' file right after phone.js.

# cat custom_post.php
<?php

header('Content-Type: application/javascript');

?>
function ProvisionBuddyList(number, displayName, mobileNum, contactNum1, contactNum2, position, description, email){
    number = number.trim();
    if(number.length == 0){
        console.log("Cannot provision buddy");
        return;
    }
    console.log("Provisioning buddy " + displayName + " with number " + number);

    var buddyObj = FindBuddyByNumber(number);
    if(buddyObj == null) buddyObj = FindBuddyByExtNo(number);
    // Make new contact of its not there
    if(buddyObj == null) {

        // Add Contact / Extension
        var json = JSON.parse(localDB.getItem(profileUserID + "-Buddies"));
        if(json == null) json = InitUserBuddies();

        if(number.length > DidLength){
            // Add Regular Contact
            var id = uID();
            var dateNow = utcDateNow();
            json.DataCollection.push(
                {
                    Type: "contact",
                    LastActivity: dateNow,
                    ExtensionNumber: "",
                    MobileNumber: mobileNum,
                    ContactNumber1: number,
                    ContactNumber2: contactNum2,
                    uID: null,
                    cID: id,
                    gID: null,
                    DisplayName: displayName,
                    Position: "",
                    Description: description,
                    Email: email,
                    MemberCount: 0
                }
            );
            var buddyObj = new Buddy("contact", id, displayName, "", mobileNum, number, contactNum2, dateNow, description, email);
            AddBuddy(buddyObj, false, false);
        }
        else {
            // Add Extension
            var id = uID();
            var dateNow = utcDateNow();
            json.DataCollection.push(
                {
                    Type: "extension",
                    LastActivity: dateNow,
                    ExtensionNumber: number,
                    MobileNumber: mobileNum,
                    ContactNumber1: contactNum1,
                    ContactNumber2: contactNum2,
                    uID: id,
                    cID: null,
                    gID: null,
                    DisplayName: displayName,
                    Position: position,
                    Description: "",
                    Email: email,
                    MemberCount: 0
                }
            );
            var buddyObj = new Buddy("extension", id, displayName, number, mobileNum, contactNum1, contactNum2, dateNow, position, email);
            AddBuddy(buddyObj, false, false, true);
        }
        // Update Size:
        json.TotalRows = json.DataCollection.length;

        // Save To DB
        localDB.setItem(profileUserID + "-Buddies", JSON.stringify(json));

    }
}

$(document).ready(function () {

console.log('Start provisioning buddies');

<?php

@require_once('custom_buddies.php');

?>

console.log('Update buddy list');
UpdateBuddyList();

});

The file custom_buddies.php is generated by another script, and it's basically a list of calls to the function defined above:

ProvisionBuddyList('1234', 'John Doe', '', '', '', '', '', '');
ProvisionBuddyList('5678', 'Mr Smith', '', '', '', '', '', '');
...etc...

I usually have hundreds of these function calls.

Now, in order to not have to maintain this provisioning function myself, would it be possible to somehow include it in phone.js?
I know that calling this function hundreds of times can block the UI, but I can live with that. Maybe a "worker" would be better off, but I'm not sure how to program that just yet.
I also know that calling this may occur before userAgent is not null.

I would also like to expand on this topic and ask if it is possible to implement some sort of auto-dial.
In this case, what I need to do is:

  1. not provide hundreds of buddies but just one (actually it would be an Asterisk extension leading to a 'queue')
  2. disable the "Find someone" and "Add someone" UI elements
  3. remove or disable 'Add someone' from the user's dropdown menu
  4. on page load, auto-dial the buddy that has been provisioned (auto-dial with video if available)

DialByLine and extensions that are not numbers

Hi,

I usually have numbers for extensions, but in some cases I use alphanumeric strings such as "patient", "doctor", etc. In other words, I have pjsip extensions registering as strings without numbers or containing numbers and letters.

The problem is now with the DialByLine function as it tries to extract the number with \d before @ in the URI. It fails of course if it's not an integer, and the peer can't be contacted.

Can't an "extension" be just about anything?

Auto Answer with Video

Hi,

The "Auto Answer" feature only answers an audio call.
Is it possible to Auto Answer with Video?

Regards,

Vieri

dependency

This isn't really an issue... Anyway, I guess I can post this here.

Just a note on the dependency list (README): fontawesome 4.7.0 should be added, and I believe the Roboto fonts are also missing.

ring/dial tone never stops

Hi,

When dialing a number that's really an IVR reachable through a IAX2 trunk on a LAN Asterisk server, sometimes the "ring tone" goes on forever, even after the call is answered by the IVR. At times, there is no "ring tone", ever, not even while waiting for the IVR to start.
This is probably not a Browser Phone issue because the ring tone or dial tone I'm hearing is non of the mp3 files in the project. It's surely the dial tone of the second Asterisk server I'm connecting to where the IVR resides.

Likewise though, the ring tone that's shipped in this project sometimes plays, and sometimes it doesn't. When receiving call, I understand it should always "ring", but it sometimes doesn't. This is probably a client browser issue, but I haven't seen anything weird in the console so far.

I'm just posting this here if anyone has seen this before.

disable account settings

Hi,

The option:

setItem("EnableAccountSettings", "0")

works fine as it grays out the tab element to configure account details.

However, when I click on the "configure extension" from the user's dropdown menu, this very tab is shown first and, even if it's grayed out, I can still see all the settings, scroll down and press the save button.
A possible solution would be to only display the "enabled tabs", or focus on an enabled tab on window open.

welcome window on Chrome

Hi,

Is it just me or is the WelcomeWindow not displaying the buttons below to dismiss it in Chrome?
Git cloned today.

Can't cancell incomming call

Hello.
Thx for you job! Very nice project!
Please fix this:

function RejectCall(buddy) {
    var session = getSession(buddy);
    if (session = null) {
        console.warn("Reject failed, null session");
        $("#contact-" + buddy + "-msg").html(lang.call_failed);
        $("#contact-" + buddy + "-AnswerCall").hide();
    }

i think need
if (session == null) {

XMPP support

Allow a user to select between xmpp and sip in the type configuration.

Raspbx Instructions

As raspbx is a famous image to start using asterisk/freepbx on raspberry pi , would be great if you add intructions to setup with it.
Thx

messages within a call

Hi,

I don't know if I have to set up something specifically in Asterisk so that "instant messages" work properly in Browser-Phone.

When I'm on a call, if I type anything in the "Type your message here" input field then an "anonymous" call is initiated to the peer. Even if the peer answers this anonymous call, there's no visual on the message interchange.

I'll look into this further if I get the chance and give some more info, but I think it's easy to reproduce.

Thanks

dialpad with letters

Hi,

A simple but useful improvement would be to add letters beneath each number in the dialpads, especially the one used during an active call.
For instance, add "ABC" under "2", "DEF" under "3", and so on.
That's because sometimes it's an easy way to remember how to use some IVRs.
For instance, one might prefer to think that one is dialing "derma" instead of the extension "33762".
Also, we have some accesses / calls restricted and require some sort of PIN or authentication. It's often easier to remember a name instead of a number even if the digits sent are numbers, of course.

Race condition on delete Buddy

Race condition on delete Buddy when buddy type is extension. Occurs when the unsubscribe() is called and removed from the subscriptions array.

Firefox & Safari Present Video

Can't present video in Firefox and Safari. Video loads, but does not play automatically, and the video is not presented to the B side.

adding buddies and starting calls automatically

Hi,

I was wondering if we could get this patch upstream somehow. It is incomplete, and that's why I'm posting it here instead of filing a PR. The idea is simple. If a user types in a number or extension on the buddy search input box, a buddy will be created automatically, and a call will also be initiated. My patch below will disable the "addsomeonewindow" which, of course, is not desired (but due to lack of time, I need to finish other tasks first).

--- phone.original.js   2020-06-09 18:10:35.034098297 +0200
+++ phone.js    2020-06-11 12:22:41.712065649 +0200
@@ -300,6 +300,88 @@
     HidePopup();
 }

+// CUSTOM: automatically add buddy and start call
+// ==========
+function AutoAddBuddy(peerid){
+    peerid = peerid.trim();
+    if(peerid.length == 0){
+       console.log("Cannot Auto Add Buddy");
+       return;
+    }
+    console.log("Auto Add Buddy " + peerid);
+
+    var CurrentCalls = CallSessions.length;
+
+    var buddyObj = FindBuddyByNumber(peerid);
+    if(buddyObj == null) buddyObj = FindBuddyByExtNo(peerid);
+    // Make new contact of its not there
+    if(buddyObj == null) {
+        var json = JSON.parse(localDB.getItem("UserBuddiesJson"));
+        if(json == null) json = InitUserBuddies();
+
+        if(peerid.length > DidLength){
+            // Add Regular Contact
+            var id = uID();
+            var dateNow = utcDateNow();
+            json.DataCollection.push(
+                {
+                    Type: "contact",
+                    LastActivity: dateNow,
+                    ExtensionNumber: "",
+                    MobileNumber: "",
+                    ContactNumber1: peerid,
+                    ContactNumber2: "",
+                    uID: null,
+                    cID: id,
+                    gID: null,
+                    DisplayName: "",
+                    Position: "",
+                    Description: "",
+                    Email: "",
+                    MemberCount: 0
+                }
+            );
+            buddyObj = new Buddy("contact", id, "", "", "", peerid, "", dateNow, "", "");
+            AddBuddy(buddyObj, true, (CurrentCalls==0));
+        }
+        else {
+            // Add Extension
+            var id = uID();
+            var dateNow = utcDateNow();
+            json.DataCollection.push(
+                {
+                    Type: "extension",
+                    LastActivity: dateNow,
+                    ExtensionNumber: peerid,
+                    MobileNumber: "",
+                    ContactNumber1: "",
+                    ContactNumber2: "",
+                    uID: id,
+                    cID: null,
+                    gID: null,
+                    DisplayName: "",
+                    Position: "",
+                    Description: "",
+                    Email: "",
+                    MemberCount: 0
+                }
+            );
+            buddyObj = new Buddy("extension", id, "", peerid, "", "", "", dateNow, "", "");
+            AddBuddy(buddyObj, true, (CurrentCalls==0), true);
+        }
+        // Update Size:
+        json.TotalRows = json.DataCollection.length;
+
+        // Save To DB
+        localDB.setItem("UserBuddiesJson", JSON.stringify(json));
+
+        UpdateBuddyList();
+    }
+    var buddy = buddyObj.identity;
+
+    VideoCall(buddy);
+}
+
 // UI Windows
 // ==========
 function AddSomeoneWindow(){
@@ -1433,7 +1515,9 @@
         CreateGroupWindow();
     });
     $("#BtnAddSomeone, #BtnAddSomeone1").on('click', function(event){
-        AddSomeoneWindow();
+        // AddSomeoneWindow();
+        // CUSTOM: add as contact buddy whatever is in txtFindBuddy (TODO: make sure buddy doesn't already exist, allow to add as extension too)
+        AutoAddBuddy($("#txtFindBuddy").val());
     });
     $("#ConfigureExtension").on('click', function(event){
         ConfigureExtensionWindow();

Do you think you can modify this and merge it in?
The idea is to keep the original behavior/design the same (add a buddy with all its details), but adding the ability to quickly dial someone. In some hectic environments (as in a hospital), users are often impatient, and they will find that the manual creation of a buddy in order to simply dial out (eg. a patient) is overly time consuming.
So, if you have the time (or I can try to pick this up again later), the idea is that when a user clicks on the "Add someone" icon, there should be a dropdown menu with "Add with details" and "Quick Add and Call". The first option would open the addsomeonewindow form (and mayube even pass it the extension or number), and the second one would launch the code I wrote (AutoAddBuddy).

Thanks

Call button and text within search input box

Hi,

If user types something in the "Find someone" text input field then maybe when click the "call" button next to it, it should simply take its value and DialByLine (directly, without the need to show the dialpad). Actually, even an ENTER key event could be enough to trigger the call.

Ideally, it would default to a video call with a downgrade to audio if one of the two peers does not support video.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.