Initial commit of a new experimental modular GAM.

This commit is contained in:
Jay Lee
2026-07-03 08:33:14 -04:00
parent 2fbc3c5c35
commit 8a89a91414
129 changed files with 88262 additions and 82716 deletions

View File

@@ -0,0 +1,95 @@
"""_Chat_Tmp sub-package.
Re-exports all symbols from sub-modules for backward compatibility."""
from gam.cmd.chat.setup import ( # noqa: F401
CHAT_EMOJI_SHOW_CREATED_BY_CHOICE_MAP,
CHAT_SECTION_POSITION,
CHAT_TIME_OBJECTS,
_chkChatAdminAccess,
_cleanChatMessage,
_cleanChatSpace,
_getChatAdminAccess,
_getChatPageMessage,
_getValidateEmojiName,
_printChatItem,
_showChatItem,
buildChatServiceObject,
createChatEmoji,
createUpdateChatSection,
deleteChatEmoji,
deleteChatSection,
doSetupChat,
exitIfChatNotConfigured,
getChatSectionName,
getEmojiName,
infoChatEmoji,
moveShowChatSectionItem,
printShowChatEmojis,
printShowChatSectionItems,
printShowChatSections,
setupChatURL,
)
from gam.cmd.chat.spaces import ( # noqa: F401
CHAT_MEMBER_ROLE_MAP,
CHAT_MEMBER_TYPE_MAP,
CHAT_ROLE_ENTITY_TYPE_MAP,
CHAT_SPACES_ADMIN_ORDERBY_CHOICE_MAP,
CHAT_SPACES_FIELDS_CHOICE_MAP,
CHAT_SPACE_MIN_MAX_MEMBERS,
CHAT_SPACE_PREDEFINED_PERMS_MAP,
CHAT_SPACE_ROLE_PERMISSIONS_MAP,
CHAT_SPACE_TYPE_MAP,
CHAT_UPDATE_SPACE_PERMISSIONS_MAP,
CHAT_UPDATE_SPACE_TYPE_MAP,
_getChatSpaceListParms,
_getChatSpaceSearchParms,
createChatSpace,
deleteChatSpace,
doInfoChatSpace,
doPrintShowChatSpaces,
getChatSpaceParameters,
getSpaceName,
infoChatSpace,
infoChatSpaceDM,
printShowChatSpaces,
updateChatSpace,
)
from gam.cmd.chat.members import ( # noqa: F401
CHAT_MEMBERS_FIELDS_CHOICE_MAP,
CHAT_MESSAGES_FIELDS_CHOICE_MAP,
CHAT_MESSAGES_ORDERBY_CHOICE_MAP,
CHAT_MESSAGE_REPLY_OPTION_MAP,
CHAT_SEARCHMESSAGES_ORDERBY_CHOICE_MAP,
CHAT_SEARCHMESSAGES_VIEW_CHOICE_MAP,
CHAT_SYNC_PREVIEW_TITLES,
_deleteChatMembers,
_getChatMemberEmail,
_getChatSenderEmail,
_getChatSpaceDisplayName,
_getChatSpaceMembers,
createChatMember,
createChatMessage,
deleteChatMessage,
deleteUpdateChatMember,
doCreateChatMessage,
doDeleteChatMessage,
doInfoChatEvent,
doInfoChatMember,
doInfoChatMessage,
doPrintShowChatMembers,
doUpdateChatMessage,
getGroupMemberID,
getUserMemberID,
infoChatEvent,
infoChatMember,
infoChatMessage,
normalizeUserMember,
printShowChatEvents,
printShowChatMembers,
printShowChatMessages,
printShowChatSearchMessages,
syncChatMembers,
trimChatMessageIfRequired,
updateChatMessage,
)

1434
src/gam/cmd/chat/members.py Normal file

File diff suppressed because it is too large Load Diff

658
src/gam/cmd/chat/setup.py Normal file
View File

