Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/osascript
- (*
- Copyright (c) 2023 Kayvan A. Sylvan
- Permission is hereby granted, free of charge, to any person obtaining a copy of
- this software and associated documentation files (the "Software"), to deal in
- the Software without restriction, including without limitation the rights to
- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
- the Software, and to permit persons to whom the Software is furnished to do so,
- subject to the following conditions:
- The above copyright notice and this permission notice shall be included in all
- copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- *)
- -- Global properties
- property scriptName : "Zoom Manage"
- property topLevelDirectory : missing value
- -- Zoom Related properties
- property appName : "zoom.us"
- property appVersion : missing value
- property topZoomWindow : "Zoom"
- property meetingWindow : "Zoom Meeting"
- property sharingWindow : "zoom share statusbar window"
- property participantWindow : missing value
- -- These three files are created in the logs/ subdirectory.
- -- The pattern of filenames: logs/20231014-log.txt
- -- This makes it easy to store and index the logs at a later time.
- -- roster.txt is the running log file capturing the roster information.
- -- log.txt is the overall log file.
- -- filtered.txt is the filtered log file (capturing "Hands Raised", etc.)
- property logFile : "log.txt"
- property meetingRoster : "roster.txt"
- property filteredRoster : "filtered.txt"
- -- The FastAPI server should be running at this URL
- property trackerURL : "http://localhost:5000"
- property batchCount : 50 -- can be set via ZOOM_MANAGE_BATCH_SIZE environment variable
- -- Top level commands the script understands
- property knownCommands : { "help", "reset", "roster", "hands", "camera_off", "admit", "server", "dashboard", "breakout", "camera_off", "camera_on", "phone", "no_audio", "muted", "unmuted", "rename", "co-host"}
- -- zoomRosterDebug uses the ZOOM_DEBUG environment variable.
- -- If it is set to true, filtered rosters (like "hands") will be
- -- output to the console (stderr) in addition to going in the log.
- property zoomRosterDebug : missing value
- -- renameParticipantsFile is a mapping of users to rename upon entry to the meeting.
- -- If set, we use it in the "roster" command to check for and rename
- -- participants. It references the ZOOM_RENAME_FILE environment
- property renameParticipantsFile : missing value
- property renameMappings : missing value -- the mappings found in the above file
- -- Path to cliclick. See https://github.com/BlueM/cliclick
- property cliclick : "/opt/homebrew/bin/cliclick"
- property environmentDocs: "Environment Varibles:" & linefeed & " - ZOOM_DEBUG: Set this to any value to see rosters and filtered lists output on the console." & linefeed & " - ZOOM_RENAME_FILE: Path to a file of participant rename mappings. See rename/README.md file for details." & linefeed
- on usageMessage(section)
- set tid to AppleScript's text item delimiters
- set AppleScript's text item delimiters to ":"
- set path_parts to every text item of ((path to me) as string)
- set AppleScript's text item delimiters to tid
- if section is "top" then
- return linefeed & "Usage: " & (item -1 of path_parts) & " [command]" & linefeed & " help - show usage message." & linefeed & linefeed & " server - run the backend server. Starts the server in its own terminal." & linefeed & " dashboard - open the Zoom Meeting Tracker dashboard." & linefeed & " reset - reset the tracking database." & linefeed & linefeed & " roster (default action) - get current roster." & linefeed & linefeed & " hands - get the current hands raised." & linefeed & " camera_off - get the list of camera off participants." & linefeed & " camera_on - get the list of participants who are on video." & linefeed & " no_audio - get the list of participants not connected to audio." & linefeed & " muted - get the list of participants who are muted." & linefeed & " unmuted - get the list of participants who are unmuted." & linefeed & " phone - get the list of participants dialing in by phone." & linefeed & linefeed & " admit - admit everyone in the Waiting Room." & linefeed & " breakout - create a set of named breakout rooms." & linefeed & linefeed & " rename - rename a participant." & linefeed & " co-host - make a participant a co-host." & linefeed & linefeed & environmentDocs
- else if section is "breakout" then
- return linefeed & "Usage: " & (item -1 of path_parts) & " breakout [create | get | help] filename" & linefeed & " help - print the breakout related help message." & linefeed & " create - Opens up the named file and creates the rooms named there." & linefeed & " get - Get the named rooms and write them into filename." & linefeed
- else if section is "rename" then
- return linefeed & "Usage: " & (item -1 of path_parts) & " rename 'Original Name' 'New Name'" & linefeed
- end if
- end usageMessage
- on todayYMD()
- -- Return the current date in the format YYYYMMDD.
- set [_day, _month, _year] to [day, month, year] of (current date)
- set _month to _month * 1 --> 3
- set _month to text -1 thru -2 of ("0" & _month) --> "03"
- set _day to text -1 thru -2 of ("0" & _day)
- set result to _year & _month & _day
- return result
- end todayYMD
- on setUpLogFiles()
- -- setUpLogFiles handler:
- -- Creates a folder called "logs" in the same directory as the program if it does not already exist.
- -- It then creates two files, logFile and meetingRoster, in the logs folder, with a prefix of the current date.
- tell application "Finder"
- set _currentDirectory to container of (path to me)
- if not (exists folder "logs" of _currentDirectory) then
- make new folder at _currentDirectory with properties {name:"logs"}
- end if
- end tell
- set _currentDirectory to POSIX path of (_currentDirectory as text)
- set _prefix to my todayYMD()
- set topLevelDirectory to _currentDirectory
- set logFile to POSIX file (topLevelDirectory & "logs/" & _prefix & "-" & logFile)
- set meetingRoster to POSIX file (topLevelDirectory & "logs/" & _prefix & "-" & meetingRoster)
- set filteredRoster to POSIX file (topLevelDirectory & "logs/" & _prefix & "-" & filteredRoster)
- my setupEnvVariables()
- end setUpLogFiles
- on setupEnvVariables()
- -- zoomRosterDebug is false if ZOOM_DEBUG is not set
- set zoomRosterDebug to do shell script "echo ${ZOOM_DEBUG:-unset}"
- set zoomRosterDebug to (zoomRosterDebug is not "unset") -- set to false or true
- -- batchCount is set to value of ZOOM_MANAGER_BATCH_SIZE (50 if not set)
- set batchCount to do shell script "echo ${ZOOM_MANAGE_BATCH_SIZE:-" & batchCount & "}"
- -- renameParticipantsFile points to a mapping file of old to new names with "->" in between them.
- set renameParticipantsFile to do shell script "echo ${ZOOM_RENAME_FILE:-}"
- if renameParticipantsFile is "" then set renameParticipantsFile to missing value
- end setupEnvVariables
- on filterFileContents(fileLines)
- -- Given a set of fileLines (usually gathered from "paragraphs of fileContents" idiom)
- -- Return a new list, ignoring all empty lines and lines starting with "#"
- set _filtered to {}
- repeat with _item in fileLines
- set _s to _item as string
- if _s is not "" and _s does not start with "#" then
- if _s starts with "\\" then set _s to text 2 through -1 of _s
- set end of _filtered to _s
- end if
- end repeat
- return _filtered
- end filterFileContents
- on splitText(theText, theDelimiter)
- set oldDelimiters to AppleScript's text item delimiters
- set AppleScript's text item delimiters to theDelimiter
- set textItems to every text item of theText
- set AppleScript's text item delimiters to oldDelimiters -- Restore the original delimiters
- return textItems
- end splitText
- on loadRenameMappings()
- if renameParticipantsFile is missing value then return
- if not renameParticipantsFile starts with "/" then
- set renameParticipantsFile to topLevelDirectory & renameParticipantsFile
- end if
- tell application "System Events"
- if (not (exists file renameParticipantsFile)) then
- error ("Missing ZOOM_RENAME_FILE file: " & renameParticipantsFile) number -1
- end if
- set _size to size of (info for POSIX file renameParticipantsFile)
- if _size is 0 then
- error ("Empty ZOOM_RENAME_FILE file: " & renameParticipantsFile) number -2
- end if
- end tell
- set _rFile to POSIX file renameParticipantsFile
- set _file to open for access file _rFile
- set fileContents to read _file
- close access _rFile
- set mappings to my filterFileContents(paragraphs of fileContents)
- set renameMappings to {}
- repeat with _item in mappings
- set end of renameMappings to my splitText(_item, "->")
- end repeat
- end loadRenameMappings
- on lookupRenaming(oldName)
- if renameMappings is missing value then return
- repeat with _i in renameMappings
- if item 1 of _i as text is oldName as text then return _i
- end repeat
- return missing value
- end lookupRenaming
- on formatDateTime(theDateTime)
- (*
- This code takes a date and time as input and formats it into a string in the form of MM/DD/YYYY HH:MM:SS.
- It does this by extracting the day, month, year, hours, minutes, and seconds from the input and
- adding a leading zero if necessary.
- *)
- set [_day, _month, _year, _hours, _minutes, _seconds] to [day, month, year, hours, minutes, seconds] of theDateTime
- set _month to _month * 1 --> 3
- set _month to text -1 thru -2 of ("0" & _month) --> "03"
- set _day to text -1 thru -2 of ("0" & _day)
- set _hours to text -1 thru -2 of ("0" & _hours) --> "05"
- set _minutes to text -1 thru -2 of ("0" & _minutes)
- set _seconds to text -1 thru -2 of ("0" & _seconds)
- set result to _month & "/" & _day & "/" & _year & " " & _hours & ":" & _minutes & ":" & _seconds
- return result
- end formatDateTime
- on appendToFile(theFile, message, logToConsole)
- -- Write the message to theFile. logToConsole is a boolean indicating whether to log to console.
- write message & linefeed to theFile starting at eof
- if logToConsole then log message
- end appendToFile
- on logMessage(message, filePath)
- -- Usage:
- -- set logFilePath to (path to desktop as string) & "logfile.txt" -- Path to the log file
- -- my logMessage("This is a log message.", logFilePath)
- try
- -- Open the log file for writing, creating it if it doesn't exist
- set _log to open for access file filePath with write permission
- -- Construct the log entry
- set logEntry to (my formatDateTime(current date)) & " " & message
- -- Write the log entry to the file
- my appendToFile(_log, logEntry, false)
- -- Close the log file
- close access _log
- on error errMsg number errNum
- -- If an error occurs, close the log file (if it's open)
- try
- close access _log
- end try
- -- Optionally, report the error in some way
- set _msg to "Error: " & errMsg & " (" & errNum & ")"
- log _msg
- display dialog _msg
- end try
- end logMessage
- on checkZoomRunning()
- -- This code checks if an application (appName) is running and if it is,
- -- it sets the variable appVersion to the version of the application.
- set _isRunning to false
- tell application "System Events"
- set _isRunning to (name of every process) contains appName
- end tell
- if not _isRunning then
- tell application appName to activate
- delay 1.0
- end if
- set appVersion to version of application appName
- end checkZoomRunning
- on findStatusMenu(nameSubstring)
- -- Find a status menu of Zoom that contains nameSubstring
- set _ret to missing value
- tell application "System Events" to tell process appName
- set menuItems to every menu item of menu 1 of menu bar 2
- repeat with m in menuItems
- if (exists name of m) and name of m contains nameSubstring then
- set _ret to m
- exit repeat
- end if
- end repeat
- end tell
- return _ret
- end findStatusMenu
- on clickStatusMenu(nameSubstring)
- -- Click a menu item in status menu. Returns true if done, false otherwise.
- tell application "System Events" to tell process appName
- set m to my findStatusMenu(nameSubstring)
- if m is not missing value then
- click m
- delay 1
- end if
- end tell
- return (m is not missing value)
- end clickStatusMenu
- on enableParticipantsWindow()
- (*
- This code is used to click the "Manage Participants" menu item of the status menu bar.
- A delay of 1 second is added after the click.
- *)
- my clickStatusMenu("Participants")
- my logMessage("Participants panel started.", logFile)
- end enableParticipantsWindow
- on findSubWindow(nameOfApp, subWinName)
- -- Look for a sub-window of nameOfApp whose name contains subWinName
- tell application "System Events" to tell process nameOfApp
- set _wins to windows
- set _ret to missing value
- repeat with w in _wins
- if name of w starts with subWinName then
- set _ret to w
- exit repeat
- end if
- end repeat
- end tell
- return _ret
- end findSubWindow
- on findParticipantsWindow()
- tell application "System Events" to tell process appName
- if exists scroll area 1 of window meetingWindow then
- set participantWindow to window meetingWindow -- Participants panel.
- else
- set participantWindow to my findSubWindow(appName, "Participants")
- end if
- end tell
- end findParticipantsWindow
- on startParticipantWindow()
- tell application "System Events" to tell process appName
- my findParticipantsWindow()
- if participantWindow is missing value then
- my enableParticipantsWindow() -- participant window started
- end if
- my findParticipantsWindow()
- end tell
- end startParticipantWindow
- on checkZoomMeetingRunning()
- repeat
- -- Make sure we are in a Zoom meeting
- tell application "System Events" to tell process appName
- set _startMeetingMenu to my findStatusMenu("Join a Meeting")
- if _startMeetingMenu is not missing value then
- display dialog "The " & scriptName & " application needs a Zoom Meeting." & linefeed & "Please join the Zoom Meeting and press OK."
- else
- exit repeat
- end if
- end tell
- end repeat
- end checkZoomMeetingRunning
- on appLogMessage(message)
- my logMessage("=== " & scriptName & ": " & message, logFile)
- end appLogMessage
- on resetTrackigData()
- do shell script "curl -X POST " & trackerURL & "/reset"
- end resetTrackigData
- on replaceText(theText, searchStr, replacementStr)
- set theText to theText as text
- if theText does not contain searchStr then
- return theText -- return the original string if it doesn't contain the search string
- end if
- set oldDelims to AppleScript's text item delimiters -- save old delimiters
- set AppleScript's text item delimiters to the searchStr
- set theTextItems to every text item of theText
- set AppleScript's text item delimiters to the replacementStr
- set theText to theTextItems as string
- set AppleScript's text item delimiters to oldDelims -- restore old delimiters
- return theText
- end replaceText
- on trackList(_nameList, apiEndpoint)
- if (count _nameList) is 0 then
- return
- end if
- set _sanitized to {}
- repeat with _p in _nameList
- set _p to my replaceText(_p, "'", "-") -- Hack for people with single-quotes in their name
- set _sanitized to _sanitized & {_p}
- end repeat
- set cmd to "curl -X PUT -H 'Content-Type: application/json' -d ' ["
- set qList to {}
- repeat with n in _sanitized
- set qList to qList & {"\"" & n & "\""}
- end repeat
- set _tid to AppleScript's text item delimiters
- set AppleScript's text item delimiters to ","
- set cmd to cmd & (qList as string) & "]' " & trackerURL & apiEndpoint
- set AppleScript's text item delimiters to _tid
- do shell script cmd
- -- log cmd
- end trackList
- on trackListBatched(_nameList, apiEndpoint)
- set _nameCount to count _nameList
- repeat with i from 1 to _nameCount by batchCount
- set endIndex to i + batchCount - 1
- if endIndex > _nameCount then set endIndex to _nameCount
- my trackList(items i thru endIndex of _nameList, apiEndpoint)
- end repeat
- end trackListBatched
- on trackJoined(_nameList)
- my trackListBatched(_nameList, "/joined_list")
- end trackJoined
- on trackWaiting(_nameList)
- my trackListBatched(_nameList, "/waiting_list")
- end trackWaiting
- on runBackendServer()
- -- check if the server is already running
- set serverRunning to false
- try
- set jsonOutput to do shell script "curl -X GET " & trackerURL & "/health -H 'accept: application/json'"
- if jsonOutput starts with "{" then set serverRunning to true
- on error errMessage number errNum
- error errMessage number errNum
- end try
- if serverRunning then
- error "Server is already running. " & jsonOutput number -1
- end if
- set titleString to "\\033]0;" & scriptName & " Backend Server\\007"
- set cmd to "echo -n -e \"" & titleString & "\";cd " & topLevelDirectory & "backend; ./server.py; exit"
- do shell script "open -a Terminal " & topLevelDirectory
- tell application "Terminal"
- activate
- do script cmd in window 1
- end tell
- end runBackendServer
- on openDashboard()
- do shell script "open " & topLevelDirectory & "frontend/index.html"
- end openDashboard
- on writeToRoster(message, filePath)
- -- Usage:
- -- set logFilePath to (path to desktop as string) & "roster.txt" -- Path to the roster file
- -- my writeTeRoster("John Smith", logFilePath)
- try
- -- Open the log file for writing, creating it if it doesn't exist
- set _log to open for access file filePath with write permission
- my appendToFile(_log, message, zoomRosterDebug)
- -- Close the log file
- close access _log
- on error errMsg number errNum
- -- If an error occurs, close the log file (if it's open)
- try
- close access _log
- end try
- -- Optionally, report the error in some way
- set _msg to "Error: " & errMsg & " (" & errNum & ")"
- log _msg
- display dialog _msg
- end try
- end writeToRoster
- on writeToRosterPart(namesList, spacesPrefix)
- -- Write a list of names to the roster.txt file
- -- prepend each with the spacesPrefix
- if (count namesList) is 0 then return
- set i to 1
- repeat with n in namesList
- my writeToRoster(spacesPrefix & i & ". " & n, meetingRoster)
- set i to i + 1
- end repeat
- end writeToRosterPart
- on generateRoster()
- set _joinedList to {}
- set _waitingList to {}
- set _joinedPrefix to "Joined "
- set _waitingPrefix to "Waiting Room "
- set _notJoinedPrefix to "Not Joined"
- my loadRenameMappings()
- tell application "System Events" to tell process appName
- tell outline 1 of scroll area 1 of participantWindow
- set allParticipantNames to get value of static text of UI element of rows
- set _num to (count allParticipantNames)
- set _inWaitingList to false
- set _inJoinedList to false
- repeat with i from 1 to _num
- set _pName to item i of allParticipantNames as string
- if _pName starts with _waitingPrefix then
- set _inWaitingList to true
- end if
- if _pName starts with _joinedPrefix then
- set _inWaitingList to false
- set _inJoinedList to true
- end if
- if _pName starts with _notJoinedPrefix then exit repeat
- if (i is 1) and (not _inWaitingList) and (not _inJoinedList) then
- set _joinedList to allParticipantNames
- exit repeat
- end if
- if _inWaitingList then
- if not (_pName starts with _waitingPrefix) then
- set _waitingList to _waitingList & {_pName}
- end if
- else
- if not (_pName starts with _joinedPrefix) then
- set _joinedList to _joinedList & {_pName}
- end if
- end if
- end repeat
- end tell
- end tell
- set _intro to "=== " & (my formatDateTime(current date)) & " ==="
- my writeToRoster(_intro, meetingRoster)
- if (count _waitingList) > 0 then
- my writeToRoster("Waiting Room:", meetingRoster)
- my writeToRosterPart(_waitingList, " ")
- my writeToRoster("Joined:", meetingRoster)
- my writeToRosterPart(_joinedList, " ")
- else
- my writeToRosterPart(_joinedList, "")
- end if
- set _num to (count _waitingList) + (count _joinedList)
- if _num > 1 then
- set plural to "s"
- else
- set plural to ""
- end if
- set _summary to "=== " & (_num as string) & " participant" & plural & " " & (my formatDateTime(current date)) & " ==="
- my writeToRoster(_summary, meetingRoster)
- my trackWaiting(_waitingList)
- my trackJoined(_joinedList)
- -- Now, run our rename procedure if needed.
- if renameMappings is missing value then return
- set _renameToDo to {}
- set _candidates to {}
- repeat with _i in renameMappings
- set end of _candidates to item 1 of _i as text
- end repeat
- repeat with participant in _joinedList
- if participant as text is in _candidates then set end of _renameToDo to participant as text
- end repeat
- -- Now we go through each participant in _renameToDo and finish setting up the renaming.
- repeat with _name in _renameToDo
- set _res to my lookupRenaming(_name)
- if _res is not missing value then
- my renameParticipant(item 1 of _res, item 2 of _res, false)
- end if
- delay 0.4
- end repeat
- end generateRoster
- on joinStringList(_strings)
- set _tid to AppleScript's text item delimiters
- set AppleScript's text item delimiters to ":"
- set _ret to _strings as string
- set AppleScript's text item delimiters to _tid
- return _ret
- end joinStringList
- on generateFilteredRoster(filterString)
- -- Look for filter strings in the description strings in participants window
- set _intro to "=== " & filterString & " " & (my formatDateTime(current date)) & " ==="
- my writeToRoster(_intro, filteredRoster)
- set _num to 0
- tell application "System Events" to tell process appName
- tell outline 1 of scroll area 1 of participantWindow
- set allParticipantNames to get value of static text of UI element of rows
- set numPart to (count allParticipantNames)
- set allRows to rows
- repeat with i from 1 to numPart
- set pName to item i of allParticipantNames as string
- set aRow to item i of allRows
- set allElem to UI elements of (item 1 of UI element of aRow)
- set allDescriptions to description of UI elements of (item 1 of UI element of aRow)
- set joinedDescription to my joinStringList(allDescriptions)
- if joinedDescription contains filterString then
- set _num to _num + 1
- set _msgToLog to (_num as string) & ". " & pName
- my writeToRoster(_msgToLog, filteredRoster)
- end if
- end repeat
- end tell
- end tell
- if _num > 1 then
- set plural to "s"
- else
- set plural to ""
- end if
- set _summary to "=== " & filterString & " " & (_num as string) & " participant" & plural & " " & (my formatDateTime(current date)) & " ==="
- my writeToRoster(_summary, filteredRoster)
- end generateFilteredRoster
- on howManyWaiting(waitingRoomText)
- -- Take a string like "Waiting Room (3)" and return the number in the parentheses
- set {tid, AppleScript's text item delimiters} to {AppleScript's text item delimiters, "("}
- set thePart to text item 2 of waitingRoomText
- set AppleScript's text item delimiters to ")"
- set theValue to text item 1 of thePart
- set AppleScript's text item delimiters to tid -- restore original text item delimiters
- return theValue as integer
- end howManyWaiting
- on clickSubElement(target, searchString)
- -- Search for a button by description that is a
- -- sub element of the target. This works for windows, or
- -- other UI elements (outlines, scroll areas, etc.)
- tell application "System Events" to tell process appName
- set elems to every UI element of target
- repeat with e in elems
- if description of e is searchString as text then
- click e
- exit repeat
- end if
- end repeat
- end tell
- end clickSubElement
- on letPeopleIn()
- tell application "System Events" to tell process appName
- tell outline 1 of scroll area 1 of participantWindow
- -- Fetch all the rows in the Participants list.
- set participantRows to every row
- -- Check to see if Waiting Room exists.
- set firstRowName to (get value of static text of UI element of row 1) as string
- if firstRowName does not start with "Waiting Room " then
- return -- Nothing to do here
- end if
- set allParticipantNames to get value of static text of UI element of rows
- set _waitingNumber to my howManyWaiting(firstRowName)
- if _waitingNumber > 1 then
- -- We will find and click the "Admit All" button.
- my clickSubElement(item 1 of UI element of row 1, "Admit All")
- else
- -- We will click the Admit button on row 2 (the participant)
- my clickSubElement(item 1 of UI element of row 2, "Admit")
- end if
- delay 1
- -- Now, we log all the people we admitted.
- -- First, gather the list of people.
- set myList to {}
- repeat with _name in allParticipantNames
- set _name to _name as text
- if _name starts with "Joined " then
- exit repeat
- else
- if not (_name starts with "Waiting Room ") then
- set myList to myList & {_name}
- my logMessage("Waiting Room: Admitted " & _name, logFile)
- end if
- end if
- end repeat
- my trackListBatched(myList, "/waiting_list")
- end tell
- end tell
- end letPeopleIn
- on createBreakoutRooms(breakoutFilename)
- if breakoutFilename starts with "/" then
- set borFilePath to breakoutFilename
- else
- set borFilePath to topLevelDirectory & breakoutFilename
- end if
- tell application "System Events"
- if (not (exists file borFilePath)) then
- error ("Missing breakout file: " & borFilePath) number -1
- end if
- set _size to size of (info for POSIX file borFilePath)
- if _size is 0 then
- error ("Empty breakout file: " & borFilePath) number -2
- end if
- end tell
- set bFile to POSIX file (borFilePath)
- set _file to open for access file bFile
- set fileContents to read _file
- close access _file
- set rooms to my filterFileContents(paragraphs of fileContents)
- set roomCount to (count rooms)
- if roomCount is 0 then
- return "No rooms are defined in the file " & borFilePath & " - exiting."
- end if
- set bor_clicked to my clickStatusMenu("Breakout")
- if not bor_clicked then
- return "Breakout room functionality not available. Make sure breakout rooms are enabled and you are a host or co-host."
- end if
- set _borWin to my findSubWindow(appName, "Breakout")
- if name of _borWin contains "In Progress" then
- return "Breakout rooms already started. Please close them first."
- else
- tell application "System Events" to tell process appName
- click _borWin
- if exists text field 1 of _borWin then
- click text field 1 of _borWin
- delay 0.5
- keystroke " " & roomCount
- delay 0.5
- my clickSubElement(_borWin, "Assign manually")
- my clickSubElement(_borWin, "Create")
- else
- my clickSubElement(_borWin, "Recreate")
- delay 0.5
- set _menuWinName to "Menu window"
- if exists window _menuWinName then
- key code 125
- key code 125
- keystroke " "
- end if
- keystroke "\t" & roomCount
- delay 0.5
- set w to window "All existing rooms will be replaced."
- my clickSubElement(w, "Assign manually")
- my clickSubElement(w, "Recreate")
- end if
- end tell
- end if
- -- Now, with the right number of rooms, we can rename them all.
- -- We can use the _borWin window handle we already have.
- tell application "System Events" to tell process appName to tell _borWin
- tell table 1 of scroll area 1 of group 1
- set _rows to rows
- set i to 0
- repeat with _r in _rows
- set i to i + 1
- click UI element 2 of UI element 1 of _r
- delay 0.2
- keystroke item i of rooms
- delay 0.2
- keystroke "\r"
- delay 0.2
- end repeat
- end tell
- end tell
- end createBreakoutRooms
- on getBreakoutRooms(breakoutFilename)
- if breakoutFilename starts with "/" then
- set borFilePath to breakoutFilename
- else
- set borFilePath to topLevelDirectory & breakoutFilename
- end if
- tell application "System Events"
- if (exists file borFilePath) then
- error ("Will not overwrite file: " & borFilePath) number -3
- end if
- end tell
- set bFile to POSIX file (borFilePath)
- set _file to open for access file bFile with write permission
- set bor_clicked to my clickStatusMenu("Breakout")
- if not bor_clicked then
- return "Breakout room functionality not available. Make sure breakout rooms are enabled and you are a host or co-host."
- end if
- set _borWin to my findSubWindow(appName, "Breakout")
- set _title to (name of _borWin)
- set logEntry to "# === " & (my formatDateTime(current date)) & " " & _title
- my appendToFile(_file, logEntry, zoomRosterDebug)
- tell application "System Events" to tell process appName to tell _borWin
- tell table 1 of scroll area 1 of group 1
- set _rows to rows
- set rooms to {}
- repeat with _r in _rows
- set _room to description of UI element 1 of UI element 1 of _r as string
- if _room is "text"
- set _room to "# " & name of UI element 1 of UI element 1 of _r as string
- end if
- set end of rooms to _room
- end repeat
- end tell
- end tell
- repeat with eachRoom in rooms
- my appendToFile(_file, eachRoom as text, zoomRosterDebug)
- end repeat
- close access _file
- end getBreakoutRooms
- on moveMouse(x, y)
- do shell script cliclick & " m:=" & x & ",=" & y
- end moveMouse
- on clickMouse(x, y)
- do shell script cliclick & " c:=" & x & ",=" & y
- end clickMouse
- on renameParticipant(oldName, newName, verbose)
- set _canRenameOthers to false
- set _renamingMe to false
- tell application "System Events" to tell process appName
- tell outline 1 of scroll area 1 of participantWindow
- set allParticipantNames to get value of static text of UI element of rows
- set _myName to item 1 of allParticipantNames as string
- set _renamingMe to (_myName is oldName)
- set _canRenameOthers to (_myName contains "(Host, me)") or (_myName contains "(Co-host, me)")
- end tell
- end tell
- if not _renamingMe and not _canRenameOthers then
- if verbose then
- log "ERROR: You need to be Host or Co-Host to rename other partcipants."
- return
- end if
- end if
- if not _renamingMe and oldName ends with "(Host)" or oldName ends with "(Co-host)" then
- log "ERROR: You can not rename a Host or Co-host."
- return
- end if
- tell application appName to activate
- tell application "System Events" to tell process appName
- tell outline 1 of scroll area 1 of participantWindow
- repeat with _r in rows
- if (get value of static text of UI element of _r) as text is oldName then
- set {_x, _y} to position of UI element 1 of UI element 1 of _r
- my moveMouse(_x, _y)
- set {_x2, _y2} to position of UI element 3 of UI element 1 of _r
- set _drag to (_x2 - _x) as integer
- do shell script cliclick & " c:+" & _drag & ",+0"
- delay 0.5
- click menu item "Rename" of menu 1 of UI element 1 of _r
- delay 0.5
- keystroke newName
- keystroke "\r"
- my logMessage("Renamed: " & oldName & " => " & newName, logFile)
- exit repeat
- end if
- end repeat
- end tell
- end tell
- end renameParticipant
- on doBreakoutRooms(borCommand, filename)
- if borCommand is "create" then
- return my createBreakoutRooms(filename)
- end if
- if borCommand is "get" then
- return my getBreakoutRooms(filename)
- end if
- log "Error: Unknown breakout subcommand: " & borCommand
- return my usageMessage("breakout")
- end doBreakoutRooms
- on run argv
- set argCount to (count argv)
- if (argCount > 0) then
- set arg to item 1 of argv
- else
- set arg to "roster"
- end if
- if arg is not in knownCommands then log "Error: unknown command: " & arg
- if arg is "help" or arg is not in knownCommands then
- return my usageMessage("top")
- else if arg is "reset" then
- return my resetTrackigData()
- end if
- my setUpLogFiles()
- if arg is "server" then
- return my runBackendServer()
- end if
- if arg is "dashboard" then
- return my openDashboard()
- end if
- my appLogMessage("START " & arg)
- try
- my checkZoomRunning()
- my logMessage("Zoom Version: " & appVersion, logFile)
- my checkZoomMeetingRunning()
- my startParticipantWindow()
- if arg is "roster" then
- my generateRoster()
- else if arg is "hands" then
- my generateFilteredRoster("Hand raised")
- else if arg is "camera_off" then
- my generateFilteredRoster("Video off")
- else if arg is "camera_on" then
- my generateFilteredRoster("Video on")
- else if arg is "phone" then
- my generateFilteredRoster("Telephone")
- else if arg is "no_audio" then
- my generateFilteredRoster("No Audio")
- else if arg is "muted" then
- my generateFilteredRoster(" muted")
- else if arg is "unmuted" then
- my generateFilteredRoster(" unmuted")
- else if arg is "admit" then
- my letPeopleIn()
- else if arg is "breakout" then
- if argCount is not 3 then
- log my usageMessage("breakout")
- else
- log my doBreakoutRooms(item 2 of argv, item 3 of argv)
- end if
- else if arg is "rename" then
- if argCount is not 3 then
- log my usageMessage("rename")
- else
- log my renameParticipant(item 2 of argv, item 3 of argv, true)
- end if
- end if
- on error errMsg number errNum
- set e_str to "Error: " & errMsg
- my logMessage(e_str, logFile)
- my appLogMessage("ABORT " & arg)
- error e_str number errNum
- end try
- my appLogMessage("END " & arg)
- end run
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement