tSIP softphone: "script" button type and event scripts

"Script" button executes small Lua script when clicked. It may partially duplicate script.dll plugin functionality, but while script.dll is intended to control softphone for arbitrary time (i.e. use delays) script button should be limited to running code that executes immediately. Same scripts can be also executed automatically, on events like call state change (call confirmed, call disconnected, etc.), registering or unregistering.

To assign Lua script to button create "script" subdirectory in softphone folder and place script file in this location. Use any text editor for editing script and "print" function for debugging purposes.
Script button configuration

Lua is extended with following commands:

Call(number)
enter specified digits and start calling
Hangup()
end current call
Answer()
answer current incoming call
SetDial(number)
set number edit field
GetDial()
get dial edit content
GetClipboardText()
text from system clipboard
SetClipboardText(text)
set text in system clipboard
Sleep(ms)
pause execution for specified time in miliseconds; not recommended as UI is blocked but can be used in combination with Beep() to create audible feedback
Beep(freq, time)
equivalent for WinAPI Beep() (PC speaker or - with 64 bit windows - default audio output)
ShowMessage
display standard modal Win32 message
MessageBox
direct equivalent of WinAPI function with the same name
  MessageBox("message with just [OK] button", "message title", 0)
  MessageBox("message with ICON_INFORMATION", "message title", 64)
  local res = MessageBox("message with MB_YESNO and question icon", "message title", 4+32)
  if res == 6 then
  	ShowMessage("\"Yes\" was pressed")
  else
  	ShowMessage("Result is other than \"Yes\"")
  end  
  
InputQuery
direct equivalent of VCL function with the same name, displays modal dialog allowing to take text input from the user
  local caption = "Some dialog caption"
  local prompt = "Some dialog prompt"
  local defaultText = "Default text"
  local text, isAccepted = InputQuery(caption, prompt, defaultText)
  if isAccepted then
  	ShowMessage("Dialog accepted, text = " .. text)
  else
  	ShowMessage("Dialog was not accepted")
  end  
  
SwitchAudioSource(module, device)
change audio source used during current call or streaming, e.g. SwitchAudioSource("aufile", "test.wav"), SwitchAudioSource("winwave", "USB Phone")
SendDtmf(digits)
send DTMF characters to current call, e.g. SendDtmf("1234*#)
BlindTransfer(phone_num)
transfer (blind) current call to specified destination, e.g. BlindTransfer("123")
GetCallState()
returns current call state: integer value according to Callback::ua_state_e, i.e.
	enum ua_state_e
	{
		CALL_STATE_CLOSED = 0,
		CALL_STATE_INCOMING, // 1
		CALL_STATE_OUTGOING, // 2, etc.
		CALL_STATE_TRYING,
		CALL_STATE_RINGING,
		CALL_STATE_PROGRESS,
		CALL_STATE_ESTABLISHED,
	}   
    
GetRecordFile()
returning name of call recording file, empty string if there is no recording; valid after call is established (recording started), cleared on new call, intented to be used mostly at CALL_STATE_CLOSED state
IsCallIncoming()
returns non-zero if call direction = outgoing
GetCallPeer()
returning caller or callee number (i.e. second party, depending on call direction)
GetStreamingState()
information if RTP streaming is currently active - int as in Callback::paging_tx_state_e enum
GetInitialCallTarget() and SetInitialCallTarget(number)
function pair intented to be use for "on making call" event, allowing to override number dialed by the user - see HOWTO list, SIP originate for example use
ShellExecute(...)
built-in Lua os.execute() displays nasty command line windows; this function gives access to WinAPI ShellExecute; example: ShellExecute("open", "nircmd.exe", "speak text \"Luke, I am your father\"", nil, 1)
SetButtonCaption(btnId, text)
set text for button with specified id
SetButtonDown(btnId, state)
set "pressed" (0 or 1) state for button with specified id
SetButtonImage(btnId, file.bmp)
set bitmap for button with specified id
PluginSendMessageText(dllName, text)
pass text (command, data...) to specified dll plugin (must be supported by dll itself)
srcType, srcTypeIsSet = GetExecSourceType()
check what type of event was caused script execution; for script assigned to button srcType would be equal to 0, see ScriptExec.h for full list of event types
srcId, srcIdIsSet = GetExecSourceId()
get additional info for execution origin; for scripts assigned to button srcId would containt id of the button thus same lua source file could be assigned to multiple buttons and run in different way depending on execution source
number, state = GetBlfState(contactId)
get BLF state id for specified contact; this function is intented to be called from "on blf state" event where contactId is passed as GetExecSourceId()
status = RecordStart(filename, channels)
start recording current call into specified file, either mono (channels = 1) or stereo (channels = 2)
filename = GetExeName()
get application executable name with full path
state = GetRecordingState()
check if recording is running
inviteText = GetCallInitialRxInvite()
get full text of initial INVITE received for incoming call
description = GetContactName(number)
get number description from phonebook

As function list grows some of them were separated into "tsip_winapi" module.

hWnd = FindWindow(className, windowName)
finds handle to window, see WinAPI
SendMessage(hWnd, msg, wParam, lParam)
sends message to window with specified handle, see WinAPI
Example:
local winapi = require("tsip_winapi")
local hWnd = winapi.FindWindow(nil, "Opera Video Cache Player")
if hWnd ~= 0 then
	-- 16 = WM_CLOSE
	print("Sending WM_CLOSE\n")
	winapi.SendMessage(hWnd, 16, 0, 0) 
else
	print("Window not found\n")
end

Functions that allow passing data between different scripts or from one script execution to another are worth special mention. As scripts are running in GUI thread context they are intented to run to completion in short time (i.e. use of Sleep() should be limited even if it does not block GUI message processing) and they are mostly uninterruptible. As some uses require keeping some state data (e.g. original call target that was replaced in case of SIP originate function) following function were added:

SetVariable("name", "value")
set text "value" for variable with specified "name" (variables are holding text and are indexed by text)
value, isset = GetVariable("name")
read back variable value; function returns two variables (Lua can do this) and if variable was not set before then isset equals 0
ClearVariable("name")
"unset" variable (remove "name" from variables map)
ClearAllVariables()
clear ("unset") all variables

Output of print() is passed to application log window.

Examples

Call to number from clipboard

txt = GetClipboardText()
print(string.format("Clipboard text = %s, dialing...\n", txt))
Call(txt)  
  

"Normalize" number entered in dial box

-- get number from softphone dial edit
txt = GetDial()
print(string.format("Dial text = %s\n", txt))
-- remove leading zeroes
nonzero = 1
for i=1, string.len(txt) do
	if string.sub(txt, i, i) ~= "0" then
		do
			print(string.format("Non-zero at index %d (%s)\n", i, string.sub(txt, i, i)))
			break
		end
	else
		nonzero = i+1  
	end
end
txt = string.sub(txt, nonzero)
print(string.format("Leading zeroes removed: %s\n", txt))
-- add (default) country prefix code if not present
if string.len(txt) == 9 then
	txt = "48" .. txt
end
print(string.format("Country code added: %s\n", txt))
-- add CO access code for PABX
txt = "00" .. txt
print(string.format("Setting number to dial: %s\n", txt))
-- set processed number back in dial edit
SetDial(txt)
-- or: Call(txt)  
  

Call to specified number (conference room) and enter code

-- user config
number = "123456789"
dtmf = "1234"
-- end of user config
 
Call(number)
for i=1, 20, 1
do
	if (i == 20) then
		print("Timed out waiting for confirmed state\n")
		break;
	end
 
	Sleep(300)
	call_state = GetCallState()
	if call_state == 6 then
		-- CALL_STATE_ESTABLISHED
		Sleep(2000)
		SendDtmf("1234")
		break
	elseif call_state == 0 then
		-- CALL_STATE_CLOSED
		print("End of call\n")
		break;
	end
end
print("End of script\n")  
  

Back to tSIP softphone


 "Cookie monsters": 2935528    Parse time: 0.000 s