@@ -0,0 +1,658 @@
"""Chat setup, emoji, and section management.
Part of the _chat_tmp sub-package."""
"""GAM Google Chat management."""
import re
import json
import sys
from gamlib import glaction
from gamlib import glapi as API
from gamlib import glcfg as GC
from gamlib import glclargs
from gamlib import glentity
from gamlib import glgapi as GAPI
from gamlib import glglobals as GM
from gamlib import glindent
from gamlib import glmsgs as Msg
Act = glaction.GamAction()
Ent = glentity.GamEntity()
Ind = glindent.GamIndent()
Cmd = glclargs.GamCLArgs()
def _getMain():
return sys.modules['gam']
def __getattr__(name):
"""Fall back to gam module for any undefined names."""
main = _getMain()
try:
return getattr(main, name)
except AttributeError:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
def buildChatServiceObject(api=API.CHAT, user=None, i=0, count=0, entityTypeList=None, useAdminAccess=False):
if user is None:
_, chat = _getMain().buildGAPIServiceObject(API.CHAT, user)
kvList = [Ent.CHAT_BOT, None]
else:
user, chat = _getMain().buildGAPIServiceObject(api, user, i, count)
if not useAdminAccess:
kvList = [Ent.USER, user]
else:
kvList = [Ent.CHAT_ADMIN, f'{user}(asadmin)']
if entityTypeList is not None:
kvList.extend(entityTypeList)
return user, chat, kvList
def _getChatPageMessage(entityType, user, i, count, pfilter, useAdminAccess=False):
if user is not None:
_getMain().printGettingAllEntityItemsForWhom(entityType, user if not useAdminAccess else f'{user}(asadmin)', i, count, pfilter)
return _getMain().getPageMessageForWhom()
_getMain().printGettingAllAccountEntities(entityType, pfilter)
return _getMain().getPageMessage()
def setupChatURL(chat):
return f'https://console.cloud.google.com/apis/api/chat.googleapis.com/hangouts-chat?project={chat._http.credentials.project_id}'
def exitIfChatNotConfigured(chat, kvList, errMsg, i, count):
if (('No bot associated with this project.' in errMsg) or
('Invalid project number.' in errMsg) or
('Google Chat app not found.' in errMsg)):
_getMain().systemErrorExit(_getMain().API_ACCESS_DENIED_RC, Msg.TO_SET_UP_GOOGLE_CHAT.format(setupChatURL(chat), GM.Globals[GM.OAUTH2SERVICE_JSON_DATA]['project_id']))
_getMain().entityActionFailedWarning(kvList, errMsg, i, count)
def _getChatAdminAccess(adminAPI, userAPI):
if _getMain().checkArgumentPresent(_getMain().ADMIN_ACCESS_OPTIONS) or GC.Values[GC.USE_CHAT_ADMIN_ACCESS]:
return (True, adminAPI, {'useAdminAccess': True})
return (False, userAPI, {})
def _chkChatAdminAccess(count):
if count != 1:
_getMain().usageErrorExit(Msg.CHAT_ADMIN_ACCESS_LIMITED_TO_ONE_USER.format(count))
def _cleanChatSpace(space):
space.pop('type', None)
space.pop('threaded', None)
def _cleanChatMessage(message):
message.pop('cards', None)
CHAT_TIME_OBJECTS = {'createTime', 'deleteTime', 'eventTime', 'lastActiveTime', 'lastUpdateTime'}
def _showChatItem(citem, entityType, FJQC, i=0, count=0):
if entityType == Ent.CHAT_SPACE:
_cleanChatSpace(citem)
dictObjectsKey = {None: 'displayName'}
elif entityType == Ent.CHAT_MESSAGE:
_cleanChatMessage(citem)
dictObjectsKey = {None: 'text'}
else:
dictObjectsKey={}
if FJQC.formatJSON:
_getMain().printLine(json.dumps(_getMain().cleanJSON(citem, timeObjects=CHAT_TIME_OBJECTS), ensure_ascii=False, sort_keys=True))
return
_getMain().printEntity([entityType, citem['name']], i, count)
Ind.Increment()
_getMain().showJSON(None, citem, timeObjects=CHAT_TIME_OBJECTS, dictObjectsKey=dictObjectsKey)
Ind.Decrement()
def _printChatItem(user, citem, parent, entityType, csvPF, FJQC, addCSVData=None):
if entityType == Ent.CHAT_SPACE:
_cleanChatSpace(citem)
baserow = {'User': user} if user is not None else {}
elif entityType in {Ent.CHAT_SECTION, Ent.CHAT_SECTION_ITEM}:
baserow = {'User': user}
elif entityType == Ent.CHAT_EMOJI:
baserow = {'User': user, 'name': citem['name'], 'emojiName': citem['emojiName']}
else:
if user is not None:
baserow = {'User': user, 'space.name': parent['name'], 'space.displayName': parent['displayName']}
else:
baserow = {'space.name': parent['name'], 'space.displayName': parent['displayName']}
if entityType == Ent.CHAT_MEMBER:
if addCSVData:
baserow.update(addCSVData)
elif entityType == Ent.CHAT_MESSAGE:
_cleanChatMessage(citem)
row = _getMain().flattenJSON(citem, flattened=baserow.copy(), timeObjects=CHAT_TIME_OBJECTS)
if not FJQC.formatJSON:
csvPF.WriteRowTitles(row)
elif csvPF.CheckRowTitles(row):
row = baserow.copy()
row.update({'name': citem['name'],
'JSON': json.dumps(_getMain().cleanJSON(citem, timeObjects=CHAT_TIME_OBJECTS), ensure_ascii=False, sort_keys=True)})
csvPF.WriteRowNoFilter(row)
def _getValidateEmojiName():
name = _getMain().getString(Cmd.OB_CHAT_EMOJI_NAME)
if re.match(r'^:[0-9a-z_-]+:$', name):
return name
Cmd.Backup()
_getMain().usageErrorExit(Msg.INVALID_EMOJI_NAME.format(name))
# gam <UserTypeEntity> create chatemoji <ChatEmojiName>
# ([drivedir|(sourcefolder <FilePath>)] [filename <FileNamePattern>])
# [formatjson]
def createChatEmoji(users):
FJQC = _getMain().FormatJSONQuoteChar()
name = _getValidateEmojiName()
body = {'emojiName': name, 'payload': {'filename': '', 'fileContent': ''}}
sourceFolder = os.getcwd()
filenamePattern = '#email#.jpg'
while Cmd.ArgumentsRemaining():
myarg = _getMain().getArgument()
if myarg == 'drivedir':
sourceFolder = GC.Values[GC.DRIVE_DIR]
elif myarg == 'sourcefolder':
sourceFolder = _getMain().setFilePath(_getMain().getString(Cmd.OB_FILE_PATH), GC.INPUT_DIR)
if not os.path.isdir(sourceFolder):
_getMain().entityDoesNotExistExit(Ent.DIRECTORY, sourceFolder)
elif myarg == 'filename':
filenamePattern = _getMain().getString(Cmd.OB_FILE_NAME_PATTERN)
else:
FJQC.GetFormatJSON(myarg)
i, count, users = _getMain().getEntityArgument(users)
for user in users:
i += 1
user, chat, kvList = buildChatServiceObject(API.CHAT_CUSTOM_EMOJIS, user, i, count, [Ent.CHAT_EMOJI, name])
if not chat:
continue
user, userName, _ = _getMain().splitEmailAddressOrUID(user)
filename = os.path.join(sourceFolder, _getMain()._substituteForUser(filenamePattern, user, userName))
try:
with open(filename, 'rb') as f:
image_data = f.read()
except (OSError, IOError) as e:
_getMain().entityActionFailedWarning([Ent.USER, user, Ent.CHAT_EMOJI, filename], str(e), i, count)
continue
body['payload'] = {'filename': os.path.basename(filename),
'fileContent':base64.urlsafe_b64encode(image_data).decode(_getMain().UTF8)}
try:
emoji = _getMain().callGAPI(chat.customEmojis(), 'create',
throwReasons=[GAPI.ALREADY_EXISTS, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED, GAPI.FAILED_PRECONDITION],
body=body)
_showChatItem(emoji, Ent.CHAT_EMOJI, FJQC, i, count)
except (GAPI.alreadyExists, GAPI.invalidArgument, GAPI.permissionDenied) as e:
exitIfChatNotConfigured(chat, kvList, str(e), i, count)
except GAPI.failedPrecondition:
_getMain().userChatServiceNotEnabledWarning(user, i, count)
def getEmojiName(myarg):
if myarg == 'emojiname':
name = _getValidateEmojiName()
return 'customEmojis/'+name
_, chatEmoji = Cmd.Previous().split('/', 1)
return 'customEmojis/'+chatEmoji
# gam <UserTypeEntity> delete chatemoji <ChatEmoji>
def deleteChatEmoji(users):
name = None
while Cmd.ArgumentsRemaining():
myarg = _getMain().getArgument()
if myarg == 'emojiname':
name = getEmojiName(myarg)
elif myarg.startswith('customemojis/'):
name = getEmojiName(myarg)
else:
_getMain().unknownArgumentExit()
if not name:
_getMain().missingArgumentExit('ChatEmoji')
i, count, users = _getMain().getEntityArgument(users)
for user in users:
i += 1
user, chat, kvList = buildChatServiceObject(API.CHAT_CUSTOM_EMOJIS, user, i, count, [Ent.CHAT_EMOJI, name])
if not chat:
continue
try:
_getMain().callGAPI(chat.customEmojis(), 'delete',
throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED, GAPI.FAILED_PRECONDITION],
name=name)
_getMain().entityActionPerformed(kvList, i, count)
except (GAPI.notFound, GAPI.invalidArgument, GAPI.permissionDenied) as e:
exitIfChatNotConfigured(chat, kvList, str(e), i, count)
except GAPI.failedPrecondition:
_getMain().userChatServiceNotEnabledWarning(user, i, count)
# gam <UserTypeEntity> info chatemoji <ChatEmoji>
# [formatjson]
def infoChatEmoji(users):
FJQC = _getMain().FormatJSONQuoteChar()
name = None
while Cmd.ArgumentsRemaining():
myarg = _getMain().getArgument()
if myarg == 'emojiname':
name = getEmojiName(myarg)
elif myarg.startswith('customemojis/'):
name = getEmojiName(myarg)
else:
FJQC.GetFormatJSON(myarg)
if not name:
_getMain().missingArgumentExit('ChatEmoji')
i, count, users = _getMain().getEntityArgument(users)
for user in users:
i += 1
user, chat, kvList = buildChatServiceObject(API.CHAT_CUSTOM_EMOJIS, user, i, count, [Ent.CHAT_EMOJI, name])
if not chat:
continue
try:
emoji = _getMain().callGAPI(chat.customEmojis(), 'get',
throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED, GAPI.FAILED_PRECONDITION],
name=name)
if not FJQC.formatJSON:
_getMain().entityPerformAction(kvList, i, count)
_showChatItem(emoji, Ent.CHAT_EMOJI, FJQC, i, count)
except (GAPI.notFound, GAPI.invalidArgument, GAPI.permissionDenied) as e:
exitIfChatNotConfigured(chat, kvList, str(e), i, count)
except GAPI.failedPrecondition:
_getMain().userChatServiceNotEnabledWarning(user, i, count)
CHAT_EMOJI_SHOW_CREATED_BY_CHOICE_MAP = {
'any': None,
'me': 'creator("users/me")',
'others': 'NOT creator("users/me")'
}
# gam <UserTypeEntity> show chatemojis
# [showcreatedby any|me|others]
# [formatjson]
# gam <UserTypeEntity> print chatemojis [todrive <ToDriveAttribute>*]
# [showcreatedby any|me|others]
# [formatjson [quotechar <Character>]]
def printShowChatEmojis(users):
csvPF = _getMain().CSVPrintFile(['User', 'name', 'emojiName']) if Act.csvFormat() else None
FJQC = _getMain().FormatJSONQuoteChar(csvPF)
pfilter = CHAT_EMOJI_SHOW_CREATED_BY_CHOICE_MAP['me']
while Cmd.ArgumentsRemaining():
myarg = _getMain().getArgument()
if csvPF and myarg == 'todrive':
csvPF.GetTodriveParameters()
elif myarg =='showcreatedby':
pfilter = _getMain().getChoice(CHAT_EMOJI_SHOW_CREATED_BY_CHOICE_MAP, mapChoice=True)
else:
FJQC.GetFormatJSONQuoteChar(myarg, True)
i, count, users = _getMain().getEntityArgument(users)
for user in users:
i += 1
user, chat, kvList = buildChatServiceObject(API.CHAT_CUSTOM_EMOJIS, user, i, count, [Ent.CHAT_EMOJI, None])
if not chat:
continue
try:
emojis = _getMain().callGAPIpages(chat.customEmojis(), 'list', 'customEmojis',
pageMessage=_getChatPageMessage(Ent.CHAT_EMOJI, user, i, count, pfilter),
throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT,
GAPI.PERMISSION_DENIED, GAPI.FAILED_PRECONDITION],
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
pageSize=GC.Values[GC.CHAT_MAX_RESULTS], filter=pfilter)
except (GAPI.notFound, GAPI.invalidArgument, GAPI.permissionDenied) as e:
exitIfChatNotConfigured(chat, kvList, str(e), i, count)
continue
except GAPI.failedPrecondition:
_getMain().userChatServiceNotEnabledWarning(user, i, count)
break
if not csvPF:
jcount = len(emojis)
if not FJQC.formatJSON:
_getMain().entityPerformActionNumItems(kvList, jcount, Ent.CHAT_EMOJI, i, count)
Ind.Increment()
j = 0
for emoji in sorted(emojis, key=lambda k: k['emojiName']):
j += 1
_showChatItem(emoji, Ent.CHAT_EMOJI, FJQC, j, jcount)
Ind.Decrement()
elif emojis:
for emoji in sorted(emojis, key=lambda k: k['emojiName']):
_printChatItem(user, emoji, '', Ent.CHAT_EMOJI, csvPF, FJQC)
elif GC.Values[GC.CSV_OUTPUT_USERS_AUDIT]:
csvPF.WriteRowNoFilter({'User': user})
if csvPF:
csvPF.writeCSVfile('Chat Custom Emojis')
# gam setup chat
def doSetupChat():
_getMain().checkForExtraneousArguments()
_, chat , _ = buildChatServiceObject()
_getMain().writeStdout(Msg.TO_SET_UP_GOOGLE_CHAT.format(setupChatURL(chat), GM.Globals[GM.OAUTH2SERVICE_JSON_DATA]['project_id']))
def getChatSectionName():
if not Cmd.ArgumentsRemaining():
_getMain().missingArgumentExit('<ChatSection>')
myarg = Cmd.Current().lower()
Cmd.Advance()
if myarg == 'section':
chatSection = _getMain().getString(Cmd.OB_CHAT_SECTION)
if chatSection.startswith('sections/') or chatSection.startswith('users/'):
return chatSection
return 'sections/'+chatSection
if myarg.startswith('sections/') or myarg.startswith('users/'):
return Cmd.Previous()
return 'sections/'+Cmd.Previous()
CHAT_SECTION_POSITION = {
'start': 'START',
'end': 'END'
}
# gam <UserTypeEntity> create chatsection
# displayname <String>
# [formatjson|returnidonly]
# gam <UserTypeEntity> update chatsection <ChatSection>
# [displayname <String>]
# [(sortorder <Integer>)|(position start|end)]
# [formatjson]
def createUpdateChatSection(users):
updateCmd = Act.Get() == Act.UPDATE
FJQC = _getMain().FormatJSONQuoteChar()
name = None
body = {}
posbody = {}
posloc = None
returnIdOnly = False
updateMask = set()
if updateCmd:
name = getChatSectionName()
while Cmd.ArgumentsRemaining():
myarg = _getMain().getArgument()
if myarg == 'displayname':
body['displayName'] = _getMain().getString(Cmd.OB_STRING, minLen=1, maxLen=80)
updateMask.add('displayName')
elif updateCmd and myarg == 'sortorder':
posloc = Cmd.Location()
posbody['sortOrder'] = _getMain().getInteger(minVal=1)
elif updateCmd and myarg == 'relativeposition':
posloc = Cmd.Location()
posbody['relativePosition'] = _getMain().getChoice(CHAT_SECTION_POSITION, mapChoice=True)
elif not updateCmd and myarg == 'returnidonly':
returnIdOnly = True
else:
FJQC.GetFormatJSON(myarg)
if updateCmd:
if not name:
_getMain().missingArgumentExit('<ChatSection>')
if 'sortOrder' in posbody and 'relativePosition' in posbody:
Cmd.SetLocation(posloc-1)
_getMain().usageErrorExit(Msg.ARE_MUTUALLY_EXCLUSIVE.format('sortorder', 'relativeposition'))
else:
if 'displayName' not in body:
_getMain().missingArgumentExit('displayname')
body['type'] = 'CUSTOM_SECTION'
i, count, users = _getMain().getEntityArgument(users)
for user in users:
i += 1
user, chat, kvList = buildChatServiceObject(API.CHAT_SECTIONS, user, i, count,
[Ent.CHAT_SECTION, body.get('displayName', '')])
if not chat:
continue
try:
if not updateCmd:
section = _getMain().callGAPI(chat.users().sections(), 'create',
bailOnInternalError=True,
throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.INTERNAL_ERROR,
GAPI.PERMISSION_DENIED, GAPI.FAILED_PRECONDITION],
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
parent=f'users/{user}', body=body)
if not returnIdOnly:
kvList[-1] = section['name']
if not FJQC.formatJSON:
_getMain().entityActionPerformed(kvList, i, count)
Ind.Increment()
_showChatItem(section, Ent.CHAT_SECTION, FJQC)
Ind.Decrement()
else:
_getMain().writeStdout(f'{section["name"]}\n')
else:
pname = name
if not pname.startswith('users/'):
pname = f'users/{user}/{name}'
kvList[-1] = pname
if not body and not posbody:
_getMain().entityActionNotPerformedWarning(kvList, Msg.NO_CHANGES, i, count)
continue
if body:
section = _getMain().callGAPI(chat.users().sections(), 'patch',
bailOnInternalError=True,
throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.INTERNAL_ERROR,
GAPI.PERMISSION_DENIED, GAPI.FAILED_PRECONDITION],
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
name=pname, updateMask=','.join(updateMask), body=body)
if posbody:
section = _getMain().callGAPI(chat.users().sections(), 'position',
bailOnInternalError=True,
throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.INTERNAL_ERROR,
GAPI.PERMISSION_DENIED, GAPI.FAILED_PRECONDITION],
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
name=pname, body=posbody)
section = section['section']
kvList[-1] = section['name']
if not FJQC.formatJSON:
_getMain().entityActionPerformed(kvList, i, count)
Ind.Increment()
_showChatItem(section, Ent.CHAT_SECTION, FJQC)
Ind.Decrement()
except (GAPI.notFound, GAPI.invalidArgument, GAPI.internalError, GAPI.permissionDenied) as e:
exitIfChatNotConfigured(chat, kvList, str(e), i, count)
continue
except GAPI.failedPrecondition:
_getMain().userChatServiceNotEnabledWarning(user, i, count)
continue
except AttributeError:
_getMain().systemErrorExit(_getMain().GOOGLE_API_ERROR_RC, Msg.DEVELOPER_PREVIEW_REQUIRED)
# gam <UserTypeEntity> delete chatsection <ChatSection>
def deleteChatSection(users):
name = getChatSectionName()
_getMain().checkForExtraneousArguments()
i, count, users = _getMain().getEntityArgument(users)
for user in users:
i += 1
user, chat, kvList = buildChatServiceObject(API.CHAT_SECTIONS, user, i, count,
[Ent.CHAT_SECTION, name])
if not chat:
continue
try:
pname = name
if not pname.startswith('users/'):
pname = f'users/{user}/{name}'
kvList[-1] = pname
_getMain().callGAPI(chat.users().sections(), 'delete',
bailOnInternalError=True,
throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.INTERNAL_ERROR,
GAPI.PERMISSION_DENIED, GAPI.FAILED_PRECONDITION],
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
name=pname)
_getMain().entityActionPerformed(kvList, i, count)
except (GAPI.notFound, GAPI.invalidArgument, GAPI.internalError, GAPI.permissionDenied) as e:
exitIfChatNotConfigured(chat, kvList, str(e), i, count)
continue
except GAPI.failedPrecondition:
_getMain().userChatServiceNotEnabledWarning(user, i, count)
continue
except AttributeError:
_getMain().systemErrorExit(_getMain().GOOGLE_API_ERROR_RC, Msg.DEVELOPER_PREVIEW_REQUIRED)
# gam <UserTypeEntity> show chatsections
# [formatjson]
# gam <UserTypeEntity> print chatsections [todrive <ToDriveAttribute>*]
# [formatjson [quotechar <Character>]]
def printShowChatSections(users):
csvPF = _getMain().CSVPrintFile(['User', 'name']) if Act.csvFormat() else None
FJQC = _getMain().FormatJSONQuoteChar(csvPF)
while Cmd.ArgumentsRemaining():
myarg = _getMain().getArgument()
if csvPF and myarg == 'todrive':
csvPF.GetTodriveParameters()
else:
FJQC.GetFormatJSONQuoteChar(myarg, True)
i, count, users = _getMain().getEntityArgument(users)
for user in users:
i += 1
user, chat, kvList = buildChatServiceObject(API.CHAT_SECTIONS, user, i, count)
if not chat:
continue
try:
sections = _getMain().callGAPIpages(chat.users().sections(), 'list', 'sections',
pageMessage=_getChatPageMessage(Ent.CHAT_SECTION, user, i, count, None),
bailOnInternalError=True,
throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.INTERNAL_ERROR,
GAPI.PERMISSION_DENIED, GAPI.FAILED_PRECONDITION],
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
parent=f'users/{user}', pageSize=GC.Values[GC.CHAT_MAX_RESULTS])
except (GAPI.notFound, GAPI.invalidArgument, GAPI.internalError, GAPI.permissionDenied) as e:
exitIfChatNotConfigured(chat, kvList, str(e), i, count)
continue
except GAPI.failedPrecondition:
_getMain().userChatServiceNotEnabledWarning(user, i, count)
continue
except AttributeError:
_getMain().systemErrorExit(_getMain().GOOGLE_API_ERROR_RC, Msg.DEVELOPER_PREVIEW_REQUIRED)
jcount = len(sections)
if jcount == 0:
_getMain().setSysExitRC(_getMain().NO_ENTITIES_FOUND_RC)
if not csvPF:
if not FJQC.formatJSON:
_getMain().entityPerformActionNumItems(kvList, jcount, Ent.CHAT_SECTION, i, count)
Ind.Increment()
j = 0
for section in sections:
j += 1
_showChatItem(section, Ent.CHAT_SECTION, FJQC, j, jcount)
Ind.Decrement()
else:
for section in sections:
_printChatItem(user, section, None, Ent.CHAT_SECTION, csvPF, FJQC)
if csvPF:
csvPF.writeCSVfile('Chat Sections')
# gam <UserTypeEntity> move chatsectionitem <ChatSectionItem> to <ChatSection>
def moveShowChatSectionItem(users):
name = getChatSectionName()
if Cmd.PeekArgumentPresent(['to']):
Cmd.Advance()
target = getChatSectionName()
_getMain().checkForExtraneousArguments()
i, count, users = _getMain().getEntityArgument(users)
for user in users:
i += 1
user, chat, kvList = buildChatServiceObject(API.CHAT_SECTIONS, user, i, count,
[Ent.CHAT_SECTION_ITEM, name])
if not chat:
continue
try:
pname = name
if not pname.startswith('users/'):
pname = f'users/{user}/{name}'
kvList[-1] = pname
ptarget = target
if not ptarget.startswith('users/'):
ptarget = f'users/{user}/{target}'
kvList[-1] = ptarget
_getMain().callGAPI(chat.users().sections().items(), 'move',
bailOnInternalError=True,
throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.INTERNAL_ERROR,
GAPI.PERMISSION_DENIED, GAPI.FAILED_PRECONDITION],
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
name=pname, body={'targetSection': ptarget})
_getMain().entityModifierItemValueListActionPerformed(kvList, Act.MODIFIER_TO, [Ent.CHAT_SECTION, ptarget], i, count)
except (GAPI.notFound, GAPI.invalidArgument, GAPI.internalError, GAPI.permissionDenied) as e:
exitIfChatNotConfigured(chat, kvList, str(e), i, count)
continue
except GAPI.failedPrecondition:
_getMain().userChatServiceNotEnabledWarning(user, i, count)
continue
except AttributeError:
_getMain().systemErrorExit(_getMain().GOOGLE_API_ERROR_RC, Msg.DEVELOPER_PREVIEW_REQUIRED)
# gam <UserTypeEntity> show chatsectionitems <ChatSection>
# [space <ChatSpace>]
# [formatjson]
# gam <UserTypeEntity> print chatsectionitems <ChatSection> [todrive <ToDriveAttribute>*]
# [space <ChatSpace>]
# [formatjson [quotechar <Character>]]
def printShowChatSectionItems(users):
cd = _getMain().buildGAPIObject(API.DIRECTORY)
csvPF = _getMain().CSVPrintFile(['User', 'name', 'space']) if Act.csvFormat() else None
FJQC = _getMain().FormatJSONQuoteChar(csvPF)
name = getChatSectionName()
kwargs = {}
while Cmd.ArgumentsRemaining():
myarg = _getMain().getArgument()
if csvPF and myarg == 'todrive':
csvPF.GetTodriveParameters()
elif myarg == 'space' or myarg.startswith('spaces/') or myarg.startswith('space/'):
kwargs['filter'] = f'space = {getSpaceName(myarg)}'
else:
FJQC.GetFormatJSONQuoteChar(myarg, True)
i, count, users = _getMain().getEntityArgument(users)
for user in users:
i += 1
user, chat, kvList = buildChatServiceObject(API.CHAT_SECTIONS, user, i, count)
if not chat:
continue
_, chatsp, _ = buildChatServiceObject(API.CHAT_SPACES, user, i, count)
if not chatsp:
continue
_, chatme, _ = buildChatServiceObject(API.CHAT_MEMBERSHIPS, user, i, count)
if not chatme:
continue
pname = name
if not pname.startswith('users/'):
pname = f'users/{user}/{name}'
kvList[-1] = pname
try:
sectionItems = _getMain().callGAPIpages(chat.users().sections().items(), 'list', 'sectionItems',
pageMessage=_getChatPageMessage(Ent.CHAT_SECTION_ITEM, user, i, count, None),
bailOnInternalError=True,
throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.INTERNAL_ERROR,
GAPI.PERMISSION_DENIED, GAPI.FAILED_PRECONDITION],
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
parent=pname, pageSize=GC.Values[GC.CHAT_MAX_RESULTS], **kwargs)
for sectionItem in sectionItems:
space = _getMain().callGAPI(chatsp.spaces(), 'get',
bailOnInternalError=True,
throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.INTERNAL_ERROR,
GAPI.PERMISSION_DENIED, GAPI.FAILED_PRECONDITION],
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
name=sectionItem['space'], fields='displayName,spaceType')
sectionItem['spaceDetails'] = {'spaceType': space['spaceType']}
if space['spaceType'] == 'DIRECT_MESSAGE':
members = _getMain().callGAPIitems(chatme.spaces().members(), 'list', 'memberships',
bailOnInternalError=True,
throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.INTERNAL_ERROR,
GAPI.PERMISSION_DENIED, GAPI.FAILED_PRECONDITION],
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
parent=sectionItem['space'], fields='memberships(member)')
for member in members:
_getChatMemberEmail(cd, member)
sectionItem['spaceDetails']['members'] = ' '.join([member['member']['email'] for member in members])
elif 'displayName' in space:
sectionItem['spaceDetails']['displayName'] = space['displayName']
except (GAPI.notFound, GAPI.invalidArgument, GAPI.internalError, GAPI.permissionDenied) as e:
exitIfChatNotConfigured(chat, kvList, str(e), i, count)
continue
except GAPI.failedPrecondition:
_getMain().userChatServiceNotEnabledWarning(user, i, count)
continue
except AttributeError:
_getMain().systemErrorExit(_getMain().GOOGLE_API_ERROR_RC, Msg.DEVELOPER_PREVIEW_REQUIRED)
jcount = len(sectionItems)
if jcount == 0:
_getMain().setSysExitRC(_getMain().NO_ENTITIES_FOUND_RC)
if not csvPF:
if not FJQC.formatJSON:
_getMain().entityPerformActionNumItems(kvList, jcount, Ent.CHAT_SECTION_ITEM, i, count)
Ind.Increment()
j = 0
for sectionItem in sectionItems:
j += 1
_showChatItem(sectionItem, Ent.CHAT_SECTION_ITEM, FJQC, j, jcount)
Ind.Decrement()
else:
for sectionItem in sectionItems:
_printChatItem(user, sectionItem, None, Ent.CHAT_SECTION_ITEM, csvPF, FJQC)
if csvPF:
csvPF.writeCSVfile('Chat Sections')

