diff options
Diffstat (limited to 'misc/freeswitch/scripts/common/conference.lua')
-rw-r--r-- | misc/freeswitch/scripts/common/conference.lua | 239 |
1 files changed, 239 insertions, 0 deletions
diff --git a/misc/freeswitch/scripts/common/conference.lua b/misc/freeswitch/scripts/common/conference.lua new file mode 100644 index 0000000..d2bf829 --- /dev/null +++ b/misc/freeswitch/scripts/common/conference.lua @@ -0,0 +1,239 @@ +-- Gemeinschaft 5 module: conference class +-- (c) AMOOMA GmbH 2012 +-- + +module(...,package.seeall) + +Conference = {} + +MEMBERS_MAX = 100; +PIN_LENGTH_MAX = 10; +PIN_LENGTH_MIN = 2; +PIN_TIMEOUT = 4000; +ANNOUNCEMENT_MAX_LEN = 10 +ANNOUNCEMENT_SILENCE_THRESHOLD = 500 +ANNOUNCEMENT_SILENCE_LEN = 3 + +-- create conference object +function Conference.new(self, arg) + arg = arg or {} + object = arg.object or {} + setmetatable(object, self); + self.__index = self; + self.class = 'conference'; + self.log = arg.log; + self.database = arg.database; + self.record = arg.record; + self.max_members = 0; + return object; +end + +-- find conference by id +function Conference.find_by_id(self, id) + local sql_query = 'SELECT * FROM `conferences` WHERE `id`= ' .. tonumber(id) .. ' LIMIT 1'; + local conference = nil; + + self.database:query(sql_query, function(conference_entry) + conference = Conference:new(self); + conference.record = conference_entry; + conference.id = tonumber(conference_entry.id); + conference.uuid = conference_entry.uuid; + conference.max_members = tonumber(conference.record.max_members) or MEMBERS_MAX; + end) + + return conference; +end + +-- find invitee by phone numbers +function Conference.find_invitee_by_numbers(self, phone_numbers) + if not self.record then + return false + end + + local sql_query = string.format( + "SELECT `conference_invitees`.`pin` AS `pin`, `conference_invitees`.`speaker` AS `speaker`, `conference_invitees`.`moderator` AS `moderator` " .. + "FROM `conference_invitees` JOIN `phone_numbers` ON `phone_numbers`.`phone_numberable_id` = `conference_invitees`.`id` " .. + "WHERE `phone_numbers`.`phone_numberable_type` = 'ConferenceInvitee' AND `conference_invitees`.`conference_id` = %d " .. + "AND `phone_numbers`.`number` IN ('%s') LIMIT 1", self.record.id, table.concat(phone_numbers, "','")); + + local invitee = nil; + + self.database:query(sql_query, function(conference_entry) + invitee = conference_entry; + end) + + return invitee; +end + +function Conference.count(self) + return tonumber(self.caller:result('conference ' .. self.record.id .. ' list count')) or 0; +end + +-- Try to enter a conference +function Conference.enter(self, caller, domain) + local cause = "NORMAL_CLEARING"; + local pin = nil; + local flags = {'waste'}; + + self.caller = caller; + + require "common.phone_number" + local phone_number_class = common.phone_number.PhoneNumber:new{log = self.log, database = self.database} + local phone_numbers = phone_number_class:list_by_owner(self.record.id, "Conference"); + + -- Set conference presence + require "dialplan.presence" + local presence = dialplan.presence.Presence:new(); + presence:init{ log = log, accounts = phone_numbers, domain = domain, uuid = "conference_" .. self.record.id }; + + local conference_count = self:count(); + + -- Check if conference is full + if conference_count >= self.max_members then + presence:early(); + self.log:debug(string.format("full conference %s (\"%s\"), members: %d, members allowed: %d", self.record.id, self.record.name, conference_count, self.max_members)); + + if (tonumber(self.record.conferenceable_id) == caller.account_owner_id) + and (self.record.conferenceable_type == caller.account_owner_type) then + self.log:debug("Allow owner of this conterence to enter a full conference"); + else + cause = "CALL_REJECTED"; + caller:hangup(cause); + return cause; + end; + end + + -- Check if conference is within time frame + if self.record.start and self.record['end'] then + local d = {} + _,_,d.year,d.month,d.day,d.hour,d.min,d.sec=string.find(self.record.start, "(%d+)-(%d+)-(%d+) (%d+):(%d+):(%d+)"); + + local conference_start = os.time(d); + _,_,d.year,d.month,d.day,d.hour,d.min,d.sec=string.find(self.record['end'], "(%d+)-(%d+)-(%d+) (%d+):(%d+):(%d+)"); + local conference_end = os.time(d); + local now = os.time(os.date("!*t", os.time())); + + log:debug("conference - open: " .. os.date("%c",conference_start) .. " by " .. os.date("%c",conference_end) .. ", now: " .. os.date("%c",now)); + + if now < conference_start or now > conference_end then + cause = "CALL_REJECTED"; + caller:hangup(cause); + return cause; + end + end + + require 'common.str' + -- Owner ist always moderator + if (tonumber(self.record.conferenceable_id) == caller.account_owner_id) and (self.record.conferenceable_type == caller.account_owner_type) then + table.insert(flags, 'moderator'); + log:debug("is owner - conference: " .. self.record.id .. ", owner: " .. caller.account_owner_type .. ":" .. caller.account_owner_id); + else + local invitee = self:find_invitee_by_numbers(caller.caller_phone_numbers); + + if not common.str.to_b(self.record.open_for_anybody) and not invitee then + log:debug(string.format("conference %s (\"%s\"), caller %s not allowed to enter this conference", self.record.id, self.record.name, caller.caller_phone_number)); + cause = "CALL_REJECTED"; + caller:hangup(cause); + return cause; + end + + if invitee then + log:debug("conference " .. self.record.id .. " member invited - speaker: " .. invitee.speaker .. ", moderator: " .. invitee.moderator); + if common.str.to_b(invitee.moderator) then + table.insert(flags, 'moderator'); + end + if not common.str.to_b(invitee.speaker) then + table.insert(flags, 'mute'); + end + pin = invitee.pin; + else + log:debug("conference " .. self.record.id .. " caller not invited"); + end + end + + if not pin and self.record.pin then + pin = self.record.pin + end + + caller:answer(); + caller:sleep(1000); + caller.session:streamFile('conference/conf-welcome.wav'); + + if pin and pin ~= "" then + local digits = ""; + for i = 1, 3, 1 do + if digits == pin then + break + elseif digits ~= "" then + caller.session:streamFile('conference/conf-bad-pin.wav'); + end + digits = caller.session:read(PIN_LENGTH_MIN, PIN_LENGTH_MAX, 'conference/conf-enter_conf_pin.wav', PIN_TIMEOUT, '#'); + end + if digits ~= pin then + caller.session:streamFile("conference/conf-goodbye.wav"); + return "CALL_REJECTED"; + end + end + + self.log:debug(string.format("entering conference %s - name: \"%s\", flags: %s, members: %d, max. members: %d", + self.record.id, self.record.name, table.concat(flags, ','), conference_count, self.max_members)); + + -- Members count will be incremented in a few milliseconds, set presence + if (conference_count + 1) >= self.max_members then + presence:early(); + else + presence:confirmed(); + end + + -- Enter the conference + local name_file = nil; + + -- Record caller's name + if common.str.to_b(self.record.announce_new_member_by_name) or common.str.to_b(self.record.announce_left_member_by_name) then + local uid = session:get_uuid(); + name_file = "/tmp/conference_caller_name_" .. uid .. ".wav"; + caller.session:streamFile("voicemail/vm-record_name1.wav"); + caller.session:execute("playback", "tone_stream://%(1000,0,500)"); + session:recordFile(name_file, ANNOUNCEMENT_MAX_LEN, ANNOUNCEMENT_SILENCE_THRESHOLD, ANNOUNCEMENT_SILENCE_LEN); + caller.session:streamFile(name_file); + end + + -- Play entering caller's name if recorded + if name_file and (self:count() > 0) and common.str.to_b(self.record.announce_new_member_by_name) then + caller.session:execute('set',"result=${conference(" .. self.record.id .. " play ".. name_file .. ")}"); + caller.session:execute('set',"result=${conference(" .. self.record.id .. " play conference/conf-has_joined.wav)}"); + else + -- Ensure a surplus "#" digit is not passed to the conference + caller.session:read(1, 1, '', 1000, "#"); + end + + local result = caller.session:execute('conference', self.record.id .. "@profile_" .. self.record.id .. "++flags{" .. table.concat(flags, '|') .. "}"); + self.log:debug('exited conference - result: ' .. tostring(result)); + caller.session:streamFile("conference/conf-goodbye.wav") + + -- Play leaving caller's name if recorded + if name_file then + if (self:count() > 0) and common.str.to_b(self.record.announce_left_member_by_name) then + if (self:count() == 1) then + caller.session:sleep(3000); + end + caller.session:execute('set',"result=${conference(" .. self.record.id .. " play ".. name_file .. ")}"); + caller.session:execute('set',"result=${conference(" .. self.record.id .. " play conference/conf-has_left.wav)}"); + end + os.remove(name_file); + end + + -- Set presence according to member count + conference_count = self:count(); + if conference_count >= self.max_members then + presence:early(); + elseif conference_count > 0 then + presence:confirmed(); + else + presence:terminated(); + end + + cause = "NORMAL_CLEARING"; + caller.session:hangup(cause); + return cause; +end |