"""Chat members, messages, reactions, and events. Part of the _chat_tmp sub-package.""" """GAM Google Chat management.""" import sys import uuid 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 _getChatMemberEmail(cd, member): if 'member' in member: if member['member']['type'] == 'HUMAN': _, memberUid = member['member']['name'].split('/') member['member']['email'], _ = convertUIDtoEmailAddressWithType(f'uid:{memberUid}', cd, None, emailTypes=['user']) if member['member']['email'].find('@') == -1: member['member']['email'] = 'id:'+member['member']['email'] elif 'groupMember' in member: _, memberUid = member['groupMember']['name'].split('/') member['groupMember']['email'], _ = convertUIDtoEmailAddressWithType(f'uid:{memberUid}', cd, None, emailTypes=['group']) def _getChatSpaceMembers(cd, chatSpace, ciGroupName): if chatSpace.startswith('space/'): _, chatSpace = chatSpace.split('/', 1) chatSpace = 'spaces/'+chatSpace kwargsUAA = {'useAdminAccess': True, 'filter': 'member.type != "BOT"'} user, chat, kvList = buildChatServiceObject(API.CHAT_MEMBERSHIPS_ADMIN, _getMain()._getAdminEmail(), 0, 0, [Ent.CHAT_SPACE, chatSpace], True) memberList = [] if not chat: return memberList fields = _getMain().getItemFieldsFromFieldsList('memberships', []) qfilter = f'{Ent.Singular(Ent.CHAT_SPACE)}: {chatSpace}, {kwargsUAA["filter"]}' try: members = _getMain().callGAPIpages(chat.spaces().members(), 'list', 'memberships', pageMessage=_getChatPageMessage(Ent.CHAT_MEMBER, user, 0, 0, qfilter), throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED], retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS, parent=chatSpace, fields=fields, pageSize=GC.Values[GC.CHAT_MAX_RESULTS], **kwargsUAA) for member in members: _getChatMemberEmail(cd, member) gmember = {} if 'member' in member: if member['member']['type'] == 'HUMAN': _, memberUid = member['member']['name'].split('/') gmember['type'] = Ent.TYPE_USER email, _ = _getMain().convertUIDtoEmailAddressWithType(f'uid:{memberUid}', cd, None, emailTypes=['user']) role = Ent.ROLE_MANAGER if member['role'] == 'ROLE_MANAGER' else Ent.ROLE_MEMBER if not ciGroupName: gmember['id'] = memberUid gmember['email'] = email gmember['role'] = role gmember['status'] = member['state'] else: gmember['name'] = f'{ciGroupName}/memberships/{memberUid}' gmember['preferredMemberKey'] = {'id': email} gmember['roles'] = [{'name': role}] gmember['createTime'] = member['createTime'] memberList.append(gmember) elif 'groupMember' in member: _, memberUid = member['groupMember']['name'].split('/') gmember['type'] = Ent.TYPE_GROUP role = Ent.ROLE_MANAGER if member['role'] == 'ROLE_MANAGER' else Ent.ROLE_MEMBER email, _ = _getMain().convertUIDtoEmailAddressWithType(f'uid:{memberUid}', cd, None, emailTypes=['group']) if not ciGroupName: gmember['id'] = memberUid gmember['email'] = email gmember['role'] = role gmember['status'] = member['state'] else: gmember['name'] = f'{ciGroupName}/memberships/{memberUid}' gmember['preferredMemberKey'] = {'id': email} gmember['roles'] = [{'name': role}] gmember['createTime'] = member['createTime'] memberList.append(gmember) return memberList except (GAPI.notFound, GAPI.invalidArgument, GAPI.permissionDenied) as e: exitIfChatNotConfigured(chat, kvList, str(e), 0, 0) return memberList def normalizeUserMember(user, userList): userList.append(_getMain().normalizeEmailAddressOrUID(user)) def getUserMemberID(cd, user, userList): userList.append(_getMain().convertEmailAddressToUID(user, cd, emailType='user')) def getGroupMemberID(cd, group, groupList): groupList.append(_getMain().convertEmailAddressToUID(group, cd, emailType='group')) # gam create chatmember # [type human|bot] [role member|manager|owner] # (user )* (members )* # (group )* (groups )* # [formatjson|returnidonly] # gam create chatmember asadmin # [type human|bot] [role member|manager|owner] # (user )* (members )* # (group )* (groups )* # [formatjson|returnidonly] def createChatMember(users): def addMembers(members, field, entityType, i, count): jcount = len(members) _getMain().entityPerformActionNumItems(kvList, jcount, entityType, i, count) if jcount == 0: return kvList.extend([entityType, '']) Ind.Increment() j = 0 for body in members: j += 1 kvList[-1] = body[field]['name'] try: member = _getMain().callGAPI(chat.spaces().members(), 'create', bailOnInternalError=True, throwReasons=[GAPI.ALREADY_EXISTS, GAPI.NOT_FOUND, GAPI.INVALID, GAPI.INVALID_ARGUMENT, GAPI.INTERNAL_ERROR, GAPI.PERMISSION_DENIED, GAPI.FAILED_PRECONDITION], parent=parent, body=body, **kwargsUAA) if role != 'ROLE_MEMBER' and entityType in (Ent.CHAT_MANAGER_USER, Ent.CHAT_OWNER_USER): member = _getMain().callGAPI(chat.spaces().members(), 'patch', bailOnInternalError=True, throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.INTERNAL_ERROR, GAPI.PERMISSION_DENIED, GAPI.FAILED_PRECONDITION], name=member['name'], updateMask='role', body={'role': role}, **kwargsUAA) if not returnIdOnly: kvList[-1] = member['name'] _getChatMemberEmail(cd, member) if not FJQC.formatJSON: _getMain().entityActionPerformed(kvList, j, jcount) Ind.Increment() _showChatItem(member, Ent.CHAT_MEMBER, FJQC) Ind.Decrement() else: _getMain().writeStdout(f'{member["name"]}\n') except (GAPI.alreadyExists, GAPI.notFound, GAPI.invalid, GAPI.invalidArgument, GAPI.permissionDenied, GAPI.internalError) as e: _getMain().entityActionFailedWarning(kvList, str(e)) except GAPI.failedPrecondition: _getMain().userChatServiceNotEnabledWarning(user, i, count) Ind.Decrement() cd = _getMain().buildGAPIObject(API.DIRECTORY) FJQC = _getMain().FormatJSONQuoteChar() parent = None role = CHAT_MEMBER_ROLE_MAP['member'] mtype = CHAT_MEMBER_TYPE_MAP['human'] userList = [] groupList = [] returnIdOnly = False useAdminAccess, api, kwargsUAA = _getChatAdminAccess(API.CHAT_MEMBERSHIPS_ADMIN, API.CHAT_MEMBERSHIPS) while Cmd.ArgumentsRemaining(): myarg = _getMain().getArgument() if myarg == 'space' or myarg.startswith('spaces/') or myarg.startswith('space/'): parent = getSpaceName(myarg) elif myarg == 'user': normalizeUserMember(_getMain().getEmailAddress(returnUIDprefix='uid:'), userList) elif myarg in {'member', 'members'}: _, members = _getMain().getEntityToModify(defaultEntityType=Cmd.ENTITY_USERS) for user in members: normalizeUserMember(user, userList) elif myarg == 'group': getGroupMemberID(cd, _getMain().getEmailAddress(returnUIDprefix='uid:'), groupList) elif myarg == 'groups': for group in _getMain().getEntityList(Cmd.OB_GROUP_ENTITY): getGroupMemberID(cd, group, groupList) elif myarg == 'role': role = _getMain().getChoice(CHAT_MEMBER_ROLE_MAP, mapChoice=True) elif myarg == 'type': mtype = _getMain().getChoice(CHAT_MEMBER_TYPE_MAP, mapChoice=True) elif myarg == 'returnidonly': returnIdOnly = True else: FJQC.GetFormatJSON(myarg) if not parent: _getMain().missingArgumentExit('space') if not userList and not groupList: _getMain().missingArgumentExit('user|members|group|groups') userEntityType = CHAT_ROLE_ENTITY_TYPE_MAP[role] userMembers = [] for user in userList: userMembers.append({'member': {'name': f'users/{user}', 'type': mtype}}) groupMembers = [] for group in groupList: groupMembers.append({'groupMember': {'name': f'groups/{group}'}}) 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, parent], useAdminAccess) if not chat: continue Ind.Increment() if userMembers: addMembers(userMembers, 'member', userEntityType, i, count) if groupMembers: addMembers(groupMembers, 'groupMember', Ent.CHAT_MEMBER_GROUP, i, count) Ind.Decrement() def _deleteChatMembers(chat, kvList, jcount, memberNames, i, count, kwargsUAA): j = 0 for name in memberNames: j += 1 kvList[-1] = name try: _getMain().callGAPI(chat.spaces().members(), 'delete', bailOnInternalError=True, throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED, GAPI.INTERNAL_ERROR], name=name, **kwargsUAA) _getMain().entityActionPerformed(kvList, j, jcount) except GAPI.notFound as e: _getMain().entityActionFailedWarning(kvList, str(e), j, jcount) except (GAPI.invalidArgument, GAPI.permissionDenied, GAPI.internalError) as e: exitIfChatNotConfigured(chat, kvList, str(e), i, count) # gam delete chatmember # ((user )|(members )| # (group )|(groups ))+ # gam delete chatmember asadmin # ((user )|(members )| # (group )|(groups ))+ # gam remove chatmember # members # gam remove chatmember asadmin # members # gam update chatmember # role member|manager|owner # ((user )|(members ))+ # gam modify chatmember # role member|manager|owner # members # gam update chatmember asadmin # role member|manager|owner # ((user )|(members ))+ # gam modify chatmember asadmin # role member|manager|owner # members def deleteUpdateChatMember(users): cd = _getMain().buildGAPIObject(API.DIRECTORY) FJQC = _getMain().FormatJSONQuoteChar() action = Act.Get() deleteMode = action in {Act.DELETE, Act.REMOVE} parent = None body = {} memberNames = [] userList = [] groupList = [] useAdminAccess, api, kwargsUAA = _getChatAdminAccess(API.CHAT_MEMBERSHIPS_ADMIN, API.CHAT_MEMBERSHIPS) while Cmd.ArgumentsRemaining(): myarg = _getMain().getArgument() if action in {Act.UPDATE, Act.MODIFY} and myarg == 'role': body['role'] = _getMain().getChoice(CHAT_MEMBER_ROLE_MAP, mapChoice=True) continue if action in {Act.REMOVE, Act.MODIFY}: if myarg in {'member', 'members'}: memberNames.extend(_getMain().getString(Cmd.OB_CHAT_MEMBER).replace(',', ' ').split()) else: _getMain().unknownArgumentExit() else: # {Act.DELETE, Act.UPDATE} if myarg == 'space' or myarg.startswith('spaces/') or myarg.startswith('space/'): parent = getSpaceName(myarg) elif myarg == 'user': normalizeUserMember(_getMain().getEmailAddress(returnUIDprefix='uid:'), userList) elif myarg in {'member', 'members'}: _, members = _getMain().getEntityToModify(defaultEntityType=Cmd.ENTITY_USERS) for user in members: normalizeUserMember(user, userList) elif deleteMode and myarg == 'group': getGroupMemberID(cd, _getMain().getEmailAddress(returnUIDprefix='uid:'), groupList) elif deleteMode and myarg == 'groups': for group in _getMain().getEntityList(Cmd.OB_GROUP_ENTITY): getGroupMemberID(cd, group, groupList) else: _getMain().unknownArgumentExit() if not deleteMode and 'role' not in body: _getMain().missingArgumentExit('role') if action in {Act.REMOVE, Act.MODIFY}: if not memberNames: _getMain().missingArgumentExit('members') else: # {Act.DELETE, Act.UPDATE} if not parent: _getMain().missingArgumentExit('space') if not userList and not groupList: _getMain().missingArgumentExit('user|members|group|groups') for user in userList: memberNames.append(f'{parent}/members/{user}') for group in groupList: memberNames.append(f'{parent}/members/group-{group}') 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, parent] if parent is not None else None, useAdminAccess) if not chat: continue jcount = len(memberNames) _getMain().entityPerformActionNumItems(kvList, jcount, Ent.CHAT_MEMBER, i, count) kvList.extend([Ent.CHAT_MEMBER, '']) Ind.Increment() if deleteMode: _deleteChatMembers(chat, kvList, jcount, memberNames, i, count, kwargsUAA) else: j = 0 for name in memberNames: j += 1 kvList[-1] = name try: member = _getMain().callGAPI(chat.spaces().members(), 'patch', bailOnInternalError=True, throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.INTERNAL_ERROR, GAPI.PERMISSION_DENIED, GAPI.FAILED_PRECONDITION], name=name, updateMask='role', body=body, **kwargsUAA) _getChatMemberEmail(cd, member) Ind.Increment() _showChatItem(member, Ent.CHAT_MEMBER, FJQC, j, jcount) Ind.Decrement() except GAPI.notFound as e: _getMain().entityActionFailedWarning(kvList, str(e), j, jcount) except (GAPI.invalidArgument, GAPI.permissionDenied, GAPI.internalError) as e: exitIfChatNotConfigured(chat, kvList, str(e), i, count) except GAPI.failedPrecondition: _getMain().userChatServiceNotEnabledWarning(user, i, count) continue Ind.Decrement() CHAT_SYNC_PREVIEW_TITLES = ['space', 'member', 'role', 'action', 'message'] # gam sync chatmembers [asadmin] # [role member|manager|owner] [type human|bot] # [addonly|removeonly] # [preview [actioncsv]] # (users )* (groups )* def syncChatMembers(users): def _previewAction(members, entityType, jcount, action): Ind.Increment() j = 0 for member in members: j += 1 _getMain().entityActionPerformed([Ent.CHAT_SPACE, parent, entityType, member, Ent.ROLE, role], j, jcount) Ind.Decrement() if csvPF: for member in members: csvPF.WriteRow({'space': parent, 'member': member, 'role': role, 'action': Act.PerformedName(action), 'message': Act.PREVIEW}) def addMembers(memberNames, members, entityType, i, count): jcount = len(memberNames) _getMain().entityPerformActionNumItems(kvList, jcount, entityType, i, count) if jcount == 0: return if preview: _previewAction(memberNames, entityType, jcount, Act.REMOVE) return kvList.extend([entityType, '']) Ind.Increment() j = 0 for memberName in memberNames: j += 1 body = members[memberName] kvList[-1] = memberName try: _getMain().callGAPI(chat.spaces().members(), 'create', bailOnInternalError=True, throwReasons=[GAPI.ALREADY_EXISTS, GAPI.NOT_FOUND, GAPI.INVALID, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED, GAPI.INTERNAL_ERROR], parent=parent, body=body, **kwargsUAA) if role != 'ROLE_MEMBER' and entityType == Ent.CHAT_MANAGER_USER: _getMain().callGAPI(chat.spaces().members(), 'patch', bailOnInternalError=True, throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED, GAPI.INTERNAL_ERROR], name=memberName, updateMask='role', body={'role': role}, **kwargsUAA) _getMain().entityActionPerformed(kvList, j, jcount) except (GAPI.alreadyExists, GAPI.notFound, GAPI.invalid, GAPI.invalidArgument, GAPI.permissionDenied, GAPI.internalError) as e: _getMain().entityActionFailedWarning(kvList, str(e), j, jcount) Ind.Decrement() del kvList[-2:] def deleteMembers(memberNames, entityType, i, count): jcount = len(memberNames) _getMain().entityPerformActionNumItems(kvList, jcount, entityType, i, count) if jcount == 0: return if preview: _previewAction(memberNames, entityType, jcount, Act.ADD) return kvList.extend([entityType, '']) Ind.Increment() _deleteChatMembers(chat, kvList, jcount, memberNames, i, count, kwargsUAA) Ind.Decrement() del kvList[-2:] useAdminAccess, api, kwargsUAA = _getChatAdminAccess(API.CHAT_MEMBERSHIPS_ADMIN, API.CHAT_MEMBERSHIPS) cd = _getMain().buildGAPIObject(API.DIRECTORY) parent = None role = CHAT_MEMBER_ROLE_MAP['member'] mtype = CHAT_MEMBER_TYPE_MAP['human'] syncOperation = 'addremove' kwargs = {} preview = False csvPF = None userList = [] usersSpecified = False groupList = [] groupsSpecified = False while Cmd.ArgumentsRemaining(): myarg = _getMain().getArgument() if myarg == 'space' or myarg.startswith('spaces/') or myarg.startswith('space/'): parent = getSpaceName(myarg) elif myarg == 'role': role = _getMain().getChoice(CHAT_MEMBER_ROLE_MAP, mapChoice=True) elif myarg == 'type': mtype = _getMain().getChoice(CHAT_MEMBER_TYPE_MAP, mapChoice=True) elif myarg in {'addonly', 'removeonly'}: syncOperation = myarg elif myarg == 'preview': preview = True elif myarg == 'actioncsv': csvPF = _getMain().CSVPrintFile(CHAT_SYNC_PREVIEW_TITLES) elif myarg == 'users': for user in _getMain().getEntityList(Cmd.OB_USER_ENTITY): getUserMemberID(cd, user, userList) usersSpecified = True elif myarg in {'member', 'members'}: _, members = _getMain().getEntityToModify(defaultEntityType=Cmd.ENTITY_USERS) for user in members: getUserMemberID(cd, user, userList) usersSpecified = True elif myarg == 'groups': for group in _getMain().getEntityList(Cmd.OB_GROUP_ENTITY): getGroupMemberID(cd, group, groupList) groupsSpecified = True else: _getMain().unknownArgumentExit() if not parent: _getMain().missingArgumentExit('space') userEntityType = CHAT_ROLE_ENTITY_TYPE_MAP[role] userMembers = {} syncUsersSet = set() for user in userList: memberName = f'{parent}/members/{user}' userMembers[memberName] = {'member': {'name': f'users/{user}', 'type': mtype}} syncUsersSet.add(memberName) groupMembers = {} syncGroupsSet = set() for group in groupList: memberName = f'{parent}/members/group-{group}' groupMembers[memberName] = {'groupMember': {'name': f'groups/{group}'}} syncGroupsSet.add(memberName) qfilter = f'{Ent.Singular(Ent.CHAT_SPACE)}: {parent}' i, count, users = _getMain().getEntityArgument(users) if useAdminAccess: kwargs['filter'] = 'member.type != "BOT"' _chkChatAdminAccess(count) for user in users: i += 1 user, chat, kvList = buildChatServiceObject(api, user, i, count, [Ent.CHAT_SPACE, parent], useAdminAccess) if not chat: continue currentUsersSet = set() currentGroupsSet = set() try: members = _getMain().callGAPIpages(chat.spaces().members(), 'list', 'memberships', pageMessage=_getChatPageMessage(Ent.CHAT_MEMBER, user, i, count, qfilter), throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED, GAPI.FAILED_PRECONDITION], retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS, parent=parent, showGroups=groupsSpecified, pageSize=GC.Values[GC.CHAT_MAX_RESULTS], **kwargs, **kwargsUAA) for member in members: if 'member' in member: if member['member']['type'] == mtype and member['role'] == role: currentUsersSet.add(member['name']) elif 'groupMember' in member: currentGroupsSet.add(member['name']) 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 syncOperation != 'addonly': Act.Set([Act.REMOVE, Act.REMOVE_PREVIEW][preview]) if usersSpecified: deleteMembers(currentUsersSet-syncUsersSet, userEntityType, i, count) if groupsSpecified: deleteMembers(currentGroupsSet-syncGroupsSet, Ent.CHAT_MEMBER_GROUP, i, count) if syncOperation != 'removeonly': Act.Set([Act.ADD, Act.ADD_PREVIEW][preview]) if usersSpecified: addMembers(syncUsersSet-currentUsersSet, userMembers, userEntityType, i, count) if groupsSpecified: addMembers(syncGroupsSet-currentGroupsSet, groupMembers, Ent.CHAT_MEMBER_GROUP, i, count) if csvPF: csvPF.writeCSVfile('Chat Member Updates') CHAT_MEMBERS_FIELDS_CHOICE_MAP = { "createtime": "createTime", "deletetime": "deleteTime", "groupmember": "groupMember", "member": "member", "name": "name", "role": "role", "state": "state", } # gam [] info chatmember members # [fields ] # [formatjson] # gam info chatmember asadmin members # [fields ] # [formatjson] def infoChatMember(users): cd = _getMain().buildGAPIObject(API.DIRECTORY) FJQC = _getMain().FormatJSONQuoteChar() useAdminAccess, api, kwargsUAA = _getChatAdminAccess(API.CHAT_MEMBERSHIPS_ADMIN, API.CHAT_MEMBERSHIPS) fieldsList = [] memberNames = [] while Cmd.ArgumentsRemaining(): myarg = _getMain().getArgument() if myarg in {'member', 'members'}: memberNames.extend(_getMain().getString(Cmd.OB_CHAT_MEMBER).replace(',', ' ').split()) elif _getMain().getFieldsList(myarg, CHAT_MEMBERS_FIELDS_CHOICE_MAP, fieldsList, initialField='name', onlyFieldsArg=True): pass else: FJQC.GetFormatJSON(myarg) if not memberNames: _getMain().missingArgumentExit('members') 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, None, useAdminAccess) if not chat: continue jcount = len(memberNames) if not FJQC.formatJSON: _getMain().entityPerformActionNumItems(kvList, jcount, Ent.CHAT_MEMBER, i, count) kvList.extend([Ent.CHAT_MEMBER, '']) j = 0 for name in memberNames: j += 1 kvList[-1] = name try: member = _getMain().callGAPI(chat.spaces().members(), 'get', bailOnInternalError=True, throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.INTERNAL_ERROR, GAPI.PERMISSION_DENIED, GAPI.FAILED_PRECONDITION], name=name, fields=fields, **kwargsUAA) _getChatMemberEmail(cd, member) Ind.Increment() _showChatItem(member, Ent.CHAT_MEMBER, FJQC, j, jcount) Ind.Decrement() except (GAPI.notFound, GAPI.invalidArgument, GAPI.permissionDenied, GAPI.internalError) as e: exitIfChatNotConfigured(chat, kvList, str(e), i, count) except GAPI.failedPrecondition: _getMain().userChatServiceNotEnabledWarning(user, i, count) continue def doInfoChatMember(): infoChatMember([None]) # gam [] show chatmembers # * [types ] # [showinvited []] [showgroups []] [filter ] # [fields ] # [formatjson] # gam [] print chatmembers [todrive *] # * [types ] # [showinvited []] [showgroups []] [filter ] # [fields ] # (addcsvdata )* # [formatjson [quotechar ]] # gam show chatmembers asadmin # * [query ] [querytime