601
src/gam/cmd/chat/spaces.py Normal file
View File

@@ -0,0 +1,601 @@
"""Chat space CRUD and listing.
Part of the _chat_tmp sub-package."""
"""GAM Google Chat management."""
import json
import sys
from gamlib import glaction
from gamlib import glapi as API
from gamlib import glcfg as GC
from gamlib import glclargs
from gamlib import glentity
from gamlib import glgapi as GAPI
from gamlib import glglobals as GM
from gamlib import glindent
from gamlib import glmsgs as Msg
Act = glaction.GamAction()
Ent = glentity.GamEntity()
Ind = glindent.GamIndent()
Cmd = glclargs.GamCLArgs()
def _getMain():
return sys.modules['gam']
def __getattr__(name):
"""Fall back to gam module for any undefined names."""
main = _getMain()
try:
return getattr(main, name)
except AttributeError:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
def getSpaceName(myarg):
if myarg == 'space':
chatSpace = _getMain().getString(Cmd.OB_CHAT_SPACE)
if chatSpace.startswith('spaces/'):
return chatSpace
if not chatSpace.startswith('space/'):
return 'spaces/'+chatSpace
_, chatSpace = chatSpace.split('/', 1)
else: # myarg.startswith('spaces/') or myarg.startswith('space/')
_, chatSpace = Cmd.Previous().split('/', 1)
return 'spaces/'+chatSpace
def getChatSpaceParameters(myarg, body, typeChoicesMap, updateMask):
if myarg == 'displayname':
body['displayName'] = _getMain().getString(Cmd.OB_STRING, minLen=0, maxLen=128)
updateMask.add('displayName')
elif myarg == 'type':
body['spaceType'] = _getMain().getChoice(typeChoicesMap, mapChoice=True)
updateMask.add('spaceType')
elif myarg == 'description':
body.setdefault('spaceDetails', {})
body['spaceDetails']['description'] = _getMain().getString(Cmd.OB_STRING, minLen=0, maxLen=150)
updateMask.add('spaceDetails')
elif myarg in {'guidelines', 'rules'}:
body.setdefault('spaceDetails', {})
body['spaceDetails']['guidelines'] = _getMain().getString(Cmd.OB_STRING, minLen=0, maxLen=5000)
updateMask.add('spaceDetails')
elif myarg == 'history':
body['spaceHistoryState'] = 'HISTORY_ON' if getBoolean() else 'HISTORY_OFF'
updateMask.add('spaceHistoryState')
elif myarg in {'audience', 'restricted'}:
body['accessSettings']= {'audience': None}
if myarg == 'audience':
body['accessSettings']['audience'] = _getMain().getString(Cmd.OB_STRING, minLen=1, maxLen=100)
updateMask.add('accessSettings.audience')
else:
return False
return True
CHAT_MEMBER_ROLE_MAP = {
'member': 'ROLE_MEMBER',
'manager': 'ROLE_ASSISTANT_MANAGER',
'owner': 'ROLE_MANAGER',
}
CHAT_ROLE_ENTITY_TYPE_MAP = {
'ROLE_MEMBER': Ent.CHAT_MEMBER_USER,
'ROLE_ASSISTANT_MANAGER': Ent.CHAT_MANAGER_USER,
'ROLE_MANAGER': Ent.CHAT_OWNER_USER,
}
CHAT_MEMBER_TYPE_MAP = {
'bot': 'BOT',
'human': 'HUMAN'
}
CHAT_SPACE_TYPE_MAP = {
'space': 'SPACE',
'groupchat': 'GROUP_CHAT',
'directmessage': 'DIRECT_MESSAGE',
}
CHAT_SPACE_PREDEFINED_PERMS_MAP = {
'announcement': 'ANNOUNCEMENT_SPACE',
'collaboration': 'COLLABORATION_SPACE',
}
CHAT_SPACE_MIN_MAX_MEMBERS = {
'SPACE': {'min': 0, 'max': 20},
'GROUP_CHAT': {'min': 2, 'max': 20},
'DIRECT_MESSAGE': {'min': 1, 'max': 1},
}
# gam <UserTypeEntity> create chatspace
# [type <ChatSpaceType>] [announcement|collaboration]
# [restricted|(audience <String>)]
# [externalusersallowed <Boolean>]
# [members <UserTypeEntity>]
# [displayname <String>]
# [description <String>] [guidelines|rules <String>]
# [<ChatContent>]
# [(csv [todrive <ToDriveAttribute>*] [formatjson [quotechar <Character>]] (addcsvdata <FieldName> <String>)*) | formatjson | returnidonly]
def createChatSpace(users):
def _writeSpaceDetails(space, resp=None):
baserow = {'User': user, 'name': space['name']}
if resp:
baserow['message.name'] = resp['name']
if addCSVData:
baserow.update(addCSVData)
row = _getMain().flattenJSON(space, flattened=baserow.copy(), timeObjects=CHAT_TIME_OBJECTS)
if not FJQC.formatJSON:
csvPF.WriteRowTitles(row)
else:
row = baserow.copy()
row['JSON'] = json.dumps(_getMain().cleanJSON(space, timeObjects=CHAT_TIME_OBJECTS), ensure_ascii=False, sort_keys=True)
csvPF.WriteRowNoFilter(row)
csvPF = None
FJQC = _getMain().FormatJSONQuoteChar(csvPF)
addCSVData = {}
body = {'space': {'spaceType': CHAT_SPACE_TYPE_MAP['space'], 'displayName': ''}, 'requestId': str(uuid.uuid4())}
members = []
tbody = {}
returnIdOnly = False
updateMask = set()
while Cmd.ArgumentsRemaining():
myarg = _getMain().getArgument()
if getChatSpaceParameters(myarg, body['space'], CHAT_SPACE_TYPE_MAP, updateMask):
pass
elif myarg in CHAT_SPACE_PREDEFINED_PERMS_MAP:
body['space']['predefinedPermissionSettings'] = CHAT_SPACE_PREDEFINED_PERMS_MAP[myarg]
elif myarg == 'externalusersallowed':
body['space']['externalUserAllowed'] = _getMain().getBoolean()
elif myarg == 'members':
_, members = _getMain().getEntityToModify(defaultEntityType=Cmd.ENTITY_USERS)
elif myarg == 'returnidonly':
returnIdOnly = True
elif myarg == 'csv':
csvPF = _getMain().CSVPrintFile(['User', 'name'])
FJQC = _getMain().FormatJSONQuoteChar(csvPF)
elif csvPF and myarg == 'todrive':
csvPF.GetTodriveParameters()
elif csvPF and myarg == 'addcsvdata':
_getMain().getAddCSVData(addCSVData)
elif myarg in _getMain().SORF_TEXT_ARGUMENTS:
tbody['text'] = _getMain().getStringOrFile(myarg, minLen=0, unescapeCRLF=True)[0]
else:
FJQC.GetFormatJSONQuoteChar(myarg)
spaceType = body['space']['spaceType']
jcount = len(members)
if (jcount < CHAT_SPACE_MIN_MAX_MEMBERS[spaceType]['min'] or
jcount > CHAT_SPACE_MIN_MAX_MEMBERS[spaceType]['max']):
_getMain().systemErrorExit(_getMain().USAGE_ERROR_RC,
Msg.INVALID_NUMBER_OF_CHAT_SPACE_MEMBERS.format(Ent.Singular(Ent.CHAT_SPACE),
spaceType, jcount,
CHAT_SPACE_MIN_MAX_MEMBERS[spaceType]['min'],
CHAT_SPACE_MIN_MAX_MEMBERS[spaceType]['max']))
mtype = CHAT_MEMBER_TYPE_MAP['human']
if members:
body['memberships'] = []
for member in members:
name = _getMain().normalizeEmailAddressOrUID(member)
body['memberships'].append({'member': {'name': f'users/{name}', 'type': mtype}})
if spaceType == 'SPACE':
if not body['space']['displayName']:
_getMain().missingArgumentExit('displayname')
elif spaceType == 'GROUP_CHAT':
body['space'].pop('displayName', None)
body['space'].pop('predefinedPermissionSettings', None)
else: # DIRECT_MESSAGE
body['space'].pop('displayName', None)
body['space'].pop('spaceDetails', None)
body['space'].pop('predefinedPermissionSettings', None)
body['space']['singleUserBotDm'] = False
if tbody:
trimChatMessageIfRequired(tbody)
if csvPF:
if tbody:
csvPF.AddTitle('message.name')
if addCSVData:
csvPF.AddTitles(sorted(addCSVData.keys()))
if FJQC.formatJSON:
csvPF.SetJSONTitles(csvPF.titlesList)
csvPF.AddJSONTitle('JSON')
csvPF.SetSortAllTitles()
i, count, users = _getMain().getEntityArgument(users)
for user in users:
i += 1
user, chat, kvList = buildChatServiceObject(API.CHAT_SPACES, user, i, count,
[Ent.CHAT_SPACE, body['space'].get('displayName', spaceType)])
if not chat:
continue
try:
space = _getMain().callGAPI(chat.spaces(), 'setup',
throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED, GAPI.FAILED_PRECONDITION],
body=body)
if returnIdOnly:
_getMain().writeStdout(f'{space["name"]}\n')
elif not csvPF:
kvList[-1] = space['name']
if not FJQC.formatJSON:
_getMain().entityActionPerformed(kvList, i, count)
Ind.Increment()
_showChatItem(space, Ent.CHAT_SPACE, FJQC)
Ind.Decrement()
elif not tbody:
_writeSpaceDetails(space, None)
except (GAPI.notFound, GAPI.invalidArgument, GAPI.permissionDenied) as e:
exitIfChatNotConfigured(chat, kvList, str(e), i, count)
continue
except GAPI.failedPrecondition:
_getMain().userChatServiceNotEnabledWarning(user, i, count)
continue
if tbody:
parent = space['name']
_, chat, kvList = buildChatServiceObject(API.CHAT_MESSAGES, user, i, count,
[Ent.CHAT_SPACE, body['space'].get('displayName', parent)])
if not chat:
if csvPF:
_writeSpaceDetails(space, None)
continue
try:
resp = _getMain().callGAPI(chat.spaces().messages(), 'create',
throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED],
parent=parent, requestId=str(uuid.uuid4()), body=tbody, fields='name')
if returnIdOnly:
_getMain().writeStdout(f'{resp["name"]}\n')
elif not csvPF:
if not FJQC.formatJSON:
kvList.extend([Ent.CHAT_MESSAGE, resp['name']])
_getMain().entityActionPerformed(kvList, i, count)
else:
_getMain().printLine(json.dumps(_getMain().cleanJSON(resp), ensure_ascii=False, sort_keys=True))
else:
_writeSpaceDetails(space, resp)
except (GAPI.notFound, GAPI.invalidArgument, GAPI.permissionDenied) as e:
exitIfChatNotConfigured(chat, kvList, str(e), i, count)
if csvPF:
_writeSpaceDetails(space, None)
if csvPF:
csvPF.writeCSVfile('Chat Spaces')
CHAT_UPDATE_SPACE_TYPE_MAP = {
'space': 'SPACE',
}
CHAT_SPACE_ROLE_PERMISSIONS_MAP = {
'owners': 'managersAllowed',
'managers': 'assistantManagersAllowed',
'members': 'membersAllowed',
}
CHAT_UPDATE_SPACE_PERMISSIONS_MAP = {
'managemembersandgroups': 'manageMembersAndGroups',
'modifyspacedetails': 'modifySpaceDetails',
'togglehistory': 'toggleHistory',
'useatmentionall': 'useAtMentionAll',
'manageapps': 'manageApps',
'managewebhooks': 'manageWebhooks',
'replymessages': 'replyMessages',
}
# gam <UserTypeEntity> update chatspace <ChatSpace>
# [restricted|(audience <String>)]|
# ([displayname <String>]
# [type space]
# [description <String>] [guidelines|rules <String>]
# [history <Boolean>])
# [managemembersandgroups owners|managers|members]
# [modifyspacedetails owners|managers|members]
# [togglehistory owners|managers|members]
# [useatmentionall owners|managers|members]
# [manageapps owners|managers|members]
# [managewebhooks owners|managers|members]
# [replymessages owners|managers|members]
# [formatjson]
def updateChatSpace(users):
FJQC = _getMain().FormatJSONQuoteChar()
useAdminAccess, api, kwargsUAA = _getChatAdminAccess(API.CHAT_SPACES_ADMIN, API.CHAT_SPACES)
name = None
body = {}
updateMask = set()
while Cmd.ArgumentsRemaining():
myarg = _getMain().getArgument()
if myarg == 'space' or myarg.startswith('spaces/') or myarg.startswith('space/'):
name = getSpaceName(myarg)
elif getChatSpaceParameters(myarg, body, CHAT_UPDATE_SPACE_TYPE_MAP, updateMask):
pass
elif myarg in CHAT_UPDATE_SPACE_PERMISSIONS_MAP:
body.setdefault('permissionSettings', {})
permissionSetting = CHAT_UPDATE_SPACE_PERMISSIONS_MAP[myarg]
role = _getMain().getChoice(CHAT_SPACE_ROLE_PERMISSIONS_MAP, mapChoice=True)
body['permissionSettings'][permissionSetting] = {}
body['permissionSettings'][permissionSetting][role] = True
if role == 'membersAllowed':
body['permissionSettings'][permissionSetting]['assistantManagersAllowed'] = True
body['permissionSettings'][permissionSetting]['managersAllowed'] = True
elif role == 'assistantManagersAllowed':
body['permissionSettings'][permissionSetting]['managersAllowed'] = True
updateMask.add(f'permissionSettings.{permissionSetting}')
else:
FJQC.GetFormatJSON(myarg)
if not name:
_getMain().missingArgumentExit('space')
if 'accessSettings.audience' in updateMask:
tempMask = updateMask-{'accessSettings.audience'}
if tempMask:
_getMain().usageErrorExit(Msg.ARE_MUTUALLY_EXCLUSIVE.format('restricted/audience', 'displayname,type,description,guidelines,history'))
i, count, users = _getMain().getEntityArgument(users)
if useAdminAccess:
_chkChatAdminAccess(count)
for user in users:
i += 1
user, chat, kvList = buildChatServiceObject(api, user, i, count, [Ent.CHAT_SPACE, name], useAdminAccess)
if not chat:
continue
try:
space = _getMain().callGAPI(chat.spaces(), 'patch',
throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED, GAPI.FAILED_PRECONDITION],
name=name, updateMask=','.join(updateMask), body=body, **kwargsUAA)
if not FJQC.formatJSON:
_getMain().entityActionPerformed(kvList, i, count)
Ind.Increment()
_showChatItem(space, Ent.CHAT_SPACE, FJQC)
Ind.Decrement()
except (GAPI.notFound, GAPI.invalidArgument, GAPI.permissionDenied) as e:
exitIfChatNotConfigured(chat, kvList, str(e), i, count)
except GAPI.failedPrecondition:
_getMain().userChatServiceNotEnabledWarning(user, i, count)
# gam <UserTypeEntity> delete chatspace <ChatSpace>
# gam <UserItem> delete chatspace asadmin <ChatSpace>
def deleteChatSpace(users):
name = None
useAdminAccess, api, kwargsUAA = _getChatAdminAccess(API.CHAT_SPACES_DELETE_ADMIN, API.CHAT_SPACES_DELETE)
while Cmd.ArgumentsRemaining():
myarg = _getMain().getArgument()
if myarg == 'space' or myarg.startswith('spaces/') or myarg.startswith('space/'):
name = getSpaceName(myarg)
else:
_getMain().unknownArgumentExit()
if not name:
_getMain().missingArgumentExit('space')
i, count, users = _getMain().getEntityArgument(users)
if useAdminAccess:
_chkChatAdminAccess(count)
for user in users:
i += 1
user, chat, kvList = buildChatServiceObject(api, user, i, count, [Ent.CHAT_SPACE, name], useAdminAccess)
if not chat:
continue
try:
_getMain().callGAPI(chat.spaces(), 'delete',
throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED, GAPI.FAILED_PRECONDITION],
name=name, **kwargsUAA)
_getMain().entityActionPerformed(kvList, i, count)
except (GAPI.notFound, GAPI.invalidArgument, GAPI.permissionDenied) as e:
exitIfChatNotConfigured(chat, kvList, str(e), i, count)
except GAPI.failedPrecondition:
_getMain().userChatServiceNotEnabledWarning(user, i, count)
CHAT_SPACES_FIELDS_CHOICE_MAP = {
"accesssettings": "accessSettings",
"admininstalled": "adminInstalled",
"createtime": "createTime",
"displayname": "displayName",
"externaluserallowed": "externalUserAllowed",
"importmode": "importMode",
"lastactivetime": "lastActiveTime",
"membershipcount": "membershipCount",
"name": "name",
"permissionsettings": "permissionSettings",
"singleuserbotdm": "singleUserBotDm",
"spacedetails": "spaceDetails",
"spacehistorystate": "spaceHistoryState",
"spacethreadingstate": "spaceThreadingState",
"spacetype": "spaceType",
"spaceuri": "spaceUri",
"threaded": "spaceThreadingState",
"type": "spaceType",
}
# gam [<UserTypeEntity>] info chatspace <ChatSpace>
# [fields <ChatSpaceFieldNameList>]
# [formatjson]
# gam <UserItem> info chatspace asadmin <ChatSpace>
# [fields <ChatSpaceFieldNameList>]
# [formatjson]
def infoChatSpace(users, name=None):
FJQC = _getMain().FormatJSONQuoteChar()
if name is None:
function = 'get'
useAdminAccess, api, kwargsUAA = _getChatAdminAccess(API.CHAT_SPACES_ADMIN, API.CHAT_SPACES)
else:
function = 'findDirectMessage'
useAdminAccess = None
kwargsUAA = {}
api = API.CHAT_SPACES
fieldsList = []
while Cmd.ArgumentsRemaining():
myarg = _getMain().getArgument()
if function == 'get' and (myarg == 'space' or myarg.startswith('spaces/') or myarg.startswith('space/')):
name = getSpaceName(myarg)
elif _getMain().getFieldsList(myarg, CHAT_SPACES_FIELDS_CHOICE_MAP, fieldsList, initialField='name', onlyFieldsArg=True):
pass
else:
FJQC.GetFormatJSON(myarg)
if function == 'get' and not name:
_getMain().missingArgumentExit('space')
fields = _getMain().getFieldsFromFieldsList(fieldsList)
i, count, users = _getMain().getEntityArgument(users)
if useAdminAccess:
_chkChatAdminAccess(count)
for user in users:
i += 1
user, chat, kvList = buildChatServiceObject(api, user, i, count, [Ent.CHAT_SPACE, name], useAdminAccess)
if not chat:
continue
try:
space = _getMain().callGAPI(chat.spaces(), function,
throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED, GAPI.FAILED_PRECONDITION],
name=name, fields=fields, **kwargsUAA)
if not FJQC.formatJSON:
_getMain().entityPerformAction(kvList, i, count)
Ind.Increment()
_showChatItem(space, Ent.CHAT_SPACE, FJQC)
Ind.Decrement()
except (GAPI.notFound, GAPI.invalidArgument, GAPI.permissionDenied) as e:
exitIfChatNotConfigured(chat, kvList, str(e), i, count)
except GAPI.failedPrecondition:
_getMain().userChatServiceNotEnabledWarning(user, i, count)
def doInfoChatSpace():
infoChatSpace([None])
# gam [<UserTypeEntity>] info chatspacedm <UserItem>
# [fields <ChatSpaceFieldNameList>]
# [formatjson]
def infoChatSpaceDM(users):
cd = _getMain().buildGAPIObject(API.DIRECTORY)
name = _getMain().convertEmailAddressToUID(_getMain().getEmailAddress(returnUIDprefix='uid:'), cd, 'user')
infoChatSpace(users, f'users/{name}')
def _getChatSpaceListParms(myarg, kwargs):
if myarg in {'type', 'types'}:
for ctype in _getMain().getString(Cmd.OB_GROUP_ROLE_LIST).lower().replace(',', ' ').split():
if ctype in CHAT_SPACE_TYPE_MAP:
kwargs.setdefault('filter', '')
if kwargs['filter']:
kwargs['filter'] += ' OR '
kwargs['filter'] += f'spaceType = "{CHAT_SPACE_TYPE_MAP[ctype]}"'
else:
_getMain().invalidChoiceExit(ctype, CHAT_SPACE_TYPE_MAP, True)
else:
return False
return True
def _getChatSpaceSearchParms(myarg, queries, queryTimes, OBY):
if myarg == 'orderby':
OBY.GetChoice()
elif myarg == 'query':
queries[0] += ' AND '+_getMain().getString(Cmd.OB_QUERY)
elif myarg.startswith('querytime'):
queryTimes[myarg] = _getMain().getTimeOrDeltaFromNow()
else:
return False
return True
CHAT_SPACES_ADMIN_ORDERBY_CHOICE_MAP = {
'createtime': 'createTime',
'lastactivetime': 'lastActiveTime',
'membershipcount': 'membershipCount.joined_direct_human_user_count'
}
# gam [<UserTypeEntity>] show chatspaces
# [types <ChatSpaceTypeList>]
# [fields <ChatSpaceFieldNameList>] [showaccesssettings]
# [formatjson]
# gam [<UserTypeEntity>] print chatspaces [todrive <ToDriveAttribute>*]
# [types <ChatSpaceTypeList>]
# [fields <ChatSpaceFieldNameList>] [showaccesssettings]
# [formatjson [quotechar <Character>]]
# gam <UserItem> show chatspaces asadmin
# [query <String>] [querytime<String> <Time>]
# [orderby <ChatSpaceAdminOrderByFieldName> [ascending|descending]]
# [fields <ChatSpaceFieldNameList>] [showaccesssettings]
# [formatjson]
# gam <UserItem> print chatspaces asadmin [todrive <ToDriveAttribute>*]
# [query <String>] [querytime<String> <Time>]
# [orderby <ChatSpaceAdminOrderByFieldName> [ascending|descending]]
# [fields <ChatSpaceFieldNameList>] [showaccesssettings]
# [formatjson [quotechar <Character>]]
def printShowChatSpaces(users):
csvPF = _getMain().CSVPrintFile(['User', 'name'] if not isinstance(users, list) else ['name']) if Act.csvFormat() else None
FJQC = _getMain().FormatJSONQuoteChar(csvPF)
OBY = _getMain().OrderBy(CHAT_SPACES_ADMIN_ORDERBY_CHOICE_MAP)
useAdminAccess, api, kwargsCS = _getChatAdminAccess(API.CHAT_SPACES_ADMIN, API.CHAT_SPACES)
kwargsSAS = {'useAdminAccess': useAdminAccess}
fieldsList = []
queries = []
queryTimes = {}
pfilter = ''
showAccessSettings = False
if useAdminAccess:
function = 'search'
queries = ['customer = "customers/my_customer" AND spaceType = "SPACE"']
else:
function = 'list'
while Cmd.ArgumentsRemaining():
myarg = _getMain().getArgument()
if csvPF and myarg == 'todrive':
csvPF.GetTodriveParameters()
elif _getMain().getFieldsList(myarg, CHAT_SPACES_FIELDS_CHOICE_MAP, fieldsList, initialField='name', onlyFieldsArg=True):
pass
elif not useAdminAccess and _getChatSpaceListParms(myarg, kwargsCS):
pass
elif useAdminAccess and _getChatSpaceSearchParms(myarg, queries, queryTimes, OBY):
pass
elif myarg == 'showaccesssettings':
showAccessSettings = True
else:
FJQC.GetFormatJSONQuoteChar(myarg, True)
if showAccessSettings and fieldsList:
fieldsList.extend(['name', 'spaceType'])
fields = _getMain().getItemFieldsFromFieldsList('spaces', fieldsList)
i, count, users = _getMain().getEntityArgument(users)
if useAdminAccess:
_chkChatAdminAccess(count)
kwargsCS['orderBy'] = OBY.orderBy
_getMain().substituteQueryTimes(queries, queryTimes)
pfilter = kwargsCS['query'] = queries[0]
kwargsCS['useAdminAccess'] = True
sortName = 'displayName' if 'displayName' in fieldsList else 'name'
else:
sortName = 'name'
for user in users:
i += 1
user, chat, kvList = buildChatServiceObject(api, user, i, count, None, useAdminAccess)
if not chat:
continue
try:
spaces = _getMain().callGAPIpages(chat.spaces(), function, 'spaces',
pageMessage=_getChatPageMessage(Ent.CHAT_SPACE, user, i, count, pfilter, useAdminAccess),
bailOnInternalError=True,
throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.INTERNAL_ERROR,
GAPI.PERMISSION_DENIED, GAPI.FAILED_PRECONDITION],
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
fields=fields, pageSize=GC.Values[GC.CHAT_MAX_RESULTS], **kwargsCS)
if showAccessSettings:
for space in spaces:
if space['spaceType'] == 'SPACE':
try:
result = _getMain().callGAPI(chat.spaces(), 'get',
throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.INTERNAL_ERROR,
GAPI.PERMISSION_DENIED, GAPI.FAILED_PRECONDITION],
name=space['name'], fields='accessSettings', **kwargsSAS)
space.update(result)
except (GAPI.notFound, GAPI.invalidArgument, GAPI.internalError, GAPI.permissionDenied, GAPI.failedPrecondition):
pass
except (GAPI.notFound, GAPI.invalidArgument, GAPI.internalError, GAPI.permissionDenied) as e:
exitIfChatNotConfigured(chat, kvList, str(e), i, count)
continue
except GAPI.failedPrecondition:
_getMain().userChatServiceNotEnabledWarning(user, i, count)
continue
jcount = len(spaces)
if jcount == 0:
_getMain().setSysExitRC(_getMain().NO_ENTITIES_FOUND_RC)
if not csvPF:
if not FJQC.formatJSON:
_getMain().entityPerformActionNumItems(kvList, jcount, Ent.CHAT_SPACE, i, count)
Ind.Increment()
j = 0
for space in sorted(spaces, key=lambda k: k[sortName]):
j += 1
_showChatItem(space, Ent.CHAT_SPACE, FJQC, j, jcount)
Ind.Decrement()
else:
for space in sorted(spaces, key=lambda k: k[sortName]):
_printChatItem(user, space, None, Ent.CHAT_SPACE, csvPF, FJQC)
if csvPF:
csvPF.writeCSVfile('Chat Spaces')
def doPrintShowChatSpaces():
printShowChatSpaces([